I want to have two different GET action to query the data by, name and id,
I have these routes:
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "ActionApiByName",
routeTemplate: "api/{controller}/{action}/{name}",
defaults: new { name = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
and these actions in the controller:
[HttpGet]
public CompanyModel CompanyId(Guid id)
{
//Do something
}
[HttpGet]
public CompanyModel CompanyName(string name)
{
//Do something
}
while a call like this: http://localhost:51119/api/companies/CompanyId/3cd97fbc-524e-47cd-836c-d709e94c5e1e
works and gets to the 'CompanyId' method,
a similar call to http://localhost:51119/api/companies/CompanyName/something
gets me to 404 not found
but this:
'http://localhost:51119/api/companies/CompanyName/?name=something
' works fine
Can anyone explain this behaviour and what am I doing wrong?
As mentioned, Web API controller can include multiple Get methods with different parameters and types. Let's add following action methods in StudentController to demonstrate how Web API handles multiple HTTP GET requests.
Usually a Web API controller has maximum of five actions - Get(), Get(id), Post(), Put(), and Delete(). However, if required you can have additional actions in the Web API controller.
The [FromBody] attribute can be applied on only one primitive parameter of an action method. It cannot be applied to multiple primitive parameters of the same action method.
The Web API route selector has no way to know if the string at the end of your URL is a GUID or not. Therefore, it is not about to select the correct route for the appropriate GET action.
In order to select the correct route, you need to add a route constraint for the GUID uri template.
public class GuidConstraint : IHttpRouteConstraint
{
public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values,
HttpRouteDirection routeDirection)
{
if (values.ContainsKey(parameterName))
{
string stringValue = values[parameterName] as string;
if (!string.IsNullOrEmpty(stringValue))
{
Guid guidValue;
return Guid.TryParse(stringValue, out guidValue) && (guidValue != Guid.Empty);
}
}
return false;
}
}
And then, add the constraint to the route that will handle the GUID.
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional },
constraints: new { id = new GuidConstraint() } // Added
);
Since this route is more specific than the general "string" route, it needs to be placed above the one that is going to resolve name.
This should appropriately route to the actions.
Hope this helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With