Routing in Asp.net Mvc 4 and Web Api - asp.net-mvc

Can I use the following two route rule together ?
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional } );
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Say by controller is = FruitApiController:ApiController and I wish to have the following
List<Fruit> Get() = api/FruitApi/
List<Fruit> GetSeasonalFruits() = api/FruitApi/GetSeasonalFruit
Fruit GetFruits(string id) = api/FruitApi/15
Fruit GetFruitsByName(string name) = api/FruitApi/GetFruitsByName/apple
Please help me on this. Thanks

You could have a couple of routes:
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" }
);
}
}

config.Routes.MapHttpRoute(
name: "ApiByName",
routeTemplate: "api/{controller}/{action}/{name}",
defaults: null,
constraints: new { name = #"^[a-z]+$" }
);

Related

No HTTP resource was found that matches the request URI :(

This is my webApiConfig
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });
config.Routes.MapHttpRoute(
name: "ApiByActionWithid",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { action = "" },
constraints: new { id = #"^[0-9]+$" });
config.Routes.MapHttpRoute(
name: "ApiByAction",
routeTemplate: "api/{controller}/{action}",
defaults: new { action = "" });
The changeOrderState action looks like this:
public ResponseDTO ChangeOrderState(Int32 changeOrderState, string state)
{
IRestaurantManager rm = Core.CoreManager.Instance.GetService<IRestaurantManager>();
return rm.ChangeOrderState(changeOrderState, state);
}
And I call a method like this:
https://localhost:44302/api/Restaurant?changeOrderState=1&state=Rechazado
But it doesn't work, but in others calls that the same way do work
i solved this, the answer is add the next in te method [HttpGet] and my action looks like this:
[HttpGet]
public ResponseDTO ChangeOrderState(int changeOrderState, string state="")
{
IRestaurantManager rm = Core.CoreManager.Instance.GetService<IRestaurantManager>();
return rm.ChangeOrderState(changeOrderState, state);
}

API Controller in /Controllers instead of /api

I have various projects in a solution (ASP.Net MVC, domain, database, entities and Telerik Report Server WebApi).
The ASP.Net MVC is hosted at hostname/ and the Report Server at hostname/ReportServer/
All works fine except the Report Server.
It places the ReportsController API at
/ReportServer/Controllers/ReportsController
instead of
/ReportServer/api/ReportsController
I have created a simple test Solution with just a ASP.Net MVC and ReportServer, in theory with the same settings. This works, but I can't see what is causing this. All the routings, base class etc are the same.
Any ideas?
Instead of calling the default Telerik Reporting Route Registration function (ReportsControllerConfiguration.RegisterRoutes), you can write your own route registration procedure and call it from WebApiConfig.Register (before the DefaultApi route). For example:
private static void RegisterReportingRoutes(HttpConfiguration config)
{
config.Routes.MapHttpRoute(name: "Clients",
routeTemplate: "ReportServer/Controllers/{controller}/clients/{clientID}",
defaults: new { controller = "MyCustomReports", action = "Clients", clientID = RouteParameter.Optional });
config.Routes.MapHttpRoute(
name: "Instances",
routeTemplate: "ReportServer/Controllers/{controller}/clients/{clientID}/instances/{instanceID}",
defaults: new { controller = "MyCustomReports", action = "Instances", instanceID = RouteParameter.Optional });
config.Routes.MapHttpRoute(
name: "DocumentResources",
routeTemplate: "ReportServer/Controllers/{controller}/clients/{clientID}/instances/{instanceID}/documents/{documentID}/resources/{resourceID}",
defaults: new { controller = "MyCustomReports", action = "DocumentResources" });
config.Routes.MapHttpRoute(
name: "DocumentActions",
routeTemplate: "ReportServer/Controllers/{controller}/clients/{clientID}/instances/{instanceID}/documents/{documentID}/actions/{actionID}",
defaults: new { controller = "MyCustomReports", action = "DocumentActions" });
config.Routes.MapHttpRoute(
name: "DocumentPages",
routeTemplate: "ReportServer/Controllers/{controller}/clients/{clientID}/instances/{instanceID}/documents/{documentID}/pages/{pageNumber}",
defaults: new { controller = "MyCustomReports", action = "DocumentPages" });
config.Routes.MapHttpRoute(
name: "DocumentInfo",
routeTemplate: "ReportServer/Controllers/{controller}/clients/{clientID}/instances/{instanceID}/documents/{documentID}/info",
defaults: new { controller = "MyCustomReports", action = "DocumentInfo" });
config.Routes.MapHttpRoute(
name: "Documents",
routeTemplate: "ReportServer/Controllers/{controller}/clients/{clientID}/instances/{instanceID}/documents/{documentID}",
defaults: new { controller = "MyCustomReports", action = "Documents", documentID = RouteParameter.Optional });
config.Routes.MapHttpRoute(
name: "Parameters",
routeTemplate: "ReportServer/Controllers/{controller}/clients/{clientID}/parameters",
defaults: new { controller = "MyCustomReports", action = "Parameters" });
config.Routes.MapHttpRoute(
name: "Formats",
routeTemplate: "ReportServer/Controllers/{controller}/clients/{clientID}/formats",
defaults: new { controller = "MyCustomReports", action = "Formats" });
config.Routes.MapHttpRoute( name: "EditingAndLayout", routeTemplate: "ReportServer/Controllers/{controller}/formats", defaults: new { controller = "MyCustomReports", action = "Formats" });
}
If the /ReportServer path is its own web application, instead of a route under the "/" application, then you can omit the "ReportServer/" part of the registration function above
The folder structure/name has no significance in case of ASP.Net Web API routing.
You don't need to put it in 'controller' folder or 'API' folder. If you noticed the routing configuration in WebAPI.config file, the default route template will be like 'api/{controller}/{id}' and this make your api routes working.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}", // <--
defaults: new { id = RouteParameter.Optional }
);

ASP.NET MVC simplify routing

How can I simplify these routes?
config.Routes.MapHttpRoute(
name: "GetUsersRoute",
routeTemplate: "api/User",
defaults: new { controller = "User", action = "GetUsers" }
);
config.Routes.MapHttpRoute(
name: "GetUserRoute",
routeTemplate: "api/User/{id}",
defaults: new { controller = "User", action = "GetUser", id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "GetEmailAddressRoute",
routeTemplate: "api/User/GetEmailAddress/{id}",
defaults: new { controller = "User", action="GetEmailAddress", id = RouteParameter.Optional }
);
/*** Default ***/
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
I needed to add the GetEmailAddressRoute because the controller didn't know which action to take for a path like api/User/5. However, since then former routes like api/User don't lead to the correct action.
Some action signatures from the controller:
public Object GetUsers() { };
public Object GetUser(int id) { };
public HttpResponseMessage GetEmailAddress (int id) { };
But now I'd need to add another route for the Post actions. Is there a simpler way?
Update: What helped me for posting new users was to remove the action from the first route. Now it is
config.Routes.MapHttpRoute(
name: "GetUsersRoute",
routeTemplate: "api/User",
defaults: new { controller = "User" }
);
You can take advantage of the new routing attribute feature coming with web api 2 :
[Route("api/User")]
public Object GetUsers() { };
[Route("api/User/{id}")]
public Object GetUser(int id) { };
[Route("api/User/GetEmailAddress/{id}")]
public HttpResponseMessage GetEmailAddress (int id) { };

MVC API Routing When Multiple Get Actions Are Present

There seems to be a thousand people asking the same question on stack overflow, but there doesn't seem to be a single solution to this problem. I am going to ask it again...
I have an API controller that has the following actions:
// GET api/Exploitation
public HttpResponseMessage Get() {
var items = _exploitationRepository.FindAll();
var mappedItems = Mapper.Map<IEnumerable<Exploitation>, IEnumerable<ExploitationView>>(items);
var response = Request.CreateResponse<IEnumerable<ExploitationView>>(HttpStatusCode.OK, mappedItems);
response.Headers.Location = new Uri(Url.Link("DefaultApi", new { }));
return response;
}
// GET api/Exploitation/5
[HttpGet, ActionName("Get")]
public HttpResponseMessage Get(int id) {
var item = _exploitationRepository.FindById(id);
var mappedItem = Mapper.Map<Exploitation, ExploitationView>(item);
var response = Request.CreateResponse<ExploitationView>(HttpStatusCode.OK, mappedItem);
response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = id }));
return response;
}
// GET api/Exploitation/GetBySongwriterId/5
[HttpGet, ActionName("GetBySongwriterId")]
public HttpResponseMessage GetBySongwriterId(int id) {
var item = _exploitationRepository.Find(e => e.Song.SongWriterSongs.Any(s => s.SongWriterId == id))
.OrderByDescending(e => e.ReleaseDate);
var mappedItem = Mapper.Map<IEnumerable<Exploitation>, IEnumerable<ExploitationView>>(item);
var response = Request.CreateResponse<IEnumerable<ExploitationView>>(HttpStatusCode.OK, mappedItem);
response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = id }));
return response;
}
// GET api/Exploitation/GetBySongwriterId/5
[HttpGet, ActionName("GetBySongId")]
public HttpResponseMessage GetBySongId(int id) {
var item = _exploitationRepository.Find(e => e.SongId == id)
.OrderByDescending(e => e.ReleaseDate);
var mappedItem = Mapper.Map<IEnumerable<Exploitation>, IEnumerable<ExploitationView>>(item);
var response = Request.CreateResponse<IEnumerable<ExploitationView>>(HttpStatusCode.OK, mappedItem);
response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = id }));
return response;
}
In my APIConfig I have defined the following routes:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional, action = RouteParameter.Optional },
constraints: new { id = #"\d+" }
);
I am finding that I can access the following actions no problem:
/api/exploitation
/api/exploitation/getbysongwriterid/1
/api/exploitation/getbysongid/1
When I try to access /api/exploitation/1 I get this exception
"Multiple actions were found that match the request: System.Net.Http.HttpResponseMessage Get(Int32) on type Songistry.API.ExploitationController System.Net.Http.HttpResponseMessage GetBySongwriterId(Int32)" exception.
Can anyone see what is wrong with my routes? Or wrong with something else?
I have found an elegant solution to the problem.
I modified my ApiRouteConfig to have the following routes:
config.Routes.MapHttpRoute(
name: "DefaultGetApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional, action = "Get" },
constraints: new { id = #"\d+", httpMethod = new HttpMethodConstraint(HttpMethod.Get) }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional },
constraints: new { id = #"\d+" }
);
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional, action = RouteParameter.Optional }
);
Now I can access:
/api/exploitation
/api/exploitation/1
/api/exploitation/getbysongid/1
/api/exploitation/getbysongwriterid/1
I did not need to modify my controller actions to work with this new routing config at all.
If you had multiple PUT or POST actions you could the create new routes that looked as follows:
config.Routes.MapHttpRoute(
name: "DefaultGetApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional, action = "Put" },
constraints: new { id = #"\d+", httpMethod = new HttpMethodConstraint(HttpMethod.Put) }
);
config.Routes.MapHttpRoute(
name: "DefaultGetApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional, action = "Delete" },
constraints: new { id = #"\d+", httpMethod = new HttpMethodConstraint(HttpMethod.Delete) }
);
I hope that this answer helps everyone as this seems to be a common issue that people are having.
The problem you have is that /api/exploitation/1 falls under:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
All of your GET methods satisfy that routing as well particularly because {id} is optional and their controller is the same.
So you have one HTTP GET request from the client and multiple methods that accept GET requests. It doesn't know which one to go to.
api/{controller}/{action}/{id}
//This works fine because you specified which action explicitly
I hope that answers your question.
Try the following in your route definition. Keep only the following route:
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional, action = "Get" },
constraints: new { id = #"\d+" }
);
Make the first Get method private, modify the second one so that id has a default value:
// GET api/Exploitation
private HttpResponseMessage Get() {
// implementation stays the same but now it's private
}
// GET api/Exploitation/5
[HttpGet, ActionName("Get")]
public HttpResponseMessage Get(int id = 0) {
if (id == 0) {
return Get();
}
// continue standard implementation
}
This way (I haven't tested it myself) I expect that:
api/Exploitation/ will map to api/Exploitation/Get(as default action) with id = 0 as default param
api/Exploitation/1 will map to api/Exploitation/Get/1 so it will call Get(1)
api/Exploitation/someOtherAction/345 will call the correct action method
This might work. A tighter route definition could actually be like this:
config.Routes.MapHttpRoute(
name: "ApiWithRequiredId",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: null /* make sure we have explicit action and id */,
constraints: new { id = #"\d+" }
);
config.Routes.MapHttpRoute(
name: "ApiWithOptionalId",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional, action = "Get" },
constraints: new { action = "Get" /* only allow Get method to work with an optional id */, id = #"\d+" }
);
But something along these lines... give it a try I hope it solves your problem.

How to make more MapHttpRoutes for MVC 4 Api

I have 2 API routes for my API atm, but I want to add more, and the way I am doing it it seems to overwrite each other, so in the code I pasted, only the CreateUser route works.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapHttpRoute(
name: "CreateUser",
routeTemplate: "api/{controller}/{cUser}",
defaults: new
{
controller = "User",
action = "CreateUser",
cUser = RouteParameter.Optional
});
routes.MapHttpRoute(
name: "AllGames",
routeTemplate: "api/{controller}/{playerId}",
defaults: new
{
controller = "Game",
action = "GetAllGames",
playerId = RouteParameter.Optional
});
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new
{
controller = "Home",
action = "Index",
id = UrlParameter.Optional
});
}
I believe the pattern api/{controller}/{cUser} in "CreateUser" route is matching with rest of the controller actions because of its more generic pattern. Use specific controller name in the routes as "User" (api/User/{cUser}) and "Game" (api/Game/{playerId}). The more specific routes should be at the top and more generic at the bottom.
routes.MapHttpRoute(
name: "CreateUser",
routeTemplate: "api/User/{cUser}",
defaults: new
{
controller = "User",
action = "CreateUser",
cUser = RouteParameter.Optional
}
);
routes.MapHttpRoute(
name: "AllGames",
routeTemplate: "api/Game/{playerId}",
defaults: new
{
controller = "Game",
action = "GetAllGames",
playerId = RouteParameter.Optional
}
);

Resources