Hi I am developing webapi application and I have three GET methods in one controller. I am able to call 2 methods but third one I am not able to call.
Below are my methods I am able to call.
[HttpGet]
[Route("me")]
public HttpResponseMessage me()
{
return Request.CreateResponse(HttpStatusCode.OK, "Get me");
}
URL:http://localhost:22045/api/user/me
[HttpGet]
public HttpResponseMessage getUser(int id)
{
return Request.CreateResponse(HttpStatusCode.OK, "Get user");
}
URL: http://localhost:22045/api/user/1
I am not able to call below one.
[Route("user/{role}")]
public HttpResponseMessage Get(string role)
{
return Request.CreateResponse(HttpStatusCode.OK, "Get me on role");
}
I want to call it like
http://localhost:22045/api/user/OptionalRoleParameter
May I get some help here? Any help would be appreciated.
Optional route parameter as string
[Route("user/{role?}")]
public HttpResponseMessage Get(string role)
{
return Request.CreateResponse(HttpStatusCode.OK, "Get me on role");
}
You can't have string as an optional parameter because it's not supported as a nullable constraint in Web API, see this for more info, Route Constraints in Web API
Using attribute routes with route constraints should help differentiate the routes enough to avoid clashes
First ensure that attribute routing is enabled.
config.MapHttpAttributeRoutes();
Then make sure that the controller has the necessary attribute
[RoutePrefix("api/user")]
public class UsersController : ApiController {
//GET api/user/me
[HttpGet]
[Route("me")]
public HttpResponseMessage me() {
return Request.CreateResponse(HttpStatusCode.OK, "Get me");
}
//GET api/user/1
[HttpGet]
[Route("{id:int")] // NOTE the parameter constraint
public HttpResponseMessage getUser(int id) {
return Request.CreateResponse(HttpStatusCode.OK, "Get user");
}
//GET api/user
//GET api/user/OptionalRoleHere
[HttpGet]
[Route("{role?}")] //NOTE the question mark used to identify optional parameter
public HttpResponseMessage Get(string role = null) {
return Request.CreateResponse(HttpStatusCode.OK, "Get me on role");
}
}
Source: Attribute Routing in ASP.NET Web API 2 : Route Constraints
Related
I have Web API, I have written action method. but it's not correctly visible when I run the application. I cannot see SendPushNotification in the attached image.
Controller Code:
[RoutePrefix("api/OTP")]
public class OTPController : ApiController
{
public IHttpActionResult Get(int id)
{
return Ok("value");
}
[HttpGet]
public IHttpActionResult SendPushNotification(string userId, string factorId, string domain)
{
var response = _oTPRepository.SendPushNotification(userId, factorId, domain);
return Ok(response);
}
add a Route over your method, something like this:
[HttpGet]
[Route("SendPushNotification")]
public IHttpActionResult SendPushNotification(string userId, string factorId, string domain)
{
var response = _oTPRepository.SendPushNotification(userId, factorId, domain);
return Ok(response);
}
This will combine with the RoutePrefix from your controller and give you what you want.
You can call it whatever you want as well, whatever makes sense for your API.
in mentioned image second method is that for which you are actually looking for.
default routing for action methods is api/{controller_name}.
if you want to access that method as your given name you have set routing attribute above that action method.
like [Routing("api/OTP/SendPushNotification")]
This question already has answers here:
Routing with multiple Get methods in ASP.NET Web API
(10 answers)
Closed 4 years ago.
I have web api 2.0 project
When i try to implement several method with same parameter, i have this error : Several actions found
namespace WebApi.Controllers
{
public class EventController : ApiController
{
[HttpGet]
public HttpResponseMessage GetTags(string token, int messageId)
{
return ApiCall<List<EntityTag>>.CallApi(token, ServicesMessage.GetTags(messageId));
}
[HttpGet]
public HttpResponseMessage Get(string token, int eventId)
{
return ApiCall<EntityEvent>.CallApi(token, ServicesEvent.Get(eventId));
}
}
}
Any idea?
Thanks
The WebAPI cannot distinquish between these two methods because they have the same parameter types and same HttpVerb, and use implicit routing (no route attribute on them).
I'm a big fan of explicit routing for controllers and methods, instead of depending on naming conventions - so I'd try adding a route attribute to the methods:
public class EventController : ApiController
{
[HttpGet]
[Route("gettags")]
public HttpResponseMessage GetTags(string token, int messageId)
{
return ApiCall<List<EntityTag>>.CallApi(token, ServicesMessage.GetTags(messageId));
}
[HttpGet]
[Route("get")]
public HttpResponseMessage Get(string token, int eventId)
{
return ApiCall<EntityEvent>.CallApi(token, ServicesEvent.Get(eventId));
}
}
... and add a RoutePrefix attribute on the controller itself, like so:
[RoutePrefix("/api/Event")]
public class EventController : ApiController
{
}
Then you should be able to call the methods with a GET request to these URLs:
/api/event/get?token=xxxxx&eventId=xxxx
and
/api/event/gettags?token=xxxxx&messageId=xxxx
Write this line in your webapiconfig.cs
config.Routes.MapHttpRoute("DefaultApiPost", "Api/{controller}", new { action = "Get" }, new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) });
your Controller:
namespace WebApi.Controllers
{
public class EventController : ApiController
{
[HttpGet]
public HttpResponseMessage Tags(string token, int messageId)
{
return ApiCall<List<EntityTag>>.CallApi(token, ServicesMessage.GetTags(messageId));
}
[HttpGet]
public HttpResponseMessage Events(string token, int eventId)
{
return ApiCall<EntityEvent>.CallApi(token, ServicesEvent.Get(eventId));
}
}
}
Using this you can give the action names for every get request.
POST
when you want same things with post method just write below line in weapiconfig.cs
config.Routes.MapHttpRoute("DefaultApiPost", "Api/{controller}", new { action = "Post" }, new { httpMethod = new HttpMethodConstraint(HttpMethod.Post) });
And after that you can give a action name. no need to routing after using this.
When you want to call api than write follows:
api/Event/Tags //parameter as per your requirement
api/Event/Events //parameter as per your requirement
I am trying to do something simple and trivial - or at least I thought.
I am trying to write a base class that can be inherited by each micro-service project I spin up. The point of this base class is to test connectivity from HTTP all the way through to SQL. It is NOT enabled in PROD.
This is one of the (simpler) base classes:
public class DevTestControllerBase: ApiControllerBase
{
public DevTestControllerBase(IHostingEnvironment env, IConfiguration configuration = null, IMediator mediator = null) : base(env, configuration, mediator)
{
}
[HttpGet]
public IActionResult Get()
{
var response = Mediator.Send(new QueryGet());
return Ok(response.Result);
}
[HttpGet("{id}", Name = "Get")]
public IActionResult Get(Guid id)
{
var response = Mediator.Send(new QueryGetById(id));
return Ok(response.Result);
}
[HttpPost]
public async Task<IActionResult> Post([FromBody]DevTestModelBinding value)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
var response = await Mediator.Send(new CommandPost(value));
return Created("Get", new { id = response });
}
[HttpPut("{id}")]
public IActionResult Put(Guid id, [FromBody]DevTestModelBinding value)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
Mediator.Send(new CommandPut(id, value));
return Ok();
}
[HttpDelete("{id}")]
public IActionResult Delete(Guid id)
{
Mediator.Send(new CommandDelete(id));
return Ok();
}
}
I was hoping to use it as:
[Produces("application/json")]
[Route("api/DevTest")]
public class DevTestController : DevTestControllerBase
{
public DevTestController(IHostingEnvironment env, IConfiguration configuration, IMediator mediator) : base(env, configuration, mediator) { }
}
Unfortunately, it produces this error instead:
AmbiguousActionException: Multiple actions matched. The following actions matched route data and had all constraints satisfied:
MyNamespace.Providers.WebApi.Features.DevTest.DevTestController.Get
(MyNamespace.Providers.WebApi)
MyNamespace.Infrastructure.Web.Controllers.DevTestControllerBase.Get
(MyNamespace.Infrastructure.Web)
And since I wanted to use Swagger, I am also getting this error when trying to hit the Swagger endpoint:
An unhandled exception has occurred while executing the request
System.NotSupportedException: Ambiguous HTTP method for action -
MyNamespace.Providers.WebApi.Features.DevTest.DevTestController.Get
(MyNamespace.Providers.WebApi). Actions require an explicit HttpMethod
binding for Swagger 2.0
Make your base controllers abstract. Otherwise, they participate in routing as possible controllers that can be routed to. Although I'm a little surprised, honestly, that the routing based on controller name convention still works with a class ending in ControllerBase instead of just Controller, it would appear that ASP.NET Core sees them both as named DevTest, and therefore ambiguous.
You could probably alternatively rename the base controller(s) to something like BaseDevTestController (i.e. with "Base" before "Controller"), which would then make the names DevTest and BaseDevTest, removing the abmiguity. However, it's still a better idea to just make it abstract, as it should be anyways. You wouldn't want someone to actually be able to navigate directly to your base controller(s).
I really can't see what I am doing wrong here. I am trying to call a asp.net core web api method that accepts an integer via HttpClient but it returns a 404 error.
HospitalController (Web API)
[HttpGet("{id}")]
[Route("GetById")]
public JsonResult Get(int id)
{
return Json(_hospitalService.Get(id));
}
HospitalController (MVC)
protected string url = "http://localhost:5001/api/Hospital";
[HttpGet]
public async Task<IActionResult> Edit(int id)
{
if (id.Equals(0))
return StatusCode(404);
var accessToken = await HttpContext.GetTokenAsync("access_token");
client.SetBearerToken(accessToken);
HttpResponseMessage responseMessage = await client.GetAsync(url + "/GetById/" + id); //returns 404 error here.
if (responseMessage.IsSuccessStatusCode)
{
var responseData = responseMessage.Content.ReadAsStringAsync().Result;
var hospital = JsonConvert.DeserializeObject<Hospital>(responseData);
var hospitalVM = Mapper.Map<HospitalViewModel>(hospital);
return View(hospitalVM);
}
return View("Error");
}
I have a POST method in the same controller in MVC that works. But this GET method returns a 404 and I can't seem to know why.
There are two route templates being used in the api according to
[HttpGet("{id}")] //Matches GET api/Hospital/5
[Route("GetById")] //Matches GET api/Hospital/GetById
neither of which match what is being called
http://localhost:5001/api/Hospital/GetById/5
Http{Verb} attribute is usually used on RestFul APIs.
When building a REST API, it's rare that you will want to use [Route(...)] on an action method. It's better to use the more specific Http*Verb*Attributes to be precise about what your API supports. Clients of REST APIs are expected to know what paths and HTTP verbs map to specific logical operations.
Reference Routing to Controller Actions
update the route template on the web api to map to the desired route
[Route("api/[controller]")]
public class HospitalController : Controller {
//...code removed for brevity
//Matches GET api/Hospital/GetById/5
[HttpGet("GetById/{id:int}")]
public IActionResult Get(int id) {
return Ok(_hospitalService.Get(id));
}
}
Also HttpClient is meant to be used asynchronously, so the MVC controller would also need to be refactored as mixing blocking calls .Result can cause deadlocks
protected string url = "http://localhost:5001/api/Hospital";
[HttpGet]
public async Task<IActionResult> Edit(int id) {
if (id.Equals(0))
return StatusCode(404);
var accessToken = await HttpContext.GetTokenAsync("access_token");
client.SetBearerToken(accessToken);
HttpResponseMessage responseMessage = await client.GetAsync(url + "/GetById/" + id);
if (responseMessage.IsSuccessStatusCode) {
var responseData = await responseMessage.Content.ReadAsStringAsync();
var hospital = JsonConvert.DeserializeObject<Hospital>(responseData);
var hospitalVM = Mapper.Map<HospitalViewModel>(hospital);
return View(hospitalVM);
}
return View("Error");
}
I have a asp.net web api, using attributes for routing on the controllers. There are no route attriutes on the action level. The route for accessing a resource is:
[Route("{id}"]
public MyApiController: ApiController
{
public HttpResponseMessage Get(Guid id)
{
// ...
}
}
My problem is that when I want to create a search controller, I'd like the URL to be
[Route("search")]
But this results in an error: Multiple controller types were found that match the URL. Is it possible to make sure the exact matching route is selected before the generic one?
Technically, the phrase search could be a valid ID for the first controller, but as {id} is a guid, this will never be the case, thus I'd like to select the controller with the exact matching route.
You can use Route constraints to do the job. For example you could constraint your ID route to accept only valid GUID's.
Here is an ID controller that accepts only GUID strings in the URL:
[System.Web.Http.Route("{id:guid}")]
public class MyApiController: ApiController
{
public HttpResponseMessage Get(Guid id)
{
return new HttpResponseMessage(HttpStatusCode.OK);
}
}
The Search controller would match to an url like "/search". Here is the Search controller:
[System.Web.Http.Route("search")]
public class SearchController : ApiController
{
public HttpResponseMessage Get()
{
return new HttpResponseMessage(HttpStatusCode.OK);
}
}
Constraints will prevent matching conflicts in the router.