Is it possible to modify Web Api Parameter (FromUri) inside Action Filter? - asp.net-web-api2

My Web API receives a param object consisting of (DateTime) dateFrom, (DateTime) dateTo.
Currently I preprocess the parameter inside my API entry function, which, is repetitive throughout the API.
I wish I could access and modify the parameter before it enters the API entry function.
So I have the action filter below:
public class MyActionParamFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
// pre-processing
object param;
object param2;
actionContext.Request.Properties.TryGetValue("dateFrom", out param);
actionContext.ActionArguments.TryGetValue("dateFrom", out param2);
}
}
From the image, it is clearly that i have passed the parameters to the API. But I have no idea why param and param2 are null...
Is it possible to achieve that?

Finally I follow the answer here:
Change webapi controller action parameter in delegatinghandler
Technically, it is to modify actionContext.ActionArguments.
But I am not too sure if this is 'Best practice' or something.. Let me know your thought if you think i am in the wrong direction of design.

Related

Using ApiExplorerSettings GroupName in Route in ASPNET Core

In ASP.NET Core - Web API project
Using [ApiExplorerSettings(GroupName = "<group-name>")] decorated on ApiController
and in [Route] attribute I want to refer above GroupName property value.
Also note I do have [ApiVersion("<some-version>")] on same controller to classify further.
Here are some samples to explain:
Example 1:
Attribute on LeadController: [ApiVersion("1.0"), ApiExplorerSettings(GroupName = "sales"), [Route("api/{groupName}/v{version:apiVersion}/leads"]
Expected translated route format: /api/sales/v1/leads
Attribute on AccountsController: [ApiVersion("2.1"), ApiExplorerSettings(GroupName = "finance"), [Route("api/{groupName}/v{version:apiVersion}/accounts"]
Expected translated route format: /api/finance/v2.1/leads
In above {version:apiVersion} gives me ApiVersion value (I assume because that attribute has ToString set to version value). But when I try {groupName} or {grp:groupName} or {grp:ApiExplorerSettings.GroupName} - none of them works. How can access this group name in route attribute?
Do you have any special settings somewhere else, it works fine on my side.
LeadController:
[ApiVersion("1.0"), ApiExplorerSettings(GroupName = "sales"),Route("api/{groupName}/v{version:apiVersion}/leads")]
[ApiController]
public class LeadController : ControllerBase
{
public string Get()
{
return "sales group";
}
}
AccountsController:
[ApiVersion("2.1"), ApiExplorerSettings(GroupName = "finance"), Route("api/{groupName}/v{version:apiVersion}/accounts")]
[ApiController]
public class AccountsController : ControllerBase
{
public string Get()
{
return "finance group";
}
}
Result:
ApiExplorerSettings.GroupName is only for the logical group name used in API descriptions. It is not used or evaluated in route templates. There is no way to make that work. That is why it never expands. It is possible to use a {groupName} route parameter, but it will be just that - a route parameter.
It's not entirely clear if you are trying to control grouping for the API Explorer or use a route parameter. Grouping is typically used by the API Explorer. API Versioning will assign or override the group name so that APIs are bucketized by their version. It is possible to change this behavior, but you'll need an API Explorer (via IApiDescriptionProvider) or OpenAPI/Swagger document generator extension to do it.

Which filter to manually deserialize incoming JSON to a specified type

I'm developing an application with ASP.NET Core WebAPI. What I would like to do is create a filter that I can apply to my controller method that will then let the system know to explicitly convert the incoming JSON object into a specified type.
For example, this is what I envision being able to do:
[HttpPost()]
[MyFilter(typeof(MyType))]
public IActionResult Post(MyType model)
{
....
}
The reason I want to do this is because the incoming JSON object does not match (at all) the structure of "MyType". So I have written a special converter whose logic I can call from this Filter. I want to explicitly state the type and not try to infer it from values within the JSON object (which is, in my case, impossible).
What I would like to do is create a filter that I can apply to my
controller method that will then let the system know to explicitly
convert the incoming JSON object into a specified type.
Filter is not quite right tool for performing custom model binding you described. You should not reinvent the wheels here: custom model binding should be performed by means of (sorry for this pun) custom model binder.
If the only argument against model binder is:
I cannot use CustomModelBinder as that is injected into the pipeline.
then ASP.NET Core provides an elegant way how to apply model binder to specific action arguments, without adding it to the whole pipeline. This could be done via ModelBinder attribute applied to action paramter, which specifies the binder in BinderType property.
Here is a sample:
[HttpPost]
public IActionResult Post([ModelBinder(BinderType = typeof(MyTypeBinder))] MyType model)
{
return Ok();
}
public class MyTypeBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
// Put your custom MyType binding code here
}
}

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.
}

MVC 4 is overwriting specific Action-Parameters

MVC 4 does present me some strange behaviour at the moment.
Imagine the following Code:
TestController.cs
public class TestController : Controller
{
public ActionResult Index(Function function, string action)
{
return View();
}
public class Function
{
public string Action { get; set; }
}
}
It seems, that when I call the URL directly through the browser (localhost:PORT/Test), the Action-Property gets automatically filled with "Index".
If the Action would be named "MySuperDuperActionWhichGetsInvokedQuiteOften", exactly this Methodname would be in the property.
Can somebody explain what MVC is doing here?
The Problem is, that I of course want to fill that stuff myself, for example through an AJAX-Query. But if MVC is filling in this property all by itself, this breaks some behaviour.
I could, of course, just rename my property and it would be working, but it would still be quite interesting what's going on.
EDIT
I would understand it that my second parameter, string action, get's filled with the method-name. But why on earth would MVC bind any property/parameter that is named the same to the request-value of it?
It is problem with default model binder. It "maps" request fields to properties in your class. There is an article of MSDN describing how does it works but to simply this situation the code will be like this:
Action = Request["action"] //where of course Request["action"] equals to name of your action

Passing parameter through all MVC4 pages

In my application there's the need of passing out a specific parameter, when it exists, through all pages.
What is the best way of I can do this?
There's some function that is called whenever I do an GET or POST requisition, in which I can verify if the parameter exist and persist it?
Create an action filter like the one below.
public sealed class ScaffoldActionFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var model = filterContext.Controller.ViewData.Model as PageModelBase;
// Whatever you want to do.
model.myParam = "Im available in all views";
}
}
Next register it to run on every request. Look in app_start/filterconfig.cs
and and it like this.
filters.Add(new ScaffoldActionFilter());
Now just return the standard pagebasemodel or a derived type from every view and you have what you want.