ASP.NET5 MVC6 routing - asp.net-web-api

I have the following route defined in Startup.cs:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "api",
template: "api/{controller}/{action}/{id?}"
);
});
And the following controller:
public class BookmarksController : Controller
{
[HttpGet]
public string GetAll()
{
return "GetAll Action";
}
[HttpGet("{id}")]
public string Get(int id)
{
return "Get action";
}
}
Can someone explain please why I can invoke GetAll Action by api/bookmarks/getall, but can not invoke Get by api/bookmarks/get/3 ?

This is by design. If you have controllers/actions which are decorated with attribute routes, any request which matches the conventional routes (the ones defined in your Startup.cs) cannot find/reach those controllers/actions. That is the reason GetAll can be invoked by using a conventional route where as you should be able to reach the Get(int id) by doing the url like /10 (of course, you migt want to modify this template :-))

Oh! I think I understand.
To invoke a action method of the Web API it is not necessary to include method name in the URL.
GetAll() can be invoked via api/bookmarks/ instead if api/bookmarks/getall
and Get(int id) can be invoked via api/bookmarks/3 instead of api/bookmarks/get/3

Related

How to assign multiple MVC routes to one method and determine the used route?

I have the following code:
public IActionResult Up(int id)
{
return Move(id, "up");
}
private IActionResult Move(int id, string action)
{
// Do something based on the action
switch (action)
{
case "up":
break;
case "down":
break;
}
}
This works fine and is easy to maintain. But now I've thought that I could refactor the code by combining the methods:
[Route("item/up/{id}")]
[Route("item/down/{id}")]
public IActionResult Move([FromRoute]string action, [FromRoute]int id)
{
}
The link /item/up/155 will hit this method while the link /item/up2/155 returns NotFound, as expected.
The problem now is, that when I check the value of action, then it does not contain the value up, but the value Move.
This seems odd as the controller name is not transformed into ItemController. So one could argue that the value should be up. But assuming this is the design and won't change, then how can I find the actual value?
I can parse Request.Path.Value (="/item/up/155"), but I'd prefer not to as I would like to keep things simple.
So I am looking for an easy way to assign multiple routes to one method and then determine what the value of the action parameter in the used route path was.
Instead of hard coding the direction as 'up' and 'down' in the route declaration, you can treat that as a parameter. Since you used the keyword 'action' as a parameter, during the parameter binding the actual action method selected by the framework will be assigned to it.
You can try the following code -
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
[Route("item/{direction}/{id}")]
public string Move([FromRoute]string controller, [FromRoute]string action, string direction, string id)
{
return string.Format("Controller={0} Action={1} Direction={2} Id={3}", controller, action, direction, id);
}
}

Is it possible to report back the route name used when calling a Web API?

Is it possible to report back the route name used when calling a Web API?
I want to do some diagnostics in a "foo" project to see what route is called based on how/what I call. Is there a way for the action/api to see what route is used to reach it? Some sort of "route reflection"?
You can use Request.GetRouteData() to get the route details for an action.
public class TestController : ApiController
{
[HttpGet]
public HttpResponseMessage Index()
{
var route = Request.GetRouteData();
return Request.CreateResponse(HttpStatusCode.OK);
}
}

Prepend a custom parameter before {controller} in ASP.NET WebApi route

I want to create a route template for Owin WebApi like this:
cfg.Routes.MapHttpRoute(
"API Default", "{myparam}/{controller}/{action}",
new { id = RouteParameter.Optional });
Because I have controllers defined that need a parameter before the controller selection.
I have tried to remove the parameter and set it into RoutePrefixAttribute on controller but it doesn't work.
{controller} must be the first dynamic parameter of the route?
I would use some form of attribute based routing to go to different controllers based on {myparam}.
First controller:
[Route("param1/customer/{id}")]
public IEnumerable<Order> GetOrdersByCustomer(int id) { ... }
Second controller:
[Route("param2/customer/{id}")]
public IEnumerable<Order> GetOrdersByCustomer(int id) { ... }
More information can be found here: Attribute Based WebAPI Routing
Delete RoutePrefix attribute and set the first parameter dynamic in your action Route attribute like the example below:
[HttpGet, Route("{myparam}/books/{bookId:int:min(1)}")]
public HttpResponseMessage Get(string myparam, int bookId)
{
...
}

