I have the following controller method which accepts command as a change model passed from the client.
[HttpPut]
[Route("api/features/{name}")]
public async Task<IActionResult> PutFeatureValueAsync(
string name,
[FromBody] SetFeatureCommand command,
CancellationToken token)
{
command.FeatureName = name;
await mediator.Send(command, token).ConfigureAwait(false);
return Ok();
}
The problem is that I have to copy a FeatureName property to the command object manually if I want to keep the {name} in the url. The other problem is that I can't properly do a validation if the FeatureName is not set during model binding.
What would be the best way to have {name} segment of the path to automatically model bind to FeatureName property of the command.
This could be achieved by using custom model binding.
I would recommend starting from reading Model Binding documentation and then look into Custom Model Binding topic. It explains both available approaches and samples shows almost what you need:
the custom model binder (via own implementation of IModelBinder)
This approach requires your model modification: you will need to apply [ModelBinder] attribute to your SetFeatureCommand class
the custom model binder provider (via own implementation of IModelBinderProvider). This is how the built-in framework binders are implemented. Provider then is registered using MVC middleware options:
services.AddMvc(options =>
{
// add custom binder to beginning of collection
options.ModelBinderProviders.Insert(0, new OwnBinderProvider());
});
Related
I want to validate model using below code
if (ModelState.IsValid)
{
}
The issue is that when I passed model object to controller action its working properly but when I called controller action using below code
public ActionResult Save()
{
}
Then my model state is always return true.So please clarify me is it possible to check ModelState.IsValid using above code.In my case save method is common which is call from multiple controller,so model is not fixed parameter.So please let me know how validate model.
MVC uses a model to extract the validation attributes, as a means for validation. You can explicitly call ValidateModel() or TryValidateModel() in the controller directly, but again that is validating a model, using model validation attributes. If you aren't explicitly using a model, you can access the form directly as you probably know, or can manually validate input and then add appropriate model validation errors using:
ModelState.AddModelError("PropertyName", "ErrorMessage");
Or use this technique depending on how your app is structured: How to add ModelState.AddModelError message when model item is not binded
ModelState.Clear();
Validate(model);
if (ModelState.IsValid)
{
// Do something
}
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());
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.
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
}
}
I want to bind an interface model from my action method with a request that the content-type is application/json. I'm using the [FromBody] attribute in my action method.
I tried to create a custom modelBinder derived from ComplexTypeModelBinder, by following this link: Custom Model Binding in Asp.net Core, 3: Model Binding Interfaces, but it doesn't work, my model is always null. I learned after that when you use the atribute [FromBody] the BodyModelBinder is called and internally is calling JsonInputFormatter and it doesn't use the custom modelBinder.
I'm looking for a way to bind my interface model. I can use the MVC DI to map each interface with its implementation. My action method is defined as :
public async Task<IActionResult> Create(IOperator user)
{
if (user == null)
{
return this.BadRequest("The user can't not be null");
}
if (!this.ModelState.IsValid)
{
return this.BadRequest(this.ModelState);
}
IOperator op = await this.AuthenticationFrontService.CreateOperatorAsync(user.Login, user.Password, user.FirstName, user.LastName, user.ValidUntil, user.Role, user.Comment);
return new CreatedAtActionResult("Get", "operators", new { id = ((Operator)op).Id }, op);
}
I tried another solution by using the MetadataType attribute in my interface but it doesn't exist in the namespace System.ComponentModel.DataAnnotations and i read that asp.net core mvc doesn't use this attribute Asp.Net MVC MetaDataType Attribute not working. I don't want to install the package microsoft.aspnetcore.mvc.dataannotations in domain model project to use the ModelDataType attribute.
I tried another solution by creating a custom JsonInputFormater in other words i derived the class JsonInputFormatter and by analyzing the source code, i've found that the JsonSerializer couldn't deserialize an interface which is logical. So i'm looking for a solution where i could custom the jsonserializer maybe by using a resolver or a generic converter.
Any help will greatly appreciated.
Thanks.
Using an interface is fine for a C# method, but MVC needs to know what concrete type it should instantiate when calling an Action since it's creating it. It doesn't know what type to use, so it can't bind input from Form/QueryString/etc to. Create a very basic model for use in your action that does nothing but implement your interface IOperator, if your goal was to keep it slim, and set that to your Action parameter and it should work fine.
I have tried using an interface on a action as well, and through my own searching, I found no way to get it to work, other than just using classes instead of interfaces to bind to.
public class Operator : IOperator
{
//Implement interface
}
.
public async Task<IActionResult> Create(Operator user)
{
if (user == null)
{
return this.BadRequest("The user can't not be null");
}
if (!this.ModelState.IsValid)
{
return this.BadRequest(this.ModelState);
}
IOperator op = await this.AuthenticationFrontService.CreateOperatorAsync(user.Login, user.Password, user.FirstName, user.LastName, user.ValidUntil, user.Role, user.Comment);
return new CreatedAtActionResult("Get", "operators", new { id = ((Operator)op).Id }, op);
}