how we can design .net core api controller to accept an array value of strings - api

I would like to know how we can design .net core api controller to accept an array value like given below
http://localhost:32656/api/Values?str[]="abc"&str[]="xyz"
I did some research online and the only two options, I was able to find is either I need to pass indexes inside the array
eg:- http://localhost:32656/api/Values?str[0]="abc"&str[1]="xyz" (Pass indexes inside the array)
or I need to pass the array as repeated query strings.
eg:- http://localhost:32656/api/Values?str="abc"&str="xyz" (Pass it as repeated query strings)
But I would like to see the other possible options to send an array to .net core 2.1 api controller.

You can take advantage of the FromQuery attribute here, specifying the Name property as str[]:
public IActionResult Values([FromQuery(Name = "str[]")] List<string> strValues)
{
...
}
If you also want to strip out the "s for each value, you can use a Select. Here's a an example:
public IActionResult Values([FromQuery(Name = "str[]")] List<string> strValues)
{
var strValuesWithoutQuotes = strValues.Select(x => x.Trim('"'));
...
}

Here is how we do it:
[Route("api/v1/[controller]")]
public class TestController : Controller
{
[HttpGet]
public async Task Get(List<string> stringValues)
{
...
}
}
Then call the endpoint with http://localhost/api/v1/test?stringValues=string1&stringValues=string2
stringValues should have the list of values in the query string

Related

asp.net core api - how to distinguish between `api/cars` and `api/cars?color=red` calls with [FromQuery] object

