I'm new to Web API. Reading up on restful made me think it was based upon verbs and as such I was hoping that the logic would be as well.
If I want to create an API for Delete and Get, which have the same signature I'm told off .
[HttpGet]
public HttpResponseMessage Index(int id)
{
return Request.CreateResponse(HttpStatusCode.OK, GetValue());
}
[HttpDelete]
public HttpResponseMessage Index(int id)
{
//logic
return Request.CreateResponse(HttpStatusCode.OK, true);
}
I was hoping by specifying the different verb Web Api 2 would tell. But even if I update the delete to (note the void return type)
[HttpDelete]
public void Index(int id)
{
//logic
}
I am still told off as the member called index with the same parameter types already exist.
According to https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/calling-a-web-api-from-a-net-client it shows
Action HTTP method Relative URI
Get a product by ID GET /api/products/id
Create a new product POST /api/products
Update a product PUT /api/products/id
Delete a product DELETE /api/products/id
The Get, Put and Delete have the same URL. Sadly, they don't display the server side code, only the client.
Are my only options to:
1. Overload the method (in this example, seems like it would be hacking as it's not needed to perform the required task)
2. Give the method a different name (eg `Delete` instead of `Index`)
Or is there another way?
You have a syntax issue. You can use attribute routes to maintain same paths but the methods must have different names and structures or you will get compilation error like you already experienced.
Using the example from your question
Action HTTP method Relative URI
Get a product by ID GET /api/products/id
Create a new product POST /api/products
Update a product PUT /api/products/id
Delete a product DELETE /api/products/id
The following would be a controller that matches the above
[RoutePrefix("api/products")]
public class ProductsController : ApiController {
[HttpGet]
[Route("{id:int}")] //Matches GET api/products/1
public IHttpActionResult Get(int id) {
return Ok(GetValueById(id));
}
[HttpPost]
[Route("")] //Matches POST api/products
public IHttpActionResult Post([FromBody]Product model) {
//...code removed for brevity
}
[HttpPut]
[Route("{id:int}")] //Matches PUT api/products/1
public IHttpActionResult Put(int id, [FromBody]Product model) {
//...code removed for brevity
}
[HttpDelete]
[Route("{id:int}")] //Matches DELETE api/products/1
public IHttpActionResult Post(int id) {
//...code removed for brevity
}
}
You can use Route attribute on api methods, check below:
[HttpGet]
[Route("same")]
public IHttpActionResult get(int id)
{
return Ok();
}
[HttpDelete]
[Route("same")]
public IHttpActionResult delete(int id)
{
return Ok();
}
And set http method to get for get request and delete for delete request, similar for post/put.
Related
I'm new to ASP.NET MVC, below is my api controller:
// API controller named Movie
[HttpGet]
public JsonResult Get()
{
....
}
[HttpGet]
public JsonResult Get([FromQuery]int id)
{
....
}
[HttpGet]
public JsonResult Get([FromQuery]string title, [FromQuery]string category)
{
....
}
then when I start the appication and routed to localhost/api/movie/123, it threw an exception which is Multiple actions were found that match the request
but only the first action method match, since there is only one parameter?
You have a route conflict because all those actions map to the same route and the route table does not know which to choose.
Routes need to be unique per action to avoid route conflicts.
In order for localhost/api/movie/123 to match Get(int id) action the route template would need to look like
//GET api/movie/123
[HttpGet("{id:int}")]
public JsonResult Get(int id) {
//....
}
Note the removal of the [FromQuery] and also the use of a route constraint {id:int} on the id route parameter
The second action should now not conflict with the first
//GET api/movie?title=someTitle&category=someCategory
[HttpGet]
public JsonResult Get([FromQuery]string title, [FromQuery]string category) {
//....
}
Reference Routing to controller actions in ASP.NET Core
Reference Routing in ASP.NET Core
How to generate some URL like http://mysite/some-id using below method?
Note: I do not want to use controller name and action name in url. because the main site used this structure and my boss does not want to change it.
public class StoryController : Controller
{
public ActionResult Index(string id)
{
if(id =="some-id"){
}
return View();
}
}
I don't know what version of MVC you are using, but if you are on the newest version or .NET Core, if you used routing attributes, you'd achieve this by:
[Route("")]
public class StoryController : Controller
{
[Route("{id}")]
public ActionResult Index(string id)
{
if(id =="some-id"){
}
return View();
}
}
Having a single parameter that is a string on your index-like route will definitely pose problems later with the routing engine though, when you try to add more controllers and views
I am new at asp.core , so I try to make valid route to {id}/visits
My code:
[Produces("application/json")]
[Route("/Users")]
public class UserController
{
[HttpGet]
[Route("{id}/visits")]
public async Task<IActionResult> GetUser([FromRoute] long id)
{
throw new NotImplementedException()
}
}
But, at route {id} generated method the same:
// GET: /Users/5
[HttpGet("{id}")]
public async Task<IActionResult> GetUser([FromRoute] long id)
{
return Ok(user);
}
How to make route /Users/5/visits nethod?
What parameters at GetUser should I add?
Name the methods differently and use constraints to avoid route conflicts:
[Produces("application/json")]
[RoutePrefix("Users")] // different attribute here and not starting /slash
public class UserController
{
// Gets a specific user
[HttpGet]
[Route("{id:long}")] // Matches GET Users/5
public async Task<IActionResult> GetUser([FromRoute] long id)
{
// do what needs to be done
}
// Gets all visits from a specific user
[HttpGet]
[Route("{id:long}/visits")] // Matches GET Users/5/visits
public async Task<IActionResult> GetUserVisits([FromRoute] long id) // method name different
{
// do what needs to be done
}
}
I am doing a Web API 2 application and I have controller named NCT_ProcessSettings and already I have two GET methods as below.
1. public IEnumerable<Process_Settings> Get()
2. public HttpResponseMessage Get(int id)
Now I want to have third one as below (Same as first one but inside I will write different logic).
3. public IEnumerable<Process_Settings> Get() //Compiler will confuse which to pick?
I tried as below.
[HttpGet]
[Route("GetGlobalSettings")]
public IEnumerable<NCT_Process_Settings> GetGlobalSettings()
{
return entityObject.NCT_Process_Settings.Where(c => c.project_id == 0).ToList();
}
Below is my angularcode to call api.
var url = '/api/NCT_ProcessSettings/GetGlobalSettings';
May I have some idea how to fix this? Any help would be appreciated?
Enable attribute routing in WebApiConfig.cs before convention-based routes.
config.MapHttpAttributeRoutes();
Next update controller to use routing attributes. (note the route prefix)
[RoutePrefix("api/NCT_ProcessSettings")]
public class NCT_ProcessSettingsController : ApiController {
//GET api/NCT_ProcessSettings
[HttpGet]
[Route("")]
public IEnumerable<Process_Settings> Get() { ... }
//GET api/NCT_ProcessSettings/5
[HttpGet]
[Route("{id:int}")]
public HttpResponseMessage Get(int id) { ... }
//GET api/NCT_ProcessSettings/GetGlobalSettings
[HttpGet]
[Route("GetGlobalSettings")]
public IEnumerable<NCT_Process_Settings> GetGlobalSettings() { ... }
}
Read up more documentation here Attribute Routing in ASP.NET Web API 2
Used Action Name attribute
[ActionName("Get")]
public IEnumerable<Process_Settings> Get1()//used any name here
{
}
I came across this issue by accident. I have this route
config.Routes.MapHttpRoute(
name: "RecycleCenters",
routeTemplate: "api/cars/{id}",
defaults: new { controller = "rc", id = RouteParameter.Optional }
);
and I have a controller like this
public class CarsController : ApiController
{
public IEnumerable<Car> Get() { ... }
public HttpResponseMessage Get(int id) { ... }
public HttpResponseMessage Post(Car car) { ... }
public HttpResponseMessage Put(int id, Car car) { ... }
public HttpResponseMessage Delete(int id) { ... }
}
Basically what's happening is that my routing is allowing to make a POST request to an endpoint like this /api/cars/id, when it shouldn't because to create a new car the request should be to this endpoint /api/cars
I've found an answer for my issue in this link but I want to know if the newer version of Web API has something already built in to prevent this scenario, if so then how to use it?
Thanks
If you use attribute routing you avoid this problem completely. Instead of defining your routes with config.Routes.MapHttpRoute you use config.MapHttpAttributeRoutes() and then place attributes on your controller methods:
public class CarsController : ApiController
{
[HttpGet]
[Route("api/cars")]
public IEnumerable<Car> Get() { ... }
[HttpGet]
[Route("api/cars/{id}")]
public HttpResponseMessage Get(int id) { ... }
[HttpPost]
[Route("api/cars")]
public HttpResponseMessage Post(Car car) { ... }
[HttpPut]
[Route("api/cars/{id}")]
public HttpResponseMessage Put(int id, Car car) { ... }
[HttpDelete]
[Route("api/cars/{id}")]
public HttpResponseMessage Delete(int id) { ... }
}
You can also add a RoutePrefix attribute at the controller level to avoid duplicating some of the information in the route on each controller. You can also avoid placing the Http verb attributes if you name your methods as you did, with the method name as the verb, but I prefer to use the attribute for each method for consistency.
This is a good article that talks about attribute routing versus convention routing. It talks specifically about MVC, but it applies to web api as well.
Hope that helps.