Web API route being ignored - c#

I have these two routes defined:
routes.MapRoute(
name: "GetVoucherTypesForPartner",
url: "api/Partner/{partnerId}/VoucherType",
defaults: new { controller = "Partner", action = "GetVoucherTypesForPartner"}
);
routes.MapRoute(
name: "Default",
url: "api/{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional}
);
In my PartnerProfile controller, I have 2 methods:
public Partner Get(string id)
{ }
public IEnumerable<string> GetVoucherTypesForPartner(string id)
{ }
If I hit the url ~/api/Partner/1234 then, as expected, the Get method is called.
However, if I hit the url ~/api/Partner/1234/VoucherType then the same Get method is called. I am expecting my GetVoucherTypesForPartner to be called instead.
I'm pretty sure something in my route setup is wrong...

You seem to have mapped standard MVC routes, not Web API routes. There's a big difference. The standard routes are used by controllers deriving from the Controller class, but if you are using the ASP.NET Web API and your controllers are deriving from the ApiController type then you should define HTTP routes.
You should do that in your ~/App_Start/WebApiConfig.cs and not inside your ~/App_Start/RouteConfig.cs.
So go ahead:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "GetVoucherTypesForPartner",
routeTemplate: "api/Partner/{partnerId}/VoucherType",
defaults: new { controller = "Partner", action = "GetVoucherTypesForPartner" }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
and then:
public class PartnerController : ApiController
{
public Partner Get(string id)
{
...
}
public IEnumerable<string> GetVoucherTypesForPartner(string partnerId)
{
...
}
}
Things to notice:
We have defined HTTP routes not standard MVC routes
The parameter that the GetVoucherTypesForPartner action takes must be called partnerId instead of id in order to respect your route definition and avoid any confusions

Related

Added a second controller to my WebAPI and it is not working