I am using [FromQuery] atribute in controller's Get method:
//CarsController, etc..
[HttpGet]
public async Task<ActionResult<IEnumerable<CarsDto>>> Get([FromQuery] CarsParameter? carsParam = null)
{
//param is always not null here
}
Inside the method I need to distinguish between api/cars and api/cars?color=red calls. Problem is, that carsParam object is never null, so I cannot say if the Color="" (defailt value) is intended to be empty string or it's because of the call was api/cars
CarsParameter is a simple class:
public class CarsParameter
{
public string Color {get; set;} = "";
//more params here
}
Yes, I can use different path, like api/cars/withParams?color=red, but i am looking for more subtle solution.
I need to distinguish between api/cars and api/cars?color=red calls. Problem is, that carsParam object is never null
Please note that default model binding starts by looking through the sources for the key carsParam.Color. If that isn't found, it looks for Color without a prefix, which cause the issue.
To achieve your requirement, you can try to specify prefix explicitly, like below.
public async Task<ActionResult<IEnumerable<CarsDto>>> Get([FromQuery][Bind(Prefix = "carsParam")] CarsParameter? carsParam = null)
{
Request to api/cars?color=red&carsParam.color=yellow&carsParam.brand=test and following is test result

Passing enum value as parameter .net core

I have a controller (ResponseResourceController) which Index Action is supposed to receive as parameter int and in the Index action - Parse enum value from Tempdata. Then this action redirects to another action with this two parameters.
public class ResponseResourceController : Controller
{
public async Task<IActionResult> Index(
int id)
{
var entityType = (EntityType)TempData["EntityType"];
var exists = await _responseResourceStringUiService.ResponseResourceStringExistsAsync(
id,
entityType);
return RedirectToRoute(!exists ?
RouteNames.ResponseResourceString_Home_Add :
RouteNames.ResponseResourceString_Home_Edit,
new {id, entityType});
}....
}
public class PublicationController : Controller
{
public async Task<IActionResult> Index()
{
var vm = new PublicationViewModel
{
...
};
TempData["EntityType"] = InboxEntityType.Publication;
........
}
My question is - is there more elegant way to achieve this without the enum value beeing in the url after the redirection.
I was thinking for example to create an abstract property of thids Enum Type in this ResponseResourceController
AND the other controller PublicationController(and othe that are going to do that) to inherit it and override the property with the right enum value and as the ResponseResourceController will have property I will not need the entity type as parameter in the Add and Edit Action.
Why don't just directly call the PublicationController and avoid creating an unnecessary redirect? You can distinguish directly in the frontend if it is a new or an existing record.
If you wanna use this method for creating new records then you should prefer to use a POST request instead of GET.
In short, multiple controllers does not work and url redirection is not necessary.
From the description I think what you really want is displaying the add/edit form base on an ID (with your temp entity type stored in somewhere). If this is what you want then single controller with multiple views should be able to handle that.
What will happen when you create multiple controllers?
The routing rules in the app has to be unique. If you have a ResponseResourceController and a PublicationController and each of them has the Index action, what will be the routes look like?
You probably need to define a route like /ResponseResource/Index for ResponseResourceController.Index() and a route like /Publication/Index for PublicationController.Index(). If there is a EntityType called Bob, you may need to define a route like /Bob/Index and create a BobController.
However, if you try to name a unique route for each of the EntityType, your route has to contain that piece of information (either in the route or in the query string). So, creating a dedicated controller for each EntityType cannot solve your problem.
Multiple views for multiple entity types
If you don't want to include the EntityType in the url, you may use ResponseResourceController.Index(int id) to get the entity ID and respond with different views for different EntityType:
[Route("/response-resource")]
public class ResponseResourceController : Controller
{
[HttpGet("")]
public async Task<IActionResult> Index(int id)
{
var entityType = (EntityType)TempData["EntityType"];
var exists = await _responseResourceStringUiService.ResponseResourceStringExistsAsync(id, entityType);
var viewName = exists ? "_AddEntity" : "_EditEntity";
var vm = new GenericViewModel
{
type = entityType,
/* other fields */
}
return View(viewName, vm); // so user may see a different view under different situation
}
}
And for the view model:
public class GenericViewModel
{
public EntityType type { get; set; }
}
You also need to define 2 views: _AddEntity and _EditEntity. In the above example, GenericViewModel contains the EntityType so you will know which entity you are handling.
Furthermore, you can even define a dedicated view for each EntityType and pass a dedicated view model to that view. e.g. PublicationViewModel will be the view model used in _AddPublication and _EditPublication views.
Expected behavior
When user requests for ResponseResource with and id, e.g. /response-resource?id=123, your application will render the view base on the id and the EntityType. No url redirection and url won't change.

Injecting IOptions<> into ApiKeyAuthorizeAttribute

I am using options pattern that stores different configurations, including API keys for different environments. So far I have been using it fine and injecting my values into classes as needed.
However, I faced a little challenge while trying to setup authorization in the controller and run validation against my ApiKey that is unique per environment, because I was not able to inject IOptions into ApiKeyAuthorizeAttribute class to perform validation.
Here is how my controller looks like now:
[ApiKeyAuthorize]
public class NotificationSettingsController : Controller
{
//some endpoints here
}
ApiKeyAuthorize Class:
public class ApiKeyAuthorizeAttribute : Attribute, IAuthorizationFilter
{
//////This...
private readonly IOptions<MyConfig> _config;
public ApiKeyAuthorizeAttribute(IOptions<MyConfig> config)
{
_config = config;
}
/////////...is what I am trying to accomplish
public void OnAuthorization(AuthorizationFilterContext context)
{
var request = context.HttpContext.Request;
var foundApiKeys = request.Headers.TryGetValue("ReplaceWithOptionsApiKeyName", out var requestApiKeys);
if (!foundApiKeys || requestApiKeys[0] != "ReplaceWithOptionsApiKeyValue")
{
context.Result = new UnauthorizedResult();
}
}
}
My problem is that injecting here isn't possible, but I need to get a value from IOptions<> to run ApiKey validation.
Attributes are constructed in-place, so it's not possible to inject dependencies into them. However, ASP.NET Core provides a workaround. Instead of applying the attribute directly, you can use the ServiceFilter attribute instead and pass it the type of the filter you want to apply:
[ServiceFilter(typeof(ApiAuthorizeAttribute))]
This will dynamically apply the filter to the controller/action while instantiating it with any dependencies it requires at the same time. However, it does limit you in the other direction. For example, if you need to do something like:
[ApiAuthorizeAttribute(Roles = "Admin")]
It would not be possible to achieve this with the ServiceFilter attribute, because you cannot pass property values, like Roles here, along with the type.

String Trim Model Binder in ASP .NET Core 2

I'm working on a .NET Core 2 API project and have been trying to implement a universal string trim model binder that would trim all string values of provided request parameters and field values. So far I have had mixed results and am struggling to find working example that would point me in the right direction. I've been trying to implement the same model binder as posted by Vikash Kumar.
This model binder works fine for all string values that are passed into controller actions via direct parameters, such as public IActionResult Profile(string username), but for string fields in complex objects the BindModelAsync method of the TrimmingModelBinder class never gets called. An example of an HttpPost action in my controller would be public IActionResult Profile([FormBody] ProfileLookupModel model). The model binder does not seem to check the fields of the complex model. It also doesn't work for fields that are Lists of strings.
I recall prior to .NET Core, specifying a string trim model binder would recursively check each field of complex models, even models within complex models. This doesn't seem to be the case in .NET Core, but I might be wrong. My project is targeting the netcoreapp2.0 framework.
I'm curious if anyone has had the same issue as me and possibly found a solution for it.
Note: I haven't posted any sample code as it is the same as the code from the referenced article.
I'll add my 2 cents here. Instead of using some kind of model binding hook, I went to a action filter. One of the advantages is that the developer can select which actions to use, instead of having this processing for all requests and model bindings (not that it should affect performance that much). Btw action filters can also be applied globally.
Here is my code, first create an action filter.
public class TrimInputStringsAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
foreach (var arg in context.ActionArguments.ToList())
{
if (arg.Value is string)
{
if (arg.Value == null)
{
continue;
}
string val = arg.Value as string;
if (!string.IsNullOrEmpty(val))
{
context.ActionArguments[arg.Key] = val.Trim();
}
continue;
}
Type argType = arg.Value.GetType();
if (!argType.IsClass)
{
continue;
}
TrimAllStringsInObject(arg.Value, argType);
}
}
private void TrimAllStringsInObject(object arg, Type argType)
{
var stringProperties = argType.GetProperties()
.Where(p => p.PropertyType == typeof(string));
foreach (var stringProperty in stringProperties)
{
string currentValue = stringProperty.GetValue(arg, null) as string;
if (!string.IsNullOrEmpty(currentValue))
{
stringProperty.SetValue(arg, currentValue.Trim(), null);
}
}
}
}
To use it, either register as global filter or decorate your actions with the TrimInputStrings attribute.
[TrimInputStrings]
public IActionResult Register(RegisterViewModel registerModel)
{
// Some business logic...
return Ok();
}
TrimmingModelBinder is essentially configured for strings only, and defaults back to SimpleTypeModelBinder if it fails, or other binders configured. So if your implementation is essentially the same as in TrimmingModelBinder then it will definitely work for strings only.
For complex types, I recommend creating a new binder, and its corresponding provider, which will have to check all string properties in the model type and trim the value before binding. Then register this binder at index 0 such that its the first one checked before any other binders are tried.
services.AddMvc(options => option.ModelBinderProviders.Insert(0, new MyComplexTypeModelBinderProvider());

Provide common data to all ASP.NET MVC 4 controller method

I am rewriting an ASP.NET webforms app in MVC4 and was wondering how to solve the following problem. It is a multi-tenant app, so part of the URL has the tenant NAME in it:
http://mysite/tenant/controller/action
But tenant is an abbreviation representing the tenant, but I'd like to always convert that to the corresponding integer id and use that throughout the code. What is the best way to write that convert code once and have some variable/property available to all controller methods.
public class DivisionController : Controller
{
//
// GET: /Division/
public ActionResult Index()
{
// I want this.TenantId to be available in all controller methods
FetchDivisions(this.TenantId);
return View();
}
Is a base controller the best way to handle this or filters or attributes?
Yes a base controller will handle this just fine. If you need to perform a database lookup to convert the abbreviation to the integer value you can use the OnActionExecuting event like so:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
// Lookup code here.
}