Recommended API design with ASP.NET MVC3

I'm working with ASP.NET MVC 3. I'm kind of new to it. I think I'm starting to get the hang of it. But there is something that I'm trying to do, that I think makes sense, but maybe I'm wrong.
I'm trying to create an API around Order objects in my database. In order to get all of the orders in the system, I was going to expose an API that looks like the following:
/orders/
In cases where I wanted to get a specific Order, I would simply append an ID. In other words, the URL would look like this:
/orders/12345
In an effort to accomplish this, I created the following controller:
public class OrdersController : Controller
{
// GET: /Orders/
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Index()
{
string result = "list of orders";
return Json(result, JsonRequestBehavior.AllowGet);
}
//
// GET: /Orders/{orderID}
public ActionResult Index(int id)
{
string result = "order:" + id;
return Json(result, JsonRequestBehavior.AllowGet);
}
}
In my AreaRegistration class, I have the following:
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"OrderList",
"{controller}/{action}",
new { action = "Index", controller="Orders" }
);
context.MapRoute(
"Order",
"{controller}/{action}/{id}",
new { action = "Index", controller = "Orders" }
);
}
When I attempted to access "/orders/", via the browser address bar, I get the JSON like I would expect. However, if I attempt to access "/orders/12345", I receive a 404. What am I missing?
Thank you
You need to also define proper routes in global.asax or use the default route which looks like {controller}/{action}/{id} where controller is defaulted to "Home", action is defaulted to "Index" and id is optional.
So /orders works because you have defined controller (orders), default action (Index) and missing id (which doesn't matter as it is optional)
But when you try /orders/12345 then you have defined controller (orders), action (12345) and missing id
So to make this work with only the default route the request should be /orders/index/12345
edit: for registering area routes you should use AreaRegistration class

ASP.Net Mvc 3 Url.Action method uses parameter values from previous request

When Urls are autogenerated using the Url.Action helper, if a page contains a line similar to
#Url.Action("Edit","Student")
is expected to generate a url like domain/student/edit and its working as expected.
But if the requested url contains some parameters, like domain/student/edit/210, the above code uses these parameters from the previous request and generates something similar even though I've not provided any such parameter to the Action method.
In short, if the requested url contains any parameters, any auto generated links of the page (served for that request) will include those parameters as well no matter if I specify them or not in the Url.Action method.
What's going wrong?
Use Darin's answer from this similar question.
#Url.Action("Edit","Student", new { ID = "" })
Weird, can't seem to reproduce the problem:
public class HomeController : Controller
{
public ActionResult Index(string id)
{
return View();
}
public ActionResult About(string id)
{
return View();
}
}
and inside Index.cshtml:
#Url.Action("About", "Home")
Now when I request /home/index/123 the url helper generates /home/about as expected. No ghost parameters. So how does your scenario differs?
UPDATE:
Now that you have clarified your scenario it seems that you have the following:
public class HomeController : Controller
{
public ActionResult Index(string id)
{
return View();
}
}
and inside Index.cshtml you are trying to use:
#Url.Action("Index", "Home")
If you request /home/index/123 this generates /home/index/123 instead of the expected /home/index (or simply / taken into account default values).
This behavior is by design. If you want to change it you will have to write your own helper which ignores the current route data. Here's how it might look:
#UrlHelper.GenerateUrl(
"Default",
"index",
"home",
null,
Url.RouteCollection,
// That's the important part and it is where we kill the current RouteData
new RequestContext(Html.ViewContext.HttpContext, new RouteData()),
false
)
This will generate the proper url you were expecting. Of course this is ugly. I would recommend you encapsulating it into a reusable helper.
Use ActionLink overload that uses parameters and supply null
You could register custom route for this action for example:
routes.MapRoute("Domain_EditStudentDefault",
"student/edit",
new {
controller = MVC.Student.Name,
action = MVC.Student.ActionNames.Edit,
ID = UrlParameter.Optional
},
new object(),
new[] { "MySolution.Web.Controllers" }
);
you then could use url.RouteUrl("Domain_EditStudentDefault") url RouteUrl helper override with only routeName parameter which generates url without parameters.

Resources