Added a second controller in my WebAPI project and it is nor working completely but the first controller is working as expected
The default URI works for the first controller to return all records:
http://localhost:59654/api/TidalBatch
The second controller does not work and returns the error in question:
http://localhost:59654/api/TidalBatchConsolidated
However, if I pass in {id} for it, it does work for when I use the id (example shown):
http://localhost:59654/api/TidalBatchConsolidated/BAM
Tried modifying the routing addresses
WebAPI config:
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "TidalBatchApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "TidalBatchConsolidatedApi",
routeTemplate: "api/TidalBatchConsolidated/{id}",
defaults: new { id = RouteParameter.Optional }
);
I have 2 controllers, TidalBatchController.cs and TidalBatchConsolidatedController.cs. Both inherit from ApiController class.
Here's an example of my second controller that is not working as expected:
public class TidalBatchConsolidatedController : ApiController
{
public TidalBatchConsolidated GetAll(string id)
{
using (BDW_ProcessingEntities_TidalBatch entities = new BDW_ProcessingEntities_TidalBatch())
{
return entities.TidalBatchConsolidateds.FirstOrDefault(e => e.CompanyAbbr == id);
}
}
}
When I navigate to the base controller in the address it should return the List results in JSON format based on which entity data model is being passed in.
First, the order you register routes is important where more generic routes need to be registered after more specific routes. Secondly you more specific route needs controller in order for it to match.
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "TidalBatchConsolidatedApi",
routeTemplate: "api/TidalBatchConsolidated/{id}",
defaults: new { controller ="TidalBatchConsolidated", id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "TidalBatchApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
The controller also needs to ensure that there is a matching action
For example
public class TidalBatchConsolidatedController: ApiController {
[HttpGet]
public IHttpActionResult Get() {
//...
}
[HttpGet]
public IHttpActionResult Get(string id) {
//...
}
}
Since you have attribute routing enabled with config.MapHttpAttributeRoutes();, you could forego convention based route and use attribute routing on the controller instead
[RoutePrefix("api/TidalBatchConsolidated")]
public class TidalBatchConsolidatedController: ApiController {
//GET api/TidalBatchConsolidated
[Route("")] //Default route
[HttpGet]
public IHttpActionResult GetAll() {
//...
}
//GET api/TidalBatchConsolidated/BAM
[Route("{id}")]
[HttpGet]
public IHttpActionResult Get(string id) {
//...
}
}
Reference Attribute Routing in ASP.NET Web API 2

Web API route not getting called

This is my web API config:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "ReaderTags",
routeTemplate: "Reader/{readerID}/Tags"
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
I want that when I call my URL /Reader/abc/Tags it should call my ReaderController's Tags action and pass abc as the string parameter.
But somehow it is using the DefaultAPI's route and trying to find abc as an action in ReaderController.
What am I missing?
The route mapping is missing defaults that would let the route table know what controller and action to invoke for routes matching the template
config.Routes.MapHttpRoute(
name: "ReaderTags",
routeTemplate: "Reader/{readerID}/Tags",
defaults: new { controller = "Reader", action = "Tags" }
);
The route template also assumes that the string parameter on the action shares the same name: i.e: readerID.
public IHttpActionResult Tags(string readerID) {
//...
}
And since config.MapHttpAttributeRoutes(); is also configured, then the same can be achieved via attribute routing instead of convention-based routing like this
//GET Reader/abc/Tags
[HttpGet]
[Route("Reader/{readerID}/Tags")]
public IHttpActionResult Tags(string readerID) {
//...
}

WebApi2 can not find route

[RoutePrefix("subscriptions")]
public class SubscriptionsController : ApiController
{
private SubscriptionsService _subscriptionsService;
public SubscriptionsController()
{
_subscriptionsService = new SubscriptionsService();
}
[Route("{email}")]
[HttpGet]
public IHttpActionResult Get(string email)
{
but when I try
http://localhost:51561/api/subscriptions/myEmail
No HTTP resource was found that matches the request URI
'http://localhost:51561/api/subscriptions/myEmail'.
any idea why?
I also set everything in WebApiCOnfig
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
You have tried a wrong URL.
Correct URL should be,
http://localhost:51561/subscriptions/myEmail
OR
If you need API prefix, then you need to change the route prefix of the controller as below,
[RoutePrefix("API/subscriptions")]
then this URL will work,
http://localhost:51561/api/subscriptions/myEmail
Note that , here you are using two routing, default routing and attribute routing. if you need to continue with attribute routing , you can comment out the default routing.
//config.Routes.MapHttpRoute(
// name: "DefaultApi",
// routeTemplate: "api/{controller}/{id}",
// defaults: new { id = RouteParameter.Optional }
I Suppose you are creating some webApi service ? Did you register this route in global.asax.cs ? Something like this :
httpConfiguration.Routes.MapHttpRoute(
name: "MyWebApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { controller = "ControllerName", action = "ActionName", id = RouteParameter.Optional },
constraints: null
);

Multiple GET and POST methods in Web API controller

I have been struggling with this problems for days. I have a controller that need to have multiple GET and POST methods. Somehow I managed to achieve multiple GET methods in the controller. At that time there was only one POST method. Everything was running fine until I introduced one more POST method. Whenever I use POST method from client side using ExtJS, only the first POST method gets called. Here are the methods in my controller:
[AllowAnonymous]
[ActionName("userlist")]
[HttpGet]
public List<MyApp.Entity.Models.usermaster> Get(bool isActive)
{
//My code
}
[AllowAnonymous]
[ActionName("alluserlist")]
[HttpGet]
public List<MyApp.Entity.Models.usermaster> Get()
{
//My code
}
[AllowAnonymous]
[ActionName("updateuser")]
[HttpPost]
public string UpdateUser(JObject userData)
{
//My code
}
[AllowAnonymous]
[ActionName("adduser")]
[HttpPost]
public string AddUser(JObject newUserData)
{
//My code
}
I also have two route config files. The first one has the following configuration:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Another file has the following configuration:
public static void Register(HttpConfiguration config)
{
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "ControllersWithAction",
routeTemplate: "api/{controller}/{action}");
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });
}
This is how the ajax call is made to the api:
Ext.Ajax.request({
url: 'localhost/myapp/api/users/adduser',
mode: 'POST',
params: {
userName: 'user',
password: 'password'
},
success: function (resp) {
var respObj = Ext.decode(resp.responseText);
if (respObj == 'added') {
Ext.Msg.alert('Success', 'User added.');
}
else {
Ext.Msg.alert('Error', respObj);
}
},
failure: function (resp) {
Ext.Msg.alert('Error', 'There was an error.');
}
});
Can anyone point out the mistake? Alternatively, any example with multiple GET and POST methods in side one controller with route config would be very helpful.
why don't you use PUT method for UpdateUser ?
[AllowAnonymous]
[HttpPut]
public string UpdateUser(JObject userData)
{
//My code
}
update:
you can use multiple POST but then you should either use ActionNames that works but is not restful or stop using Complex Types as parameters. because Web API ignores Complex Types when it tries to select most appropriate Action . check these :
Multiple actions for the same HttpVerb
http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-and-action-selection
You could try this
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "ApiById",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional },
constraints: new { id = #"^[0-9]+$" }
);
config.Routes.MapHttpRoute(
name: "ApiByName",
routeTemplate: "api/{controller}/{action}/{name}",
defaults: null,
constraints: new { name = #"^[a-z]+$" }
);
config.Routes.MapHttpRoute(
name: "ApiByAction",
routeTemplate: "api/{controller}/{action}",
defaults: new { action = "Get" }
);
}
}
you can use ActionName for your method as well
[AllowAnonymous]
[HttpPost]
[ActionName("userlist")]
public string UpdateUser(JObject userData)
{
//My code
}
[AllowAnonymous]
[HttpPost]
[ActionName("alluserlist")]
public string AddUser(JObject newUserData)
{
//My code
}
Also, you have to use ActionName in your URL for API, when you call your post method.
Make sure your route Config have {action} in WebApiConfig.
for custom binding in web API you can refer following link
https://blogs.msdn.microsoft.com/jmstall/2012/04/16/how-webapi-does-parameter-binding/

How to define the PUT method in routing only limit to the Put methods in controller without parameter?

Here is the routing configuration in WebApiConfig.cs:
config.Routes.MapHttpRoute(
name: "DefaultApiPut",
routeTemplate: "api/{controller}",
defaults: new { httpMethod = new HttpMethodConstraint(HttpMethod.Put) }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { httpMethod = new HttpMethodConstraint(HttpMethod.Get, HttpMethod.Post, HttpMethod.Delete) }
);
Here is my controller:
public class MyController : ApiController {
[HttpPut]
public void Put()
{
//blah
}
}
Somehow when the client sents the PUT request with the URL /api/myController/12345, it still maps to the Put method in MyController, I am expecting an error like resource not found.
How to force the Put method only accept the request without a parameter?
Thanks in advance!
This works to constrain http method on routes:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "LocationApiPOST",
routeTemplate: "api/{orgname}/{fleetname}/vehicle/location",
defaults: new { controller = "location" }
constraints: new { httpMethod = new HttpMethodConstraint(HttpMethod.Post) }
);
config.Routes.MapHttpRoute(
name: "LocationApiGET",
routeTemplate: "api/{orgname}/{fleetname}/{vehiclename}/location/{start}",
defaults: new { controller = "location", start = RouteParameter.Optional }
constraints: new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) }
);
...
}
You're putting your httpMethod constraint into defaults, but it should be in constraints.
defaults just says what the default values will be if the request doesn't include some or all of them as routing parameters (which in the case of the verb, is meaningless, since every HTTP request always has a verb as part of the protocol). constraints limit the combination of route values that will activate the route, which is what you're actually trying to do.
FYI, for this simple/standard routing, you don't need the [HttpPut] attribute in an API controller either. That's already handled by the HTTP routing which maps the verb to the controller method.

Categories