I have two web api methods with following route url's
[HTTPGET]
[Route("{Code}/{Id}")]
[HTTPGET]
[Route("{Code}/counter")]
Request /01/counter
{Id} is also a string parameter. Hence I am now getting error when calling Second api. "Multiple controllers action found for this Url" as webapi considers /01/counter valid for both routes.
I have seen few solutions with regex but can't find a working one yet. What is the good solution for this so that both Url's work as expected.
UPDATE:
I Found that the issue was occuring as the two methods were in different controllers hence webapi was having a problem in deciding which controller to choose. Once I moved them in the same controller, the problem is solved since , the route arguments are checked after controller is fixed.
If you are using WebAPI 2, you can use the RouteOrder property to define precedence when multiple actions match.
https://learn.microsoft.com/en-us/aspnet/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2#route-order
[HttpGet]
[Route("{Code}/{Id}", RouteOrder = 2)]
[HttpGet]
[Route("{Code}/counter", RouteOrder = 1)]
If you are using MVC, you can use Order property:
https://learn.microsoft.com/en-us/previous-versions/aspnet/mt150670(v=vs.118)
[HttpGet]
[Route("{Code}/{Id}", Order = 2)]
[HttpGet]
[Route("{Code}/counter", Order = 1)]
There is no problem in your snippet code.
[HTTPGET]
[Route("{Code}/{Id}")]
[HTTPGET]
[Route("{Code}/counter")]
It seems that in your Web API routing configuration action is not specifying like this. It's by default setting provided by the template.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
In WebApi.config file use this code
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional, action = RouteParameter.Optional }
);
Hopefully it will solve your problem.
Related
I've played with web api a bit before, and I always seem to run into the same problem where my methods do not get routed to.
My application has that application insights package and so I can see that it captures the requests I make - by looking at the requests line above my method signature, but they never actually execute and App Insights reports a failed request.
Here is my WebApiConfig
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
;
Here is my simple controller and method
public class ExampleController : ApiController
{
[HttpGet]
[ActionName("Test")]
public IHttpActionResult Test()
{
return Ok();
}
}
This is part of an MVC application, so when I launch the project, the Home/Index view is displayed in my browser. I then go to postman and create a new Get request pointing at
http://localhost:port/api/Example/Test
But this results in a 404.
I must be doing something wrong as I always run into this
The default mapping for WebAPI does not include the action as part of the route, as it, by default, expects the controller to be the main identifier for a resource and the GET/POST/PUT/DELETE verb to define which operation is run.
So, even though you are manually specifying the ActionName of "Test", there's nothing in the default handler to pattern match against it.
You could adjust your default mapping to include actions, like so:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
;
However, you might find you prefer attribute routing (I know I do), which you could apply to your controller like this:
[RoutePrefix("api/Example")]
public class ExampleController : ApiController
{
[HttpGet]
[Route("Test")]
public IHttpActionResult Test()
{
return Ok();
}
}
To enable attribute routing, you will need to add the following to your startup configuration:
config.MapHttpAttributeRoutes();
I have two APIs,
[HttpGet]
public bool WithoutParamBooleanResponse()
and
[HttpGet]
public string ComplexReferenceTypeParamStringResponse([FromUri]ComplexRefType VariableComplexRef)
However, this leads to having error
multiple actions were found that match the request web api get.
If I were to add another dummy parameter for the second method, the whole thing works. Could someone explain why a parameterless method and a method with a complex parameter are seen similar by the API ?
why a parameterless method and a method with a complex parameter are
seen similar by the API ?
When a parameter is annotated with FromUri attribute and is a complex type, the value is constructed from the query params, therefore the route for both methods would be the same (since the query params are not taken into account).
Try to create a new route like:
config.Routes.MapHttpRoute(
name: "ComplexRefType",
routeTemplate: "api/{controller}/{action}/{VariableComplexRef}",
defaults: new { VariableComplexRef = RouteParameter.Optional }
);
and try to add attribute on your action
[Route("ComplexReferenceTypeParamStringResponse/{VariableComplexRef?}"]
You need to add an action to your routing url.
config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional }
When calling a route and only passing in a controller, the routing assumes there is only one action for each method(GET,POST..) and looks for it. This is why you are having an error with more than one GET.
When you also pass an action, it is more specific to look for the correct action with this method
I checked other sulotion sugessions that asked and answered here, but still couldn't find what's wrong with my own code.
I have a 'members' and 'fals' (means something like blogpost) controllers. every member has its own multiple 'fals' records, so I wanted to list 'fals' belongs to spesific member.
I wanted to add a custom action to my Web API to do this job, here is it:
[HttpGet, ActionName("fals"), Route("members/{id}/fals")]
public IQueryable<Fal> fals(int id)
{
Member member = db.Members.Find(id);
return member.Fals();
}
So, here is WebApiConfig customization:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}/{action}",
defaults: new { id = RouteParameter.Optional, action = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApiWithAction",
routeTemplate: "api/{controller}/{id}/{action}"
);
I know there are two Route, even if I comment or uncomment the second Route it changes nothing.
When I try to call http://localhost:51601/api/members/1/fals URL it says:
{
"Message": "No HTTP resource was found that matches the request URI 'http://localhost:51601/api/members/1/fals'.",
"MessageDetail": "No action was found on the controller 'Members' that matches the name 'fals'."}
ID 1 existed, as I pasted the code fals existed, but couldn't figured it out.
Thanks!
I'm not sure if you are using attribute routing or not, but try removing the Route attribute from the controller action.
[HttpGet, ActionName("fals")]
If the route is "interfering" with the conventional routing I expect this would work, or you could also try calling the URL without the /api/ part because that it missing from the Route you have specified in the attribute.
I got this Route:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { id = UrlParameter.Optional }
);
And this Actions:
[System.Web.Http.HttpPost]
[System.Web.Http.ActionName("GetLoginSeed")]
public object GetLoginSeed()
[System.Web.Http.HttpPost]
[System.Web.Http.AllowAnonymous]
[System.Web.Http.ActionName("Authenticate")]
public object PerformLogin(JObject jr)
This is the Post Request:
http://localhost:61971/api/Login/GetLoginSeed
Why I always get an multiple actions were found that match the request error?
I got this Route:
What you have shown is a route for MVC controllers. I hope you realize that Web API controllers are an entirely different thing. They have their own routes defined in the ~/App_Start/WebApiConfig.cs.
So make sure tat you have included the {action} token in your Web API route definition (which I repeat once again has nothing to do with your MVC route definitions):
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}"
);
In web api the default route is:
/api/locations/123?days=5
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
But what if I wanted the route to look like this /api/locations/123/events?days=5 while still being able to hit the LocationsController with a route like this /api/locations/123?state=md
Controller:
public class LocationsController : ApiController {
// GET api/locations/123/events?days=5
public IEnumerable<Event> GetEventsByDays(int idLocation, int days) {
// do stuff
}
// GET api/locations/123?state=md
public IEnumerable<Location> GetLocationsByState(string state) {
// do stuff
}
}
There is really two questions here:
Does it make sense to have a LocationsController that returns events or should there be a completely separate controller for that?
How would the route in the WebApiConfig be setup to allow for routes like this?
You're talking about two differents things :
Routing
Routing is used in ASP.NET MVC & Web Api to map URLs directly to a controller and/or an action. This is especially useful for readability, as the developer can focus on designing URLs that are human readable (for example, product support and search engine indexing). What is important here, is that there is no unique relation between a route and a controller. You can create 10 routes if you want that will map the same controller/action.
For example, your two routes can be like this
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{idLocation}/events/{days}",
defaults: new { id = RouteParameter.Optional }
);
Also note that /api/locations/123?state=md is not correct for the default route template. It's /api/locations/123. Because you have an extra parameter in the url, you will execute GetLocationsByState.
Controller
It's recommanded to have a single responsiblity per controller and to keep it as small as possible. Your business logic should be somewhere else.
Jeffrey Palermo (creator of Onion Architecture) say
if you can’t see a ASP.NET MVC action method on a screen without having to scroll, you have a problem
At the end, as you everything, you can do what you want without paying attention if it's good or bad. The difficulty is not always to setup an architecture but to maintain and to follow your own rules.
I hope this will help you.
I suppose you are not so familliar with routing, so do not hesitate to read intro and action selection.
You can have a separate controller for events, if you would want to manipulate events independent of location. Please see if this is of help.