Should I follow WebApi route convention? - asp.net-web-api

Should I follow WebApi route convention?
I want to change my default route config (api/{controller}/{id}) to api/{controller}/{action}/{id}
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Is it ok to add {action} placeholder to my route?
What is the pros/cons of default WebApi route?

Yes, you can have action names in your routes and use them.
First, you have to add the following to your WebApiConfig.cs
routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Be sure to add this before the default route or else it will not work.
Then add ActionName attribute in your controller methods
public class ProductsController : ApiController
{
[HttpGet]
[ActionName("Thumbnail")]
public HttpResponseMessage GetThumbnailImage(int id);
}
Then your route will be /api/Products/Thumbnail
For more info check the Routing by Action Name from here
Be sure to marks as answer if this helped you :)
By using Custom Action Names, you can move beyond the HTTP verbs in your controllers and have customized methods available in the API

Related

How can I set HttpPost attribute globally in Web API?

I am working on an existing Web API in which I need to make all actions HttpPost based. For achieving this I am using configuration like this.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}/{action}",
defaults: new { id = RouteParameter.Optional },
constraints: new { httpMethod= new HttpMethodConstraint(HttpMethod.Post) }
);
But this is not working on actions having name strating with "Get". But It is working fine when I am decorating action with [HttpPost] atrribute like this
[HttpPost]
public IHttpActionResult GetUserByName()
There are many actions like this and I want to avoid decorating each Action with [HttpPost] atrribute. Is there any way to set [HttpPost] atrribute globally?

Visual Studio Web API: How to change the routing path

I am working on Web Api. By default, it uses the api/{controller}/{id} as a url. I am able to have it to route api/device/{controller}/{id} but this will affect to all Web API route to that path.
But I only want certain controller to be in api/device/{controller}/{id} and the rest will go to another path.
I saw something like RoutePrefix but it doesn't seem to work...
[RoutePrefix (api/data/abc)] where abc is the controller name.
Add the custom route mapping in WebApiConfig.cs file before default route map:
By adding this before, any requests which matches the custom route will be executed, else the other one.
config.Routes.MapHttpRoute(
name: "CustomRoute",
routeTemplate: "api/device/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
If you want to customize the routing with a per controller approach, then you have to use Attribute Routing instead of the standard convention-based routing.
Decorate your controllers with RoutePrefix attribute, and your actions with Route attribute in this way:
[RoutePrefix("api/device/mydevice")]
public class MyDeviceController : ApiController {
[Route("{id}")]
[HttpGet]
public IHttpActionResult Get(int id) {
//DoWork
//...
}
}
And remember to enable attribute routing on the HttpConfiguration object:
config.MapHttpAttributeRoutes();
You may also remove MapHttpRoute method calls if you do not want to allow access to your actions in the standard convention-based way.
More on attribute routing on the official documentation.

Actions and QueryString parameters in Web API

I want to have my generic route determine if a query string was passed in the Url like this
http://localhost/query/DailyLogs/1?dateOfLog='1/13/2013'
Here is my current Route definition:
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "query/{controller}/{id}",
defaults: new { id = RouteParameter.Optional}
);
I have read some answers that say to add the dateOfLog value as an optional action on the Route defintion:
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "query/{controller}/{id}/{dateOfLog}",
defaults: new { id = RouteParameter.Optional,
dateOfLog = RouteParameter.Optional }
);
This does not seem to work, maybe I am doing something wrong, I am not sure.
This is how I am currently handling the problem, but I would like to use the ModelBinding power of the Routing Engine:
var queryValue = Request.RequestUri.ParseQueryString();
string dateOfLog = queryValue["dateOfLog"];
Please tell me how to create a Route definition that will use ModelBinding correctly and map my custom url to the controller's parameters.
In the controller action, just include DateTime dateOfLog as a method parameter and continue to use the query string as it will get mapped just fine, Web API will use the correct method overload if it finds it.

Specify valid controllers for a route

I've to provide two Web API controllers PublicController and PrivateController for our system. These should have the following routes:
/public/{controller}/{id}
and
/private/{controller}/{id}
On the firewall, all requests to /private are blocked and only available from inside the network. But by convention, both of my controllers are available for both routes, so I could request PrivateController (which should only be available under /private) with the url /public/PrivateController/1.
Is there a way to specify valid controllers for a route, so that the PrivateController is only available for the private route? Or are there some other practices to fullfill this requirement?
Thanks for your replies.
You can use the constraints parameter to provide restrictions on the controller name in the simplest case with a very simple regular expression:
config.Routes.MapHttpRoute(
name: "private",
routeTemplate: "api/private/{controller}/{id}",
defaults: new { id = RouteParameter.Optional },
constraints: new { controller = #"private" }
);
config.Routes.MapHttpRoute(
name: "public",
routeTemplate: "api/public/{controller}/{id}",
defaults: new { id = RouteParameter.Optional },
constraints: new { controller = #"public" }
);
So the now the "private" route only accept the controller which named Private and the "public" route will only accept the controller which named Public. If you have multiple public and private controllers you can easily extend the regex to match them.
If the regex is not enough for your needs you can create your custom route contaraint with implementing the IRouteConstraint interface. You can find an example implantation In this article.

Web API route mapping to root

I am setting up an ASP.NET MVC 4 Web API to accept requests from a 3rd party server, and I simply can't figure out how to set the route mappings.
Assume the 3rd party server expects to get responses for:
http://[my_server]/authorize?user=[user name]&session=[session token]&item=[item]
http://[my_server]/release?user=[user name]&session=[session token]
Alternatively, the requests can use a dedicated path, i.e.:
http://[my_server]/***api***/authorize?user=[user name]&session=[session token]&item=[item]
http://[my_server]/***api***/release?user=[user name]&session=[session token]
I would like to be able to support both alternatives.
Additional requests, following the more traditional /controller/id form, should be implemented too, but I'd like to focus on the above (I'm not even sure that Web API is the way to go here).
I have written the following controller:
public class MyController : ApiController
{
[HttpGet]
[ActionName("authorize")]
public string Authorize(string user, string session, string item)
{
...
// return "OK" or "DENY";
}
[HttpGet]
[ActionName("release")]
public string Release(string user, string session)
{
...
return "OK";
}
}
and tried everything I could find in SO and elsewhere in WebAppConfig.Register, but I keep getting a 404 error when I try the request in the browser:
http://localhost:22332/api/authorize?user=ury&session=token&item=an_item
My question is, what do I have to do - specifically in WebAppConfig.Register and in the controller - in order to serve the above requests (assuming my test URL is correct...)?
You're getting tripped up by Web API conventions for matching a controller name in a URL to a controller class.
If the name of your controller is "MyController", then the URL to request is:
http://localhost:22332/api/my/authorize?user=ury&session=token&item=an_item
To support a request w/o the "api" bit in the URL, simply add a second route definition in the Register method of WebApiConfig.cs. Since you're also using "actions" in your URL's, you'd need these two routes:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "RootWebApiRoute",
routeTemplate: "{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
If you want to omit the "api" prefix as well as the controller parameter, I think you need a different route definition. Something like:
public static void Register(HttpConfiguration config) {
config.Routes.MapHttpRoute(
name: "ActionOnlyRoute",
routeTemplate: "{action}",
defaults: new { controller = "My" }
);
}
Here, we'll only look for an "action" in the URL and route everything to your "MyController" class.
After a few hours working on this and with a lot of help from the Route Debugger - Thanks Phil Haack! - I've found both the problem and the solution.
The problem: Route mapping matching is ordered, and RouteTable.Routes, from which the app's route mapping is initialized, contains quite a few of them. The request pattern I was looking for also matched some of these mappings ("authorize" was matched as a controller, for example).
The solution: add "my" route mappings before the default mappings.
Yeah, right...
Since most operations are not supported on HttpRouteCollection, the resulting code is a bit ugly, but it works:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
routesCopy = new RouteBase[RouteTable.Routes.Count];
RouteTable.Routes.CopyTo(routesCopy, 0);
config.Routes.Clear();
config.Routes.MapHttpRoute(name: "AuthorizeWebApiRoute", routeTemplate: "authorize", defaults: new { controller = "My", action = "authorize" });
config.Routes.MapHttpRoute(name: "ReleaseWebApiRoute", routeTemplate: "release", defaults: new { controller = "My", action = "release" });
foreach (var route in routesCopy)
{
RouteTable.Routes.Add(route);
}
}
}
As long as "my" route mappings don't match the default mappings (I made them specific enough not to), I'm all good - I think...

Resources