MediaFormatter or ModelBinder for web api PUT method - asp.net

I have a PUT method in web api which accepts a JSON data and a route data as follows.
[Route("api/v1/Orders/{orderId}/active")]
public HttpResponseMessage Put(Guid? orderId,List<ActiveRequest> activeRequests)
{
}
public class ActiveRequest
{
public int Id { get; set; }
public bool IsActive { get; set; }
}
Now is it possible to simplify the method signature as:
[Route("api/v1/Orders/{orderId}/active")]
public HttpResponseMessage Put(ActiveRequestModel model)
{
}
public class ActiveRequestModel
{
public Guid OrderId { get; set; }
public List<ActiveRequest> ActiveRequests {get; set;}
}
I tried writing a custom ModelBinder by implementing the System.Web.Http.ModelBinding.IModelBinder interface but could'nt find a way to read the JSON data that is coming inside the Request object.
I doubt that is there a way by which I can bind my model with data coming from three different places i.e. from route data, json & form.

You cannot simplify the parameter as described.
Unlike MVC model binding, beacuse of how the Web API formatter works, in Web API you only can have a single parameter that is deserialized from the payload, and a number of simple type parameters coming from route parameters or url query string. The reason is that the creation of the parameter coming from the payload is done in a single pass deserialization of the payload.
So, for your example you need the two parameters in your original version, i.e.:
public HttpResponseMessage Put(Guid? orderId, List<ActiveRequest> activeRequests)
If you want to use the ActiveRequestModel you need to include a payload which has exactly the same structure, so you should include the orderId in the payload, because it will not be recovered from the url (even if the name matches).
Please, read this article which explains how parameter binding works in Web API:
Parameter Binding in ASP.NET Web API
If you read it thoroughly you'll see that you can create and register your own model binder to make it work the same way that an MVC controller, but I think it's not worth the effort (so I include it only in this last paragraph), and it's not the standard way of working.

Related

ASP.Net MVC Controller accepting "application/x-www-form-urlencoded" content type

I inherited an old ASP.Net MVC web application. I need to modify it and add a page that can handle an incoming HTTP POST with hidden fields sent as "application/x-www-form-urlencoded" content type. This page's URL will be provided as a webhook URL to an external system that will use it to send back the control to my application and will provide some data in the form of "application/x-www-form-urlencoded" content type.
As I mentioned, this is an old MVC 5 application and is not targeting the .NET Core framework. Therefore I cannot declare my controller's method like this:
[HttpPost]
[Consumes("application/x-www-form-urlencoded")]
public void Webhook([FromForm] Response webHookResponse)]
The "Consumes" and "FromForm" attributes are available in the "Microsoft.AspNetCore.Mvc" namespace, but I don't have access to that in my application. Is there a different way to handle this in MVC 5?
Thanks,
Ed
You shouldn't have to do anything. The DefaultModelBinder will bind your form values to the parameter object without specifying Consumes or FromForm attributes. The same is also true for .NET Core.
Edit - code for clarity:
Also an important note: this (automatic form binding) will only work if you do NOT have the [ApiController] attribute tagging your controller.
Assume you have three hidden fields coming in, one is named foo, the other is named fudge
Either one of these will work:
[HttpPost]
public void Webhook(string foo, string fudge)
{
}
or:
public class WbResponse
{
public string foo {get; set;}
public string fudge {get; set;}
}
[HttpPost]
public void Webhook(WbResponse response)
{
}
Turns out that the request object contains all the hidden fields in its "Form" member which is a NameValueCollection. I declared my method like this:
// POST: /{Controller}/Webhook
[HttpPost]
public virtual ActionResult Webhook()
{
var requestFormFields = HttpContext.Request.Form;
and then I can access each field in the NameValueCollection by its name:
var hiddenFieldValue = requestFormFields["hiddenFieldName"];

How to bind multiple file uploads with model in web api?

I can't see what's missing here.
I have a model that looks like this:
public class ModelDto
{
public string X { get; set; }
// ...
public IList<IFormFile> Attachments { get; set; }
}
It is used for a POST endpoint in Web Api, like this:
[HttpPost()]
public async Task<ActionResult<ResponseModel>> PostEndpoint([FromForm] ModelDto modelDto)
{
// ...
}
When I POST with Postman, with Form-Data, and fields, including Attachment[0] as a file, I receive a model, complete with all other fields, but without any file. But I do receive the file in the request. If I look at this.Request.Form.Files, it's there, but it was not loaded as part of the model.
So, obviously, I can manually re-attach the list of files to the appropriate part of the model. But why doesn't the binding work?
Thanks
Well, I finally got it, so the problem was on the Postman side of how I wrote the request.
For arrays of text keys, the [] notation is used. It must not be used for arrays of files.
So instead of sending Attachments[0] or Attachments[], I just had to send Attachments as the key to each file. Then the binding works fine.

Is there a way to make an ASP.NET POST method work without a model as a param?

Here's my method now:
public class UserTestAdminTestId
{
public int AdminTestId { get; set; }
}
[HttpPost]
[Route("Post")]
public async Task<IHttpActionResult> Post([FromBody]UserTestAdminTestId userTestAdminTestId)
There's more code inside of the Post Method and the only data I need for it is the AdminTestId.
I made a model (class) to accept this but assuming that I send the AdminTestId as a JSON object, is there a way for me to tell the post method what to expect without creating a class for just one object?
Yes, but it will have to be a nullable int.
Keep in mind though that your body is only bound to one variable so if you have multiple values, you'll have to group them in a single type.

Proper way to send json object to HttpGet endpoint in WebAPI 2

I am developing web api as an facade which will encapsulated request to underlying systems.
So, lets assume I have cars endpoint:
api/v1/cars
Now I want my api to get parameters which will determine calls to underlying systems.
Like:
{
provider: 'service_1'.
access_token: 'token_2',
info: 'some_info'
},
{
provider: 'service_2'.
access_token: 'token_2',
info: 'some_info'
}
Besides that api will take standard parameters like startdate, enddate, offset and others.
public async Task<Result<Cars>> Get([FromUri] RequestParams requestParams);
public class RequestParams
{
public RequestParams()
{
Limit = 50;
Offset = 0;
StartDate = DateTime.Now;
EndDate = DateTime.Now;
}
public string UserId { get; set; }
public int Limit { get; set; }
public int Offset { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
It's easy to map standard params from uri, but I do know how to properly pass json collection. Any ideas?
By definition, a GET request doesn't have payload (i.e. data in the body). So, the only way to pass data to a GET request is in the url, i.e. using route data or query string parameters.
If you need to pass a JSON object you need to use a different request method, usualy POST. This includes passing collections.
If you use POST, Web API will automatically load the parameter object with the JSON posted object. If the paramerter is a collection (for example a List<T>) it will also be correctly populated.
There is only one thing that you must take into account: the method can only have one parameter loaded from the body. I.e. you cannot receive several parameters in a Web API action from the body. (You can, however, have data coming from the URL and data coming from the body).
So, you need to change your request and your method to use POST or any other method with payload. Besides, you must choose one of thesse two options:
create a class that includes all the standard parameters, and the collection, and use it as parameter, and post al lthe data in a single object, with the same structure.
pass the standard parameters in the query string, or using route data, and the collection as JSON. In this case, yourmethod must have several parameters: onw for the collection posted as JSON, and one for each other parameters postes in the query string or route data
Posting a collection in the querystring, as proposed in kapsi's answer, is not possible, unless you make some kind of serialization of the parameter on the client side and deserialization when receiving it on the server side. That's overkill, just use POST or any other method with body, as explained above.
If you for example use jQuery, you can use the ajax method to address this:
$.ajax({
url: "./",
type: "GET",
data: {
UserId: 1,
Limit: 2,
Offset: 2,
StartDate: "02/15/2015",
EndDate: "05/15/2015"
}
});
jQuery takes action and following GET is made:
?UserId=1&Limit=2&Offset=2&StartDate=02%2F15%2F2015&EndDate=05%2F15%2F2015&_=1423137376902

ViewModel type architecture from Controller to View

I have a fairly complex class of Policies, of which I display a checkbox list of them, the user checks which one they want, and returns back to the server via ajax. The class is fairly complex:
public class Policy {
public int PolicyId { get; set; }
public string PolicyName { get; set; }
... another 15 properties ...
}
To display the list of checkboxes I really only need the Id and Name, so I've created a lightweight class PolicyViewModel that is simply:
public class PolicyViewModel {
public int PolicyId { get; set; }
public string PolicyName { get; set; }
}
So I then pass a List to the View and get a List back containing the selected Policies.
Another developer on my team said that he doesn't necessarily want to translate from the ViewModel to the Policy class on the Ajax call to save the selected policies, but I'm resistant to send a List of policies due to how heavy they are to send to the view, retrieving all the properties, etc.
EDIT: For clarification, on the Ajax save method, to persist to the DB, the call needs a list of the full Policy class.
What is the best way to display this list and get back the values? Is there a better way than I am proposing?
Usually, you wouldn't need a separate model when serializing to json. Simply pluck out what you need from the domain object into an anonymous object.
return policies.Select(x => new { PolicyId = x.PolicyId, Name = x.PolicyName});
on the return trip, you shouldn't have to send anything more than the Ids of the policies that the user selected. Those can be easily mapped back to your policy objects.
public Whatever PostPolicyChoices(IEnumerable<int> ids)
{
var checked = _context.Policies.Where(x => returnIds.Contains(x.PolicyId));
// snip
boom. done.
I will recommend you not to work with Domain objects in your mvc application . You must work just with ViewModels, I think this is best practice for mvc projects. Take a look at Automapper and use it in your project, this will simplify your work, so this should look something like this :
in your [HttpGet] method you will have :
var model =Mapper.Map<IList<Policy>,IList<VmSysPolicy>>(yourlist)
And in your [HttpPost] method you will have :
var domainList=Mapper.Map<IList<VmSysPolicy>,IList<Policy>>(modelList);
And in your mapping configuration you will do :
Mapper.CreateMap<Policy,PolicyVmSysPolicy,>()
.ForMemeber()//Your mapping here
and
Mapper.CreateMap<VmSysPolicy,Policy>()
.ForMemeber//mapping here

Resources