I have created a RESTful service using web API 2. I have the following route to return information about a stock item :
http://localhost/api/stockitems/{stockCode}
i.e. http://localhost/api/stockitems/BOMTEST1
I have stock codes in my system that contain forward slashes i.e. CA/BASE/SNG/BEECH. Naturally i can't request the details using the standard convention due to the slashes.
http://localhost/api/stockitems/CA/BASE/SNG/BEECH
I have tried URL encoding but it's not hitting the controller
http://localhost/api/stockitems/CA%2FBASE%2FSNG%2FBEECH
I just keep getting a 404
How do i handle this in Web API?
You will need to change your WebApiConfig. If you don't use IDs in more than this place you can just add a wildcard ({*id}) in to that part of the config:
config.Routes.MapHttpRoute(
name: "Default",
routeTemplate: "api/{controller}/{*id}",
defaults: new { id = RouteParameter.Optional }
);
I'd recommend creating a specific route for this case tho (assuming it is the only scenario where you need to allow slashes):
config.Routes.MapHttpRoute(
name: "StockItems",
routeTemplate: "api/stockitems/{*id}",
defaults: new { controller = "StockItems", id = RouteParameter.Optional }
);
You won't need to url encode the URLs this way.
Related
I'm trying to access an app on my localhost connected to IIS with the following endpoint I'm trying to hit https://api.url.com/api/tab. I have a TabController.cs in my Controllers folder. I also have a Views/Tab/Index.cshtml file and am wondering why I'm getting the following two errors:
<Error>
<Message>No HTTP resource was found that matches the request URI 'https://api.url.com/api/tab'.</Message>
<MessageDetail>No type was found that matches the controller named 'tab'.</MessageDetail>
</Error>
I have the same folder structure for a controller named Footer, and I'm able to access https://api.url.com/api/footer successfully. I've included my WebApiConfig.cs and RouteConfig.cs code below:
WebApiConfig.cs
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
RouteConfig.cs
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
I've a feeling it's something quite obvious I'm missing as I'm a bit new to .NET and the MVC structure. So any point in the right direction would be greatly appreciated.
You trying to access the MVC Controller (inherited from the System.Web.Mvc.Controller) as if it's the Web API Controller (inherited from the System.Web.Http.ApiController). As you know API controllers have their own routing configuration, which is completely separate from the rest of
the application. Therefore, to access an action method from the regular MVC controller don't use api prefix in the URL. Just enter https://api.url.com/tab to call the default action method in the tab controller.
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.
I currently have two controllers. One is MVC and the other is a WebApi controller.
I've published these two with ease on the IIS server with the following settings:
WebApiConfig:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
RouteConfig:
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "MVCControllerName", action = "MVCControllerName", id = UrlParameter.Optional }
);
Now I've added another WebApi controller, but when published, the webserver says:
No HTTP resource was found that matches the request URI
http://example.com/folder/api/Other?id=45&text=bla
Implementation of the OtherController:
public void Get(int id, string text)
{
// something simply gets commited to the DB
}
Also, something weird is happening when publishing... I've deleted the first WebApi controller from the solution, but for some reason when publishing I can STILL call that WebApi through HTTP GET request! Even though it's gone!
OTOH, it works fine in the debug mode..
What's going on here?
EDIT: It seems the problem is not in my code, but in VS' publishing feature. It has always worked until now. I had to manually copy the files to the server, which isn't the point of so-called "one-click publishing". Other similar SO questions seem to indicate it is a VS bug that is still not fixed in SP2.
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.
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}"
);