I'm having a problem using Attribute Routing where the compiler is showing the error "Type 'ValuesController' already defines a member called 'Get' with the same parameter types".
I have checked that config.MapHttpAttributeRoutes() is in the webapiConfig file
// GET api/values/5
[Route("api/values/{id}")]
public string Get(int id)
{
return "value";
}
[Route("api/v2/values/{id}")]
public string Get(int id)
{
return "value";
}
From reading http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2 something like this should be possible.
Your code is invalid C#, even aside from Web API. You can't define the same function signature twice. If you rename the second Get to GetNew, or move it to a different controller, it should work.
The problem, as I see it, is that you have two methods with the same name and arguments, which isn't allowed in C# - if you really must have the same method names, you need to overload on the number of arguments, and/or their types:
public string Get(int id, int id2)
Or
public string Get(Guid id)
Related
I'm working on an endpoint which looks like this:
GET {{url}}/v1.0/GroupsInfo/StuffToGet/1/2/hea
where the 2 integers are certain ids and the text on the end is a search term.
I want the last parameter (the search text) to be optional. I have got that working using the following, but it does not appear as mandatory in Swagger:
[Route("[action]/{catId:int}/{dogId:int}/{search?}")]
public IActionResult StuffToGet(int catId, int dogId, string search)
{
// do stuff
}
Swagger is ignoring the question mark on the end of the Search parameter in the route constraint.
Is this a known issue, or do I need to right some custom code to get Swagger to recognise that optional flag?
your variant will be working, but if you have a problem with swagger try this
[Route("[action]/{catId:int}/{dogId:int}]
[Route("[action]/{catId:int}/{dogId:int}/{search}")]
public IActionResult StuffToGet(int catId, int dogId, string search)
{
// do stuff
}
or maybe this
[Route("StuffToGet/{catId:int}/{dogId:int}/{search}")]
public IActionResult StuffToGet(int catId, int dogId, string search)
{
// do stuff
}
[Route("StaffToGet/{catId:int}/{dogId:int}]
public IActionResult StuffToGet(int catId, int dogId)
{
return StuffToGet(catId, dogId,string.empty);
}
Create two actions with similar routes, but one without the search parameter. Both actions call the same business layer code.
I'm trying to generate a swagger specification with NSwag.MSbuild but whenever I do it throws me this message:
The method 'get' on path '/api/Account' is registered multiple times
Now the problem is that my methods are route-less as shown below with some examples of the controller
[HttpPost]
[HttpGet]
[AllowAnonymous]
public IActionResult ExternalRegister(string provider, string returnUrl = null)
[HttpGet]
public IActionResult AddLogin(string provider, string returnUrl)
[HttpGet]
[AllowAnonymous]
public ActionResult SignUpConfig()
I understand why it does this but what I don't understand is that doing the same thing in NSwag Studio works, the command I use is $(NSwagExe_Core22) webapi2swagger is there an option so that it generates successfully like NSwag Studio?
In a WebAPI if you have more than one HttpGet or HttpPost etc you should add Route Attribute to distinguish them.
Add HttpGet["{name}"]
Turns out you don't have to specify the routes if you don't want to it has something to do with the Default Url Template:
/DefaultUrlTemplate:"{controller}/{action}/{id?}"
adding {action} solved it for me
In my case, I had already added custom [Route("")] attributes to all the paths. The problem was I had two public helper methods in the controller which NSwag identified as GET methods. Simply making them private (which they should have been anyway) made the problem go away...
In my case I had
[HttpGet, ActionName("Stuff")]
public async Task<Stuff> GetStuff(long byId, string color)
{
/* Do things one way */
}
[HttpGet, ActionName("Stuff")]
public async Task<Stuff> GetStuff(string byName, string color)
{
/* Do things another way */
}
The problem was that there were two identically named routes that take in different parameters. This is an overload situation that ASP.NET seems to be perfectly fine with but apparently blows NSwag's mind.
Because this was in legacy code, renaming the methods was not an option for me so I created a single method with optional parameters like so:
[HttpGet, ActionName("Stuff")]
public async Task<Stuff> GetStuff(string color, long? byId = null, string byName = null )
{
if (byId != null)
{
/* Do things one way */
}
else
{
/* Do things another way */
}
}
What helped me in this situation was to set the Route Attribute like this:
[Route("SignUpConfig")] ,[Route("AdLogin")]
If your controller is decorated with
[Route("[controller]")]
then you need you specify separate names
HttpGet("get1") and HttpGet("get2")
Else it will pick if decoration contains action name it it like
Route("[controller]/[action]") or from default route {controller}/{action}/{id?}
Aloha :D
I would like to create a dynamic route binding.
What I mean by this, is basically replacing the Query String with a dynamic route.
Example:
Instead of this:
POST http://localhost:5000/api/documents?templatename=individualemploymentagreement
this:
POST http://localhost:5000/api/documents/individualemploymentagreement
Note: after "http://localhost:5000/api/documents/" I want to put anything I want, but this route will always be used and what comes after should be used like a variable. Obviously, this will lead to a non-existing API Route at the moment. But is there any way to deal with this?
Note 2: The reasons I want to use this are:
- According to RESTful services "rules", query strings should be used just for queries, In this case I'm not using a query, I'm calling a generic document service, which however, treats every document slightly different when needed. So query strings are not recommended in my case.
- This service will deal with hundreds of document types, so I can't really make a different path / api for each one of them. So this is not recommended as well.
My code (In which I'm using a query string for {templateName}:
namespace DocumentGenerator.Api.Controllers
{
[Route("api/{controller}")]
[ApiController]
public class DocumentsController : ControllerBase
{
//useless details
[HttpPost]
public async Task<IActionResult> Generate([FromQuery] string templateName, [FromBody] object properties)
{
// according to {templateName} do this or that...
// useless details
}
}
}
What I would want in code:
namespace DocumentGenerator.Api.Controllers
{
[Route("api/{controller}")]
[ApiController]
public class DocumentsController : ControllerBase
{
//useless details
[HttpPost("{templateName}"]
public async Task<IActionResult> Generate([FromBody] object properties)
{
// according to {templateName} do this or that...
// useless details
}
}
}
You can specify the parameter name as a route attribute value in HttpPost :
[HttpPost("{templateName}"]
public async Task<IActionResult> Generate(string templateName, [FromBody] object properties)
{
}
or even
[HttpPost("/api/documents/{templateName}"]
public async Task<IActionResult> Generate(string templateName, [FromBody] object properties)
{
}
In a controller, I have a method like this:
public class FooController : ApiController
{
[HttpPost]
public HttpResponseMessage ApplySomething(int id)
{
...
}
}
When I do a POST request like .../Foo/ApplySomething/ and passing id=1 as POST values, I get the error:
{
Message: "No HTTP resource was found that matches the request URI '.../Foo/ApplySomething/'."
MessageDetail: "No action was found on the controller 'Foo' that matches the name 'ApplySomething'."
}
But when I change the URL to have the ID (like .../Foo/ApplySomething/1) it works, but getting the value from the URL, not from POST values.
What I'm doing wrong?
By default, Web API uses the following rules to bind parameters:
If the parameter is a “simple” type, Web API tries to get the value from the URI. Simple types include the .NET primitive types (int, bool, double, and so forth), plus TimeSpan, DateTime, Guid, decimal, and string, plus any type with a type converter that can convert from a string. (More about type converters later.)
For complex types, Web API tries to read the value from the message body, using a media-type formatter.
Given those rules, if you want to bind the parameter from the POST body simply add a [FromBody] attribute in front of the type:
public HttpResponseMessage ApplySomething([FromBody] int id) { ... }
For more information please see the documentation.
Try this :
public class FooController : ApiController
{
[HttpGet]
public HttpResponseMessage ApplySomething(int? id=0)
{
...
return View();
}
[HttpPost]
public HttpResponseMessage ApplySomething(FormCollection Form,int? id=0)
{
...
return View();
}
}
Now Try this url .../Foo/ApplySomething?id=1
Hopefully it works...!
mention the httpget method with same action or
check you routeconfig file.
you can also refer following solutions:
solution 1: No HTTP resource was found that matches the request URI in Web API
solution 2: http://forums.asp.net/t/1916587.aspx?WebAPI+No+HTTP+resource+was+found+that+matches+the+request+URI
solution 3: http://our.umbraco.org/forum/developers/extending-umbraco/46086-UmbracoApiController-No-HTTP-resource-was-found-that-matches-the-request-URI
I'm using System.Web.Http.RouteAttribute and System.Web.Http.RoutePrefixAttribute to enable cleaner URLs for my Web API 2 application. For most of my requests, I can use routing (eg. Controller/param1/param2) or I can use query strings (eg. Controller?param1=bob¶m2=mary).
Unfortunately, with one of my Controllers (and only one), this fails. Here is my Controller:
[RoutePrefix("1/Names")]
public class NamesController : ApiController
{
[HttpGet]
[Route("{name}/{sport}/{drink}")]
public List<int> Get(string name, string sport, string drink)
{
// Code removed...
}
[HttpGet]
[Route("{name}/{drink}")]
public List<int> Get(string name, string drink)
{
// Code removed...
}
}
When I make a request to either using routing, both work fine. However, if I use a query string, it fails, telling me that that path does not exist.
I have tried adding the following to my WebApiConfig.cs class' Register(HttpConfiguration config) function (before and after the Default route), but it did nothing:
config.Routes.MapHttpRoute(
name: "NameRoute",
routeTemplate: "{verId}/Names/{name}/{sport}/{drink}",
defaults: new { name = RouteParameter.Optional, sport = RouteParameter.Optional, drink = RouteParameter.Optional },
constraints: new { verId = #"\d+" });
So for clarity, I would like to be able to do both this:
localhost:12345/1/Names/Ted/rugby/coke
localhost:12345/1/Names/Ted/coke
and,
localhost:12345/1/Names?name=Ted&sport=rugby&drink=coke
localhost:12345/1/Names?name=Ted&drink=coke
but sadly the query string versions don't work! :(
Updated
I've removed the second Action altogether and now trying to use just a singular Action with optional parameters. I've changed my route attribute to [Route("{name}/{drink}/{sport?}")] as Tony suggested to make sport nullable, but this now prevents localhost:12345/1/Names/Ted/coke from being a valid route for some reason. Query strings are behaving the same way as before.
Update 2
I now have a singular action in my controller:
[RoutePrefix("1/Names")]
public class NamesController : ApiController
{
[HttpGet]
[Route("{name}/{drink}/{sport?}")]
public List<int> Get(string name, string drink, string sport = "")
{
// Code removed...
}
}
but still, using query strings does not find a suitable path, while using the routing method does.
I was facing the same issue of 'How to include search parameters as a query string?', while I was trying to build a web api for my current project. After googling, the following is working fine for me:
Api controller action:
[HttpGet, Route("search/{categoryid=categoryid}/{ordercode=ordercode}")]
public Task<IHttpActionResult> GetProducts(string categoryId, string orderCode)
{
}
The url I tried through postman:
http://localhost/PD/search?categoryid=all-products&ordercode=star-1932
http://localhost/PD is my hosted api
After much painstaking fiddling and Googling, I've come up with a 'fix'. I don't know if this is ideal/best practice/plain old wrong, but it solves my issue.
All I did was add [Route("")] in addition to the route attributes I was already using. This basically allows Web API 2 routing to allow query strings, as this is now a valid Route.
An example would now be:
[HttpGet]
[Route("")]
[Route("{name}/{drink}/{sport?}")]
public List<int> Get(string name, string drink, string sport = "")
{
// Code removed...
}
This makes both localhost:12345/1/Names/Ted/coke and localhost:12345/1/Names?name=Ted&drink=coke valid.
With the Attribute routing you need to specify default values so they would be optional.
[Route("{name}/{sport=Football}/{drink=Coke}")]
Assigning a value will allow it to be optional so you do not have to include it and it will pass the value to specify.
I have not tested the query string for this but it should work the same.
I just re-read the question and I see that you have 2 Get verbs with the same path, I believe this would cause conflict as routing would not know which one to utilize, perhaps using the optional params will help. You can also specify one can be null and do checking in the method as to how to proceed.
[Route("{name}/{sport?}/{drink?}")]
Then check the variables in the method to see if they are null and handle as needed.
Hope this helps, some? lol
If not perhaps this site will, it has more details about attribute routing.
http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2
Clip from that site:
Optional parameters and default values You can specify that a
parameter is optional by adding a question mark to the parameter, that
is:
[Route("countries/{name?}")]
public Country GetCountry(string name = "USA") { }
Currently, a default value must be specified on the optional parameter
for action selection to succeed, but we can investigate lifting that
restriction. (Please let us know if this is important.)
Default values can be specified in a similar way:
[Route("countries/{name=USA}")]
public Country GetCountry(string name) { }
The optional parameter '?' and the default values must appear after
inline constraints in the parameter definition.
Just a side note from my part as well. In order for queryString params to work, you need to provide a default value for your method parameters to make it optional. Just as you would also do when normally invoking a C# method.
[RoutePrefix("api/v1/profile")]
public class ProfileController : ApiController
{
...
[HttpGet]
[Route("{profileUid}")]
public IHttpActionResult GetProfile(string profileUid, long? someOtherId)
{
// ...
}
...
}
This allows me to call the endpoint like this:
/api/v1/profile/someUid
/api/v1/profile/someUid?someOtherId=123
Using Route("search/{categoryid=categoryid}/{ordercode=ordercode}") will enable you to use both Querystrings and inline route parameters as answered by mosharaf hossain. Writing this answer as this should be top answer and best way. Using Route("") will cause problems if you have multiple Gets/Puts/Posts/Deletes.
Here's a slight deviant of #bhargav kishore mummadireddy's answer, but an important deviation. His answer will default the querystring values to an actual non-empty value. This answer will default them to empty.
It allows you to call the controller through path routing, or using the querystring. Essentially, it sets the default value of the querystring to empty, meaning it will always be routed.
This was important to me, because I want to return 400 (Bad Request) if a querystring is not specified, rather than having ASP.NET return the "could not locate this method on this controller" error.
[RoutePrefix("api/AppUsageReporting")]
public class AppUsageReportingController : ApiController
{
[HttpGet]
// Specify default routing parameters if the parameters aren't specified
[Route("UsageAggregationDaily/{userId=}/{startDate=}/{endDate=}")]
public async Task<HttpResponseMessage> UsageAggregationDaily(string userId, DateTime? startDate, DateTime? endDate)
{
if (String.IsNullOrEmpty(userId))
{
return Request.CreateResponse(HttpStatusCode.BadRequest, $"{nameof(userId)} was not specified.");
}
if (!startDate.HasValue)
{
return Request.CreateResponse(HttpStatusCode.BadRequest, $"{nameof(startDate)} was not specified.");
}
if (!endDate.HasValue)
{
return Request.CreateResponse(HttpStatusCode.BadRequest, $"{nameof(endDate)} was not specified.");
}
}
}
I use FromUri attribute as solution
[Route("UsageAggregationDaily")]
public async Task<HttpResponseMessage> UsageAggregationDaily([FromUri] string userId = null, [FromUri] DateTime? startDate = null, [FromUri] DateTime? endDate = null)
Since you have [Route("{name}/{drink}/{sport?}")] as attribute routing, this code will never be hit.
config.Routes.MapHttpRoute(
name: "NameRoute",
routeTemplate: "{verId}/Names/{name}/{sport}/{drink}",
defaults: new { name = RouteParameter.Optional, sport = RouteParameter.Optional, drink = RouteParameter.Optional },
constraints: new { verId = #"\d+" });
So only the attribute route [Route("{name}/{drink}/{sport?}")] is going to be honored here. Since your request localhost:12345/1/Names?name=Ted&sport=rugby&drink=coke, doesn't have name, sport or drink in the URL it is not going to match this attribute route. We do not consider the query string parameters when matching the routes.
To solve this, you need to make all 3 optional in your attribute route. Then it will match the request.