Send 2 or more Raw Body Parameters Post Request with Postman - asp.net-web-api

I have an ASP.NET Core 2.0 API Method:
[HttpPost("api/days")]
GetDays([FromBody] DateTime startTime, [FromBody]DateTime endTime)
{
}
And I tried to send a Post request with Postman, but there is a problem, the parameters always have default values.
Here is my Post request looks like:
Result: Not Worked; Both parameters in API method get the default values.
If I change my API params to:
[HttpPost("api/days")]
GetDays([FromBody] Inputparam param)
{
}
public class Inputparam
{
public DateTime startTime { get; set; }
public DateTime endTime { get; set; }
}
That's worked perfectly!
But I wanna to send parameters directly and not inside wrapper object.
So, I came back with first API method and then I tried:
Result: Not Worked; Both parameters in API method get the default values.
And This one:
Result: Not Worked perfectly; Just first parameter (startTime) set it and second parameter still have default value.
And This one:
Result: Not Worked; Both parameters in API method get the default values.
I also tried [FromForm] instead of [FromBody] in API, nothings changed.
If I don't use [FromBody] in api and send the request via x-www-form-urlencoded that's worked perfectly.
But I need send a raw body with JSon.
How could I sent 2 different parameters as a raw body json?
Any idea?
Where is the problem?

I found that it's just one [FromBody] is allowed in API, and that makes sense. So probably the answer is: There is no way to have 2 or more parameters with [FromBody] attribute.

[FromUri] can do the Job.For your reference Microsoft Documentation
public class DateTimes
{
public datetime startdate { get; set; }
public datetime enddate { get; set; }
}
GetDays([FromBody] DateTimes _date)
{
//Controller action
}
URI:
api/values/?startdate=2018-06-01&endate=2018-07-01

Related

When POST has Id in the URI - should Id also be in Body Model Parameters? Web API

I have a ViewModel used in a Web API web service.
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
Then I have an ApiController with a method like this:
[Route("api/Customer/{Id}")]
public IHttpActionResult Post(int Id, [FromBody]Customer Customer)
{
...Add to db, Id also exists in Customer
return Ok();
}
Customer Id is available in the URI - but the Customer object contains a Customer Id - which could be passed into the Post.
This seems to be redundant - and may lead to the Id being put in the wrong place.
What is best practice? If I need to remove Id from Customer body how do I do it?
Reference - "Using HTTP Verbs with the Task Resource" - level 2 in the REST Maturity Model - states the Id should be passed in the URI for updating or inserting tasks with an Id:
Generally we call a POST method when we try to create a new record, in your case "Customer" and PUT method to update an existing record. But that is not mandatory. You can create and update an existing record by using POST method too.
In general, we set auto generated field to the Id (primary key) field of a table. That means, we don't need to pass data for Id field when we try to add a record. In that case you don't need to pass Id in your URI and you don't need to write a parameter for Id in your API POST method.
So your ajax method will be like this bellow,
$.ajax({
url: 'myURL',
type: "json",
contentType: 'application/json; charset=utf-8',
data: myFormData,
success: function(data)
{
alert("success");
}
})
As I mentioned above you can use the same method (POST) for add a new record or edit an existing record, so how is that possible ?
I assume you are passing Id value in your ajax code in data parameter.
So your API Post method will be like this,
[Route("api/Customer")] // I removed Id here
public IHttpActionResult Post([FromBody]Customer Customer)
{
if(Customer!=null && Customer.Id==0) //You can check Id with null value
{
// Add a new record to Customer
}
else
{
// fetch Customer data by Customer.Id and update here
}
return Ok();
}
As simple as that. But yes, you can write this update code in a PUT method.
But if you are passing value to the Id field(that is not auto generated field) in POST method, then also you can remove the Id parameter from your POST method and your Route will be like this,
[Route("api/Customer")]
Hope it helps :)

ASP.NET WebAPI method with two parameters, one array

i'm new to WebAPI and had a few qeustion to custom method calling.
So, im working with Entity Framework and created a WebAPI with basic CRUD methods.
But now i want to add some custom methods, is it possible to call arrays as parameters? And when yes, how?
This is my method:
public void AddRoles(Guid userid, Guid[] roleids)
So how it is possible to call this method through webapi?
I tryed it with
http://localhost:60690/api/MyController/AddRoles...
And is it possible to call void method? What is the response?
thanks and greetings,
Joerg
http://localhost:60690/api/MyController/AddRoles?userid=<user id guid here>&roleids=<guid1 here>&roleids=<guid2 here>...
As for the void method, of course it is possible, response will be with 200 code and empty body.
For GET you can refer to the following SO question:
How to pass an array of integers to ASP.NET Web API?
If you want to try to use POST then continue to read:
You should create a DTO for your parameters like such:
public class AddRoleModel
{
Guid UserId { get; set; }
Guid[] RoleIds { get; set; }
}
Change your method to accept accept POST and your new AddRoleModel DTO instead of the two different parameters like so:
[HttpPost]
public void AddRoles(AddRoleModel model)
{
...
}
And POST the json for that model to the method
json could look like this:
{
UserId: "{guid}",
RoleIds: ["{some guid}", "{some other guid}"]
}

WebApi parameters

I am very new to WebApi and don't understand parameters mapping.
I had a controller with HttpGet method with 2 parameters. In WebApiConfig mapping defined like
config.Routes.MapHttpRoute(
name: "MyActionApi",
routeTemplate: "api/{controller}/{action}/{p},{q}");
which seemed to work fine.
By analogy I've added another controller (DetailsController) that has 3 parameter HttpGet method.
I've added
config.Routes.MapHttpRoute(
name: "MyActionApi2",
routeTemplate: "api/{controller}/{action}/{p},{q},{r}");
But navigating to
http://mysite/api/Details/CrossReport/12,14,Peter
gives 404 error and says
No action was found on the controller 'Details' that matches the request.
But navigating like this
http://mysite/api/Details/FilterByDate/12,14?q=10
gives correct results.
Why is that? I'd like to have it comma separated as in the first case. And why it works in first case but not the second one?
Working controller's method:
public IEnumerable<Order> FilterByDate(DateTime dateStart, DateTime dateEnd).
Not working:
public IEnumerable<Detail> FilterByDate(DateTime dateStart, DateTime dateEnd, int maxCount)
Both have HttpGet attribute.
You need to define a matching action. Try adding an action with the following signature on your DetailsController class:
[HttpGet]
public IEnumerable<Detail> CrossReport(string p, string q, string r)
As you see, the action name and parameter names must match what you have on your route.
One thought, have you made sure that MyActionApi2 ahead of MyActionApi in you routing config? If I am not mistaken, It looks for the first possible match... and so MyActionApi would match (even if there are 3 parameters)
There are two types of parameters in WebApi: parameters in routes and parameters in body/url.
Parameter in route
In this example the id param is in the route.
http://mysite/api/Details/CrossReport/{id}
The route params are separate by "/" and there are part of the route.
http://mysite/api/Details/CrossReport/{id}/{name}/{detailId}
In your web api controller you must be:
public IEnumerable<Order> FilterByDate(int id, string name, int detailId)
Parameter in body/url
The parameters in url are separate by & and all this params are after a ? in route. For example.
http://mysite/api/Details/CrossReport?id=3&name="john"&detailId=5
And in your web api controller is the same:
public IEnumerable<Order> FilterByDate(int id, string name, int detailId)
If the object is compound by others properties:
public class MyObject
{
public int Id { get; set; }
public string Name { get; set; }
}
You can only have one in the body of the message and you have to send in the body on the message and your web api controller receipt them:
public IEnumerable<Order> FilterByDate(MyObject obj)

Is there any way to pass a whole model via html.actionlink in ASP.NET MVC 3?

How do I pass a whole model via html.actionlink or using any other method except form submission? Is there any way or tips for it?
Though it's not advisable in complex cases, you can still do that!
public class QueryViewModel
{
public string Search { get; set; }
public string Category { get; set; }
public int Page { get; set; }
}
// just for testing
#{
var queryViewModel = new QueryViewModel
{
Search = "routing",
Category = "mvc",
Page = 23
};
}
#Html.ActionLink("Looking for something", "SearchAction", "SearchController"
queryViewModel, null);
This will generate an action link with href like this,
/SearchController/SearchAction?Search=routing&Category=mvc&Page=23
Here will be your action,
public ViewResult SearchAction(QueryViewModel query)
{
...
}
No, you cannot pass entire complex objects with links or forms. You have a couple of possible approaches that you could take:
Include each individual property of the object as query string parameters (or input fields if you are using a form) so that the default model binder is able to reconstruct the object back in the controller action
Pass only an id as query string parameter (or input field if you are using a form) and have the controller action use this id to retrieve the actual object from some data store
Use session
You could use javascript to detect a click on the link, serialize the form (or whatever data you want to pass) and append it to your request parameters. This should achieve what you're looking to achieve...

ASP.NET MVC AllowHtml bug or something I didn't use correctly

My model contains a string field called "longdescription" which gets the value of the tinymce editor's content
Public class ArticleModel:BaseModel{
[StringLength(8000, ErrorMessage = "Long description must be in 8000 characters or less"), AllowHtml]
public string LongDescription { get; set; }
}
Here is my controller code
[HttpPost]
public ActionResult AddEdit(ArticleModel model)
{
string buttonName = Request.Form["Button"];
if (buttonName == "Cancel")
return RedirectToAction("Index");
// something failed
if (!ModelState.IsValid)
{
}
// Update the articles
}
My problem is when I use Request.Form to access the post value, it's working fine without throwing "A potentially dangerous...." error, but when I use Request.Params["Button"], it threw that errors. Is something I am missing?
Thanks
Updated
Sorry the answer Adam gave doesn't really answer my question. Can anyone give more suggestion?
Ideally you shouldn't really be using either. Those are more Web Forms centric values even though they 'can' be used.
Either pass in a FormsCollection item and check it there using collection["Button"] or even better - your cancel button itself should probably just do the redirect. Why post when you do nothing but redirect?
In your view you can emit the url via Url.Action() and put that into your button's click handler (client side)
It is the HttpRequest.Params getter that is throwing this exception. This getter basically builds and returns a key/value pair collection which is the aggregation of the QueryString, Form, Cookies and ServerVariables collections in that order. Now what is important is that when you use this getter it will always perform request validation and this no matter whether you used the [AllowHtml] attribute on some model property or if you decorated the controller action with the [ValidateInput(false)] attribute and disabled all input validation.
So this is not really a bug in the AllowHtml attribute. It is how the Params property is designed.
As #Adam mentioned in his answer you should avoid accessing request values manually. You should use value providers which take into account things such as disabled request validation for some fields.
So simply add another property to your view model:
public class ArticleModel: BaseModel
{
[StringLength(8000, ErrorMessage = "Long description must be in 8000 characters or less")]
[AllowHtml]
public string LongDescription { get; set; }
public string Button { get; set; }
}
and then in your controller action:
[HttpPost]
public ActionResult AddEdit(ArticleModel model)
{
string buttonName = model.Button;
if (buttonName == "Cancel")
{
return RedirectToAction("Index");
}
// something failed
if (!ModelState.IsValid)
{
}
// Update the articles
}

Resources