Refer to the official ASP.NET attribute routing documentation, it seems that Route attribute can be used without RoutePrefix.
But, in my webapi controller, below cases are happened.
1. Not working. (error: No matched http route found)
public class GroupController : ApiController
{
[Route("api/group/{id}/register")]
public IHttpActionResult Post(int id, InputModel model)
{
return Ok();
}
}
2. Working good.
[RoutePrefix("api/group")]
public class GroupController : ApiController
{
[Route("{id}/register")]
public IHttpActionResult Post(int id, InputModel model)
{
return Ok();
}
}
Is it right that Route attribute should be used with RoutePrefix, or what am I missing?
In addition, below code is my WebApi route config in WebApiConfig.Register class.
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}/{subId}",
defaults: new { id = RouteParameter.Optional, subId = RouteParameter.Optional }
);
Related
I'm trying to get this method to work:
public class DocumentenController : ApiController
{
[HttpPost]
[Route("DeleteDocument/{name}/{planId}")]
public IHttpActionResult DeleteDocument(string name, int planId)
{
_documentenProvider.DeleteDocument(planId, name);
return Ok();
}
}
This is the WebApiConfig :
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: UrlPrefix + "/{controller}/{action}/{id}",
defaults: new {id = RouteParameter.Optional}
);
But I get a 404 when I call it like this using a post:
http://localhost/QS-documenten/api/documenten/deletedocument/testing/10600349
What is the proper way do solve this?
The URL in example does not match attribute route on controller.
To get
http://localhost/QS-documenten/api/documenten/deletedocument/testing/10600349
to work, assuming that http://localhost/QS-documenten is the host and root folder, and that api/documenten is the api prefix then add a route prefix to the controller...
[RoutePrefix("api/Documenten")]
public class DocumentenController : ApiController {
//eg POST api/documenten/deletedocument/testing/10600349
[HttpPost]
[Route("DeleteDocument/{name}/{planId}")]
public IHttpActionResult DeleteDocument(string name, int planId) {
_documentenProvider.DeleteDocument(planId, name);
return Ok();
}
}
Source: Attribute Routing in ASP.NET Web API 2 : Route Prefixes
You must send your request as follows:
http://localhost/QS-documenten/deletedocument/testing/10600349
When you use route attribute, the custom route override default api routing configuration.
I am trying to post to the following Web API:
http://localhost:8543/api/login/authenticate
LoginApi (Web API) is defined below:
[RoutePrefix("login")]
public class LoginApi : ApiController
{
[HttpPost]
[Route("authenticate")]
public string Authenticate(LoginViewModel loginViewModel)
{
return "Hello World";
}
}
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 }
);
}
Here is the error I get:
Request URL:http://localhost:8543/api/login/authenticate
Request Method:POST
Status Code:404 Not Found
Remote Address:[::1]:8543
Your controller name "LoginApi" needs to end in "Controller" in order for the framework to find it. For example: "LoginController"
Here is a good article which explains routing in ASP.NET Web API: http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api
You are using login as your route prefix on your controller so trying to call
http://localhost:8543/api/login/authenticate
will not be found as this code
[RoutePrefix("login")]
public class LoginApi : ApiController
{
//eg:POST login/authenticate.
[HttpPost]
[Route("authenticate")]
public string Authenticate(LoginViewModel loginViewModel)
{
return "Hello World";
}
}
will only work for
http://localhost:8543/login/authenticate
You need to change your route prefix to
[RoutePrefix("api/login")]
public class LoginApi : ApiController
{
//eg:POST api/login/authenticate.
[HttpPost]
[Route("authenticate")]
public string Authenticate(LoginViewModel loginViewModel)
{
return "Hello World";
}
}
Notice you are using both attribute routing on the controller/action and convention routing with config.Routes.MapHttpRoute.
config.Routes.MapHttpRoute will map the routes as per your definition "api/{controller}/{id}".
While attribute routing, will map the routes based on how you've defined them: /login/authenticate.
Also, since you are using both attribute routing and convention routing, attribute routing takes presendence. I would stick to using one or the other. Having both adds a bit of confusion as to what route will be used to access an action method.
API
[RoutePrefix("api/diagnostics")]
public class DiagnosticsController : ApiController
{
[HttpPost]
[Route("pings")]
public IHttpActionResult Pings(Ping ping)
{
}
}
Ping
public class Ping
{
public Guid ServerKey {get;set;}
public DateTime CreatedDateTime {get;set;}
}
I am trying to test the class using Postman application. Here is the Screenshot.
The message I get back is:
{
"message": "No HTTP resource was found that matches the request URI 'http://localhost:61668/api/diagnostics/pings'.",
"messageDetail": "No action was found on the controller 'Diagnostics' that matches the request."
}
I am failing to understand why it does not match the post action in Diagnostics controller. The only other route configured is the default route:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{key}",
defaults: new { key = RouteParameter.Optional }
);
Also attribute routing is enabled: config.MapHttpAttributeRoutes();
Try to mark your parameter with [FromBody] attribute - public IHttpActionResult Pings([FromBody] Ping ping).
I have one api Controller with two different routes:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "downloadApi",
routeTemplate: "api/{controller}/{download}/{id}"
);
I want to have these actions with these routes:
1 - For downloading a file: public GetFile()
GET /api/v1/documents/download/id
2 - For getting file info: public GetFileInfo()
GET /api/v1/documents/id
So in my controller I want to have these actions:
public class DocumentsController : apiController
{
// triggers with GET /api/v1/documents/download/id
public HttpResponseMessage GetFile(){}
// triggers with GET /api/v1/documents/id
public HttpResponseMessage GetFileInfo(){}
}
How can I do this?
Thanks
Here's an example to explain my comment. Rather than defining a route in your WebApiConfig.cs that applies to some routes, but not others (which I don't think you can do, but I've never tried so maybe you can), use the Route method attribute to define the route used at the method level.
public class DocumentsController : apiController
{
// triggers with GET /api/v1/documents/download/id
[HttpGet]
[Route("api/v1/documents/download/{id}")]
public HttpResponseMessage GetFile(int id){}
// triggers with GET /api/v1/documents/id
[HttpGet]
[Route("api/v1/documents/{id}")]
public HttpResponseMessage GetFileInfo(int id){}
}
Untested, but it should work
I am a bit new to WebApi so maybe someone can explain this to me, the default route added for WebApis is:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
So given an ApiController that looks like this:
public class LookupController : ApiController
{
[HttpGet]
public IHttpActionResult GetCountries()
{
// do stuff
return Ok();
}
[HttpGet]
public IHttpActionResult GetStates()
{
// do stuff
return Ok();
}
}
How would it know which action to call? It wouldn't right?
Shouldn't the default Route be more like:
config.Routes.MapHttpRoute(
name: "ApiWithAction",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Or should you only have one GET/UPDATE/DELETE etc per ApiController? Which really wouldn't cater for some scenarios...
Web API figures out which method to call based on routing data. You don't need to specify the action because Web API will use the verb(GET, POST,DELETE,etc) of the request.
If you would like to have multiple GET actions in a controller, you can specify a route for each action.
[Route("api/lookup/countries"]
[HttpGet]
public IHttpActionResult GetCountries()
{
// do stuff
return Ok();
}
[Route("api/lookup/states"]
[HttpGet]
public IHttpActionResult GetStates()
{
// do stuff
return Ok();
}
More information is available here http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api
Strictly speaking your controller should never look like that. Out of the box the idea is that a controller handles requests for a single entity (e.g. Customer) and the operations, which map to HTTP verbs, operate against that entity. So under normal circumstances your controller would look something like the following (created using scaffolding against a simple model class, method bodies omitted for brevity):
public class CustomerController : ApiController
{
public IQueryable<Customer> GetCustomers()
{
}
[ResponseType(typeof(Customer))]
public IHttpActionResult GetCustomer(int id)
{
}
[ResponseType(typeof(void))]
public IHttpActionResult PutCustomer(int id, Customer customer)
{
}
[ResponseType(typeof(Customer))]
public IHttpActionResult PostCustomer(Customer customer)
{
}
[ResponseType(typeof(Customer))]
public IHttpActionResult DeleteCustomer(int id)
{
}
}
If you have more business-oriented operations to perform (e.g. BillCustomer), rather than just the basic CRUD operations, I would suggest creating a separate route for those. We did that in one of our applications and it created a nice logical separation. For example:
config.Routes.MapHttpRoute(
name: "RestApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "RpcApi",
routeTemplate: "rpc/{controller}/{action}"
);