Get Request and APIs with/without parameter - c#

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

Related

Multiple controllers match the URL WebAPI

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.

ASP.NET Web API Error: No HTTP resource was found that matches the request URL

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.

WebAPI: Why do these methods match the routing template?

I just re-read the Routing in ASP.NET WebAPI document, and unless I'm missing something, by default WebAPI should only match methods that start with the HTTP verb. So why do I get this error when doing a POST against /api/mymodels:
ExceptionMessage, Multiple actions were found that match the request:
Post on type MyApp.Controllers.MyModelController
MaterializerFactory on type Pyro.Controllers.MyModelsController
MaterializerFactory on type Pyro.Controllers.MyModelsController
QueryableFactory on type Pyro.Controllers.MyModelsController
Only the first one should match. Here are the routes from my WebApiConfig.cs:
config.Routes.MapHttpRoute(
name: "Children",
routeTemplate: "api/{controller}/{id}/{childroute}/{childid}",
defaults: new { childid = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Here are the signatures for the erroneously matching methods:
public override JSONAPI.Core.IMaterializer MaterializerFactory() {}
public override TM MaterializerFactory<TM>() {}
public override IQueryable<T> QueryableFactory(Core.IMaterializer materializer = null) {}
The only possibly unusual thing about those is that they are inherited from an intermediate subclass of ApiController that I created…though I can't see how that would matter.
None of my methods are decorated with any WebAPI attributes (e.g. AcceptVerbs, HttpPost, etc.). If I decorate one of the above with [NonAction] it disappears from the list…but I don't know why it's even trying to match methods with those names?
Grrrr…okay, then I re-read the Routing and Action Selection in ASP.NET Web API, and buried in there is this gem:
HTTP Methods. The framework only chooses actions that match the HTTP method of the request, determined as follows:
You can specify the HTTP method with an attribute: AcceptVerbs, HttpDelete, HttpGet, HttpHead, HttpOptions, HttpPatch, HttpPost, or HttpPut.
Otherwise, if the name of the controller method starts with "Get", "Post", "Put", "Delete", "Head", "Options", or "Patch", then by convention the action supports that HTTP method.
If none of the above, the method supports POST.
Okay, so this doesn't really stop you from decomposing your code into methods, you just can't make any of them public, or you have to put [NonAction] on them. Still, I wish that was far more obvious than a bullet buried in the "advanced topics" document.

Using attribute routing and a global route for the same controller action

So, we're developing a webapi-heavy application that serves multiple types of clients (including a website that's a JavaScript client and multiple C# clients), and we'd like to use attribute routing to make friendlier urls. Something like this:
[RoutePrefix(Constants.RoutePrefixes.Api + "foo")]
[HttpPut]
FooController : ApiController
{
[Route("{fooId}/sub/{subFooId}")]
[HttpPut]
public HttpResponseMessage UpdateAFoo(int fooId, int subFooId)
{
return Request.CreateResponse(HttpStatusCode.OK);
}
}
And a request to /api/foo/1/sub/2 would hit this controller, as expected.
However, we have a message handler built into one of our default routes for processing identity tokens, and requests coming from clients that require that special behavior still have to use that route, or at least something that runs the appropriate message handler, and the following request (which uses that other route):
/integration/foo?fooId=1&subFooId=2
..gives me an error that no suitable controller could be found to handle the request. If I remove the route attribute, this second request hits my action method as expected.
I've read in several places that you can use both default and attribute routes, and I have both working in the application, but I haven't been able to use both for a specific action.
My route config looks like this:
public static void RegisterHttpRoutes(HttpConfiguration configuration)
{
var routes = configuration.Routes;
configuration.MapHttpAttributeRoutes();
routes.MapHttpRoute(
name: Constants.RouteNames.Api,
routeTemplate: Constants.RoutePrefixes.Api + "{controller}/{id}/{p}",
defaults: new { id = RouteParameter.Optional, p = RouteParameter.Optional }
);
routes.MapHttpRoute(
name: Constants.RouteNames.IntegrationApi,
routeTemplate: Constants.RoutePrefixes.IntegrationApi + "{controller}/{id}/{p}",
defaults: new { id = RouteParameter.Optional, p = RouteParameter.Optional },
constraints: null,
handler: new SuperDuperAuthenticationHandler())
);
}
Is that just how it works? Do I need a separate action without the route attribute? Is there a way to specify message handlers for attribute routes? Can I have both attribute and convention routing apply to the same action method? I'm not really clear on what my options are.
Controllers/Actions which are decorated with attribute routes cannot be reached via routes matched by conventional routing...so the behavior that you are seeing is expected...
Per-route message handlers are not supported for attribute routes.
Can you use AuthenticationFilterAttribute for your scenario?...if you need this filter for a set of controllers, then you could probably create a base controller decorated with this filter and let all these set of controller derive from it...

Routes for specific controllers

In my Web API scenario, this (default) route works well for almost all my controllers.
config.Routes.MapHttpRoute(
name: "DefaultRoute",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
and for a couple of my controllers, I want to have them mapped by their action name, like this:
config.Routes.MapHttpRoute(
name: "ActionRoute",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Background:
Most of my controllers are to be mapped by default to GEE, POST, PUT and DELETE.
However, I reserve some "admin" controllers that perform operations, such as data initialization, cleanup and testing stuff. These controllers may have multiple methods that are excetude via GET, hence the second route.
If I use either of these routes, they work well, however, if I set both, I get one of two failures:
Default route first:
api/books/ -> OK
api/books/someGuidId -> OK
api/admin/someAdminAction -> 500 Internal Server Error (multiple actions found)
api/test/sometestAction -> 500 Internal Server Error (multiple actions found)
Action route first:
api/books/ -> OK
api/books/someGuidId -> 404 Not Found (no action that matches "someGuidId")
api/admin/someAdminAction -> OK
api/test/sometestAction -> OK
My question is: how can I have these routes working out at the same time?
[EDIT]
api/books/someGuidId is not an important call, I can live without it (via GET) however, POST, PUT and DELETE to same url will fail, wich is not acceptable.
The order of the routs matter, so You should set more specific routs first:
config.Routes.MapHttpRoute(
name: "ActionRoute",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultRoute",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Edit:
Default route first:
api/books/
This url matchs the first pattern: api/{controller}/{id}.
Framework looks for books controller with GET (PUT,POST,DELETE) method, and finds it. This method is invoked with default id value.
api/admin/someAdminAction
This url also matchs the first pattern. (BTW, It doesn't matter how You name Your parameters in the route definition: id or superParamName, When Framework compares URL with routs it will look only for patterns and can not distinguish by param names.
The Framework will try to invoke GET (PUT,POST,DELETE) method of admin controller with someAdminAction id (Which, I suppose, is not a valid id value:)
Similar thing You get when You define Action route first:
api/books/
Matchs api/{controller}/{id}, id can be omitted, cause it was set to "optional".
api/books/someGuidId
Matchs api/{controller}/{action}/{id}, so, when Framework looks for someGuidId action method in book controller and it doesn't find it, it throws an exception. 404 Not Found (no action that matches "someGuidId")
Edit 2: I think, in case You will always pass id param for "Actions" and place "Actions" route first, You will not get any collisions. Try this.
If You need id param be optional in both cases, You can just remove it from both routs and pass it in a normal way via ?(question mark).
One of this would fix Your routing problem.

Categories

Resources