I'm new to Web API, I know one of the difference between web api and MVC
is that web API uses HTTP method to choose which method to invoke by convention.
Now I added an action parameter into my route,
I think there's something went wrong to my knowledge,
the route doesn't work.
Here's my controller, main problem is that I just need to map the SignIn and SignUp method.
(Other RestFul methods work fine.)
public class UsersController : ApiController
{
private Model1Container db = new Model1Container();
public IEnumerable<User> GetUsers()
{
return db.UserSet;
}
[HttpPost]
public IHttpActionResult SignIn(string account, string password)
{
...
}
[HttpPost]
public IHttpActionResult SignUp(User user)
{
...
}
public IHttpActionResult GetUser(int id)
{
...
}
public IHttpActionResult PutUser(int id, User user)
{
...
}
public IHttpActionResult DeleteUser(int id)
{
...
}
}
What I've tried is even to add the direct constraint on my top two routes for both methods,
config.Routes.MapHttpRoute(
name: "UsersSignIn",
routeTemplate: "api/Users/SignIn",
defaults: new { controller = "Users", action = "SignIn" }
);
config.Routes.MapHttpRoute(
name: "UsersSignUp",
routeTemplate: "api/Users/SignUp",
defaults: new { controller = "Users", action = "SignUp" }
);
config.Routes.MapHttpRoute(
name: "RestFulUser",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional },
constraints: new { controller = "Users" }
);
But it turns out [POST] "api/Users/SignUp" and "api/Users"
could map to SignUp method, but "api/Users/SignIn" always not succeed.
And here's about how I try the API in POSTMAN
Please point out where did I go wrong ..
If using attribute routing you need to differentiate between the two routes. Also once using routes it is all or nothing on the controller itself. You can't mix them with convention-based routes.
Review Attribute Routing in ASP.NET Web API 2
[RoutePrefix("api/Users")]
public class UsersController : ApiController {
private Model1Container db = new Model1Container();
[HttpGet]
[Route("")] //Matches GET api/Users
public IEnumerable<User> GetUsers() {
return db.UserSet;
}
[HttpPost]
[Route("SignIn")] //Matches POST api/Users/SignIn
public IHttpActionResult SignIn(string account, string password) {
//...
}
[HttpPost]
[Route("SignUp")] //Matches POST api/Users/SignUp
public IHttpActionResult SignUp([FromBody]User user) {
//...
}
[HttpGet]
[Route("{id:int}")] //Matches GET api/Users/5
public IHttpActionResult GetUser(int id) {
//...
}
[HttpPut]
[Route("{id:int}")] //Matches PUT api/Users/5
public IHttpActionResult PutUser(int id,[FromBody]User user) {
//...
}
[HttpDelete]
[Route("{id:int}")] //Matches DELETE api/Users/5
public IHttpActionResult DeleteUser(int id) {
//...
}
}
Related
I'm trying to add custom route to the controller with [RoutePrefix("front")] that contains three methods:
[HttpGet]
[ResponseType(typeof(SparePartsFrontDTO))]
public async Task<IHttpActionResult> Get()
{
...
}
[HttpGet]
[ResponseType(typeof(IEnumerable<SparePartSearchDTO>))]
public async Task<IHttpActionResult> Get(string filter)
{
...
}
[HttpGet]
[Route("categories")]
[ResponseType(typeof(IEnumerable<DeviceCategoryDTO>))]
public async Task<IHttpActionResult> GetCategories()
{
...
}
But when I'm calling method by the route api/front/categories, instead of the custom route, default get method was called.
Here is WebApi config:
config.MapHttpAttributeRoutes();
config.MessageHandlers.Add(new PreflightRequestsHandler());
config.SuppressDefaultHostAuthentication();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
The route prefix needs to have the desired template plus the other actions need Route attribute as well.
[RoutePrefix("api/front")]
public class MyController : ApiController {
[HttpGet]
[Route("")] // matches GET api/front
[ResponseType(typeof(SparePartsFrontDTO))]
public async Task<IHttpActionResult> Get() {
//...
}
[HttpGet]
[Route("filter/{filter}")] // matches GET api/front/filter/anything-here
[ResponseType(typeof(IEnumerable<SparePartSearchDTO>))]
public async Task<IHttpActionResult> Get(string filter) {
//...
}
[HttpGet]
[Route("categories")] //matches GET api/front/categories
[ResponseType(typeof(IEnumerable<DeviceCategoryDTO>))]
public async Task<IHttpActionResult> GetCategories() {
//...
}
}
Reference Attribute Routing in ASP.NET Web API 2 : Route Prefixes
I have a ApiController with those GETs:
public class UsersController : ApiController
{
public IHttpActionResult GetUsers()
{
[...]
}
public IHttpActionResult GetUsers(guid ID)
{
[...]
}
[Route("api/Users/{CodeA}/{CodeB}")]
public IHttpActionResult GetUsers(string CodeA, string CodeB)
{
[...]
}
}
The routing in webapiconfig.cs is the standard one:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}/",
defaults: new { id = RouteParameter.Optional}
);
Trying to use swaggerUI I get a 500 error, and by fiddler I get:
Not supported by Swagger 2.0: Multiple operations with path 'api/utenti' and method 'GET'. See the config setting - \"ResolveConflictingActions\" for a potential workaround"
If I remove the last GET method swagger parses the api correctly. I've read from many sources that the problem is solved by specifying a different route, and I've tried to achieve this by adding the Route attribute to the last action.
Can someone please point me out in the right direction?
Thank you in advance.
The following code worked for me:
[Route("users")]
public class UsersController : ApiController
{
[Route("")]
public IHttpActionResult GetUsers()
{
string test = "";
return Ok(test);
}
[Route("{id}")]
public IHttpActionResult GetUsers(Guid id)
{
string test = "";
return Ok(test);
}
[Route("{CodeA}/{CodeB}")]
public IHttpActionResult GetUsers(string CodeA, string CodeB)
{
string test = "";
return Ok(test);
}
}
I'm trying to use RoutePrefix, but ASP.NET is partially ignoring it. Given the following controller (method bodies and class fields removed):
[RoutePrefix("api/users/sharepoint")]
public class SharePointController : ApiController
{
public SharePointController(ISharePointUserRepository repo, IAzureUserRepository otherRepo)
{
}
[HttpGet]
public ExternalUser Get(int id)
{
}
[HttpGet]
public ExternalUser Get(Guid guid)
{
}
[HttpGet]
public IEnumerable<ExternalUser> Get()
{
}
[HttpGet]
public ExternalUser Get(string username)
{
}
[HttpGet]
public IEnumerable<ExternalUser> GetByPersonalEmail(string email)
{
}
[HttpGet]
[Route("GetWithDifferences")]
public IEnumerable<ExternalUser> GetWithDifferences()
{
}
[HttpGet]
[Route("GetUnique")]
public IEnumerable<ExternalUser> GetUnique()
{
}
[HttpPost]
[Route("search")]
public IEnumerable<ExternalUser> Search([FromBody] ExternalUserSearchModel model)
{
}
I get the following API (via host/help):
SharePoint
GET api/users/sharepoint/GetWithDifferences
GET api/users/sharepoint/GetUnique
POST api/users/sharepoint/search
GET api/SharePoint/{id}
GET api/SharePoint?guid={guid}
GET api/SharePoint
GET api/SharePoint?username={username}
GET api/SharePoint?email={email}
Which, when tested, works as advertised.
As you can see the RoutePrefix is ignored when I don't specify a [Route(..)] for the exposed methods. However I want default actions on GET, so I don't want to specify any additional routing for these.
How can I enforce the RoutePrefix, or, alternatively, how can I maintain default GET and POST behavior with [Route(..)]?
RoutePrefixAttribute alone does not define any route, it will just add the chosen prefix to any route defined in the class decorated with such attribute.
You need to make a choice then: use only attribute routing (and define a route for each method) so you may benefit from RoutePrefix, or keeping the code as is, and define another convention routing which complies with your URIs.
Sample using attribute routing:
[HttpGet]
[Route("{id:int}"]
public ExternalUser Get(int id)
{ }
[HttpGet]
[Route(""]
public ExternalUser Get(Guid guid)
{ }
[HttpGet]
[Route("")]
public IEnumerable<ExternalUser> Get()
{ }
[HttpGet]
[Route("")]
public ExternalUser Get(string username)
{ }
And here it is another example for the second approach:
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "MyCustomApi",
routeTemplate: "api/users/sharepoint/{id}",
defaults: new { controller = "SharePoint", id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
I have five actions in my web api controller as follows.
http://localhost:1234/products - to map getallproduct action
http://localhost:1234/products/1 - to map getproductnyid action
http://localhost:1234/products - saveproduct action (post)
http://localhost:1234/products/1/getcolor - getproductcolorbyid action
http://localhost:1234/products/1/getcost - getproductcostbyid action
I need only one custom routing url for this.
I have tried following routing but it appends action name in url(http://localhost:24007/product/GetProductByID/Id) which i don't want.
config.Routes.MapHttpRoute(
name: "ProductRoute",
routeTemplate: "product/{action}/{productId}",
defaults: new { controller = "Product", productId= RouteParameter.Optional }
);
You have to use Attribute Routing if you want this kind of flexibility:
[RoutePrefix("products")]
public class ProductsController : ApiController {
[HttpGet]
[Route("")]
public IHttpActionResult GetAllProduct()
{
//...
}
[HttpGet]
[Route("{productId}")]
public IHttpActionResult GetProductById(int id)
{
//...
}
[HttpPost]
[Route("")]
public IHttpActionResult SaveProduct(Product product)
{
//...
}
[HttpGet]
[Route("{productId}/getcolor")]
public IHttpActionResult GetProductColorById(int id)
{
//...
}
[HttpGet]
[Route("{productId}/getcost")]
public IHttpActionResult GetProductCostById(int id)
{
//...
}
}
And remember to register them in your HttpConfiguration object:
config.MapHttpAttributeRoutes();
As an aside: if you are designing a RESTful API (which it seems to me) I strongly suggest you to avoid using RPC-like actions in your URIs (e.g. never use URI segments like getcolor, getcost), but use names that conforms with REST constraints:
http://localhost:1234/products/1/color
http://localhost:1234/products/1/cost
This can be achieved by changing your RouteAttributes:
[Route("{productId}/color")]
//...
[Route("{productId}/cost")]
All the examples of WebAPI I have seen show how to have a basic CRUD setup with a controller. For example the controller below shows a controller for campaigns in my application:
public IEnumerable<CampaignModel> Get()
{
return _campaignService.GetAll();
}
public CampaignModel Get(int id)
{
return _campaignService.GetByID(id);
}
public void Post(CampaignModel campaign)
{
_campaignService.Create(campaign);
}
public void Put(CampaignModel campaign)
{
_campaignService.Update(campaign);
}
public void Delete(int id)
{
_campaignService.Delete(id);
}
This is all well and good, and I end up with a few nice endpoints in the following format:
GET campaigns/
GET campaigns/{id}
POST campaigns/{campaign}
PUT campaigns/{campaign}
DELETE campaigns/{id}
However, I now want to extend the api further and add the following endpoints:
POST campaigns/send
POST campaigns/schedule/{date}
Is it possible to have these as part of the same controller as the CRUD actions above? Or do I have to add other controllers to and then set up a route for each of these endpoints?
Check out "Routing by Action Name" over here
routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
public class CampaignsController : ApiController
{
[HttpPost]
public void send();
[HttpPost]
public void schedule(DateDto date);
}
will map to “api/campaigns/send” and “api/campaigns/schedule”