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)
Related
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
i have setup the route to be:
[Route("{id}/users/search/{search}")]
and the associated action is:
SomeAction(int id, string text)
The service has the following function.
for the resource with id={id} and the users of this resource get the users that match with the {search} term (username, email etc).
the {search} can have a value so the service returns only the matching entities or does not have a value (empty string or null) so the service returns everything.
For the part with a value it works fine.
For the second part i cannot find something to set the get request that matches the empty string.
i tried the following:
1/users/search/null {search} = "null"
1/users/search/ does not match route
1/users/search does not match route
has anyone a hint how this could be done?
Update: i have tried to replace the action:
SomeAction(int id, string text)
with:
SomeAction(Model model) where model is
public class ApplicationUserSearchModel
{
[Required]
public int Id { get; set; }
[Required(AllowEmptyStrings = true)]
public string MatchText { get; set; }
}
with no luck since i don't know what to send in order to match the url to this.
You should tag your search parameter with ? to mark it as optional in the route, and set it to null by default in your action.
[Route("{id}/users/search/{search?}")]
public HttpResponseMessage Search(int id, string search = null)
I originally thought the route/action parameter names were the issue, but I was incorrect. Here is the previous answer:
The parameter names in your route definition and your action don't match, which is causing your problem.
[Route("{id}/users/search/{search}")]
public HttpResponseMessage Search(int id, string text)
You should update the string parameter in your action from text to search, to match the parameter name in your Route attribute.
I can't figure out why I need to create an empty method signature to allow a Get rest call with 3 null parameters to work. I have the following Code:
public class SessionPresenterController : ApiController
{
public HttpResponseMessage Get()
{
return Get(null, null, null);
}
public HttpResponseMessage Get(int? codeCampYearId, int? sessionId, int? attendeesId)
{
and in my WebApiConfig I have
config.Routes.MapHttpRoute
("API Default Rest", "rest/{controller}/{id}",
new { id = RouteParameter.Optional });
config.Routes.MapHttpRoute
("API Default RPC", "rpc/{controller}/{action}/{id}",
new { id = RouteParameter.Optional });
If I call /rest/SessionPresenter without any parameters and I Don't have the Get() defined, the Get with the three null parameters is not found.
Why not?
Try specifying defaults for your parameters:
public HttpResponseMessage Get(
int? codeCampYearId = null,
int? sessionId = null,
int? attendeesId = null)
{
//...
}
Jacobs' snippet will solve your issue. Let's answert the question why not? why get is not found..
There is a complete documentation of the Routing and Action Selection. Let's use some extratc and reveal what happens:
Action Selection
Create a list of all actions on the controller that match the HTTP request method.
If the route dictionary has an "action" entry, remove actions whose name does not match this value.
Try to match action parameters to the URI, as follows:
For each action, get a list of the parameters that are a simple type, where the binding gets the parameter from the URI. Exclude optional parameters.
From this list, try to find a match for each parameter name, either in the route dictionary or in the URI query string. Matches are case insensitive and do not depend on the parameter order.
Select an action where every parameter in the list has a match in the URI.
If more that one action meets these criteria, pick the one with the most parameter matches.
Other words, if there will be only Get with three parameters (omited Get()), to decided which action should be selected:
Selected was Get(int? codeCampYearId, int? sessionId, int? attendeesId)
still the Get(int? codeCampYearId, int? sessionId, int? attendeesId) is selected
URL is /rest/SessionPresenter
no optional parameters excluded. All have to be found
URL does not have a match for all three parameters
So to solve it, we have to either pass all params (empty, null):
/rest/SessionPresenter?codeCampYearId&attendeesId&sessionid
Or change the signature to have parameters optinal (Jacobs' answer), and skipped during the action selection
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}"]
}
I am having difficulty with a nested Web API routing set-up.
Given a routing config of:
config.Routes.MapHttpRoute(
name: "UsersGroups",
routeTemplate: "api/users/{userID}/groups/{groupID}",
defaults: new { controller = "UsersGroups", groupID = UrlParameter.Optional }
);
and controller actions like thus:
public AuthGroup Get(long userID, int groupID)
{
//Get specific group here
}
public IEnumerable<AuthGroup> Get(long userID)
{
//get all groups for user here
}
Calling this route /api/users/1528/groups gives this error:
The parameters dictionary contains a null entry for parameter groupID of non-nullable type System.Int32 for method AuthGroup Get(Int64, Int32) in UsersGroupsController. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
I was expecting it to grab the action with the single long parameter, but obviously for some reason it's ignoring this and going straight for the one with most arguments.
Based on what's available at MS on how Web API interprets routes: http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-and-action-selection I think what I have should be correct, but obviously it is not working as it seems like it should.
You should use RouteParameter.Optional (from Web API), not UrlParameter.Optional (from ASP.NET MVC).
Everything will behave as you want then.
More info:
UrlParameter.Optional - http://msdn.microsoft.com/en-us/library/system.web.mvc.urlparameter.optional(v=vs.108).aspx
RouteParameter.Optional - http://msdn.microsoft.com/en-us/library/system.web.http.routeparameter.optional(v=vs.108).aspx