How can I get the url from web api in my view?
Example (from the msdn-blog):
[RoutePrefix("reviews")]
public class ReviewsController : ApiController
{
// eg.: /reviews
[Route]
public IHttpActionResult Get() { ... }
// eg.: /reviews/5
[Route("{reviewId}")]
public IHttpActionResult Show(int reviewId) { ... }
// eg.: /reviews/5/edit
[Route("{reviewId}/edit")]
public IHttpActionResult Edit(int reviewId) { ... }
}
Now I want to construct "/reviews/edit" in my view, how can I do this?
I've tried creating a little extension method, but it requires me to give every route an actual "RouteName". Is there a method I can use (like in MVC) where I can just pass the controller and action?
#Url.Action("Edit", "Reviews)
The method I'm using now (with RouteName) also doesn't allow me to use integers as parameters (unless I pass a default value). If I do need to name all my routes, how can I create a route url, but pass my parameters in the "data"-portion of my request?
Current method:
public static string ResolveWebApiRoute(this UrlHelper urlHelper, string routeName, object routeValues = null)
{
var newRouteValues = new RouteValueDictionary(routeValues);
newRouteValues.Add("httproute", true);
return urlHelper.RouteUrl(routeName, newRouteValues);
}
EDIT
When I used methods like Url.RouteUrl(new { controller = ..., action = ...}), It redirects directly to that action (e.g. new { controller = "Reviews", action = "Show"} --> /reviews/show, whilest I want it to redirect to /reviews/...
Generating links to Web API routes always require a RouteName, so you should have something like below:
[Route("{reviewId}/edit", Name="EditView")]
public IHttpActionResult Edit(int reviewId) { ... }
You can then generate a link like /reviews/1/editto Web API.
Url.RouteUrl(routeName: "EditView", routeValues: new { httpRoute = true, reviewId = 1 });
or
Url.HttpRouteUrl(routeName: "EditView", routeValues: , reviewId = 1)
Note that route names need to be specified explicitly and they are no longer generated automatically like what #Karhgath is suggesting. This was a change made from RC to RTM version.
When using route attributes I was able to get the route of a WebApi2 controller from an MVC view using something like this:
Url.HttpRouteUrl("RouteName", new { })
In WebApi2 when using AttributeRouting, route names are named by default Controller.Action, but you could specify a RouteName also:
[RoutePrefix("reviews")]
public class ReviewsController : Controller
{
// The route name is defaulted to "Reviews.Index"
[Route]
public ActionResult Index() { ... }
// The route name is "ShowReviewById"
[Route("{reviewId}"), RouteName("ShowReviewById")]
public ActionResult Show(int reviewId) { ... }
// The route name is by default "Reviews.Edit"
[Route("{reviewId}/edit")]
public ActionResult Edit(int reviewId) { ... }
Then to call it in the view you only need to set the route name and send the parameters as an anonymous object:
// Outputs: /reviews/123
#Url.Action("ShowReviewById", new { reviewId = 123 })
// Outputs: /reviews/123/edit
#Url.Action("Reviews.Edit", new { reviewId = 123 })
Related
I would like to have 2 methods in my controller that have the same route but differ only in the HTTP method. Specifically, if my route looks like
routes.MapRoute(
name: "DataRoute",
url: "Sample/{user}/{id}/",
defaults: new { controller = "Sample", action = "--not sure--", user = "", id = "" }
);
and I have 2 methods in my controller as such:
[HttpGet]
public void ViewData(string user, string id)
[HttpPost]
public void SetData(string user, string id)
The desired behavior is to call ViewData() if I GET Sample/a/b and call SetData() if I POST to Sample/a/b, the same URL.
I know I can just create 2 separate routes, but for design reasons I want to have one route differentiated only by GET and POST. Is there a way to configure either the route or controller to do this without having to create a new route?
With attribute routing you should be able to set the same route with different methods.
[RoutePrefix("Sample")]
public class SampleController : Controller {
//eg GET Sample/a/b
[HttpGet]
[Route("{user}/{id}")]
public void ViewData(string user, string id) { ... }
//eg POST Sample/a/b
[HttpPost]
[Route("{user}/{id}")]
public void SetData(string user, string id) { ... }
}
Don't forget to enable attribute routing before convention-based routes
routes.MapMvcAttributeRoutes();
You should edit the SetData method to take some payload from the body of the POST.
public void SetData(string user, string id, MyCustomObject data) { ... }
I need to define template based routing to controller and then attribute based for actions in ASP.NET Core. Something like:
public class Foo : Controller
{
[HttpGet]
public object Get()
{
return new
{
ID = "A"
};
}
[HttpPost]
public object Create([FromBody]dynamic entity)
{
return new
{
ID = "B"
};
}
}
Route
app.UseMvc(routes =>
{
routes.MapRoute("Settings", "settings/api/foo",
new { controller = "Foo" }
);
});
And I expect this to work:
GET /settings/api/foo
POST /settings/api/foo
Unfortunately it is not a case. It looks like route attributes are ignored. What is the best way to achieve requirement?
The trick here is to route the URL to a specific controller and action. Then use action method overloading with an action method selector to switch between the GET and POST.
Change your route setup code to this:
app.UseMvc(routes =>
{
routes.MapRoute(
"Settings",
"settings/api/foo",
new {
controller = "Foo", // specific controller
action = "DoThing", // AND specific action
}
);
});
And change the controller to have both action methods (or however many you want - one for each HTTP verb) to have the same name, but using different action methods selectors:
public class FooController : Controller
{
[HttpGet] // different action method selector!
public object DoThing() // same name!
{
return new
{
ID = "A"
};
}
[HttpPost] // different action method selector!
public object DoThing([FromBody]dynamic entity) // same name!
{
return new
{
ID = "B"
};
}
}
This way MVC will route all the requests for that URL to an action called DoThing on controller Foo. Once it gets there it sees "oh my, oh my, there are two actions with the same name!" But then it sees the [HttpGet] and [HttpPost] action method selectors, and whichever one of them says it can handle the request will win.
I'm looking for a way to know what link was pressed when a controller was called. My link is here in my view page:
<li id="tabFiles">Files</li>
and my Files controller is here:
public ActionResult Index(string submit)
{
string s = submit;
return View();
}
I tried to pass in a string hoping it would be the id of the clicked link, but this only returns a null.
You should pass the id as query string:
<li id="tabFiles">Files</li>
public ActionResult Index(string id)
{
string s = id;
return View();
}
Or you may want to pass that in this form:
<li id="tabFiles">Files</li>
The way you send parameters to the controller depends on how you have configured routing.
You can create custom action filter and set this on controller like this:
...
[CustomActionFilter]
public class FilesController : Controller
{
...
}
and on this custom filter you can get action name like this:
public class
CustomActionFilter : ActionFilterAttribute, IActionFilter
{
void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
{
// TODO: Add your acction filter's tasks here
// Log Action Filter Call
MusicStoreEntities storeDB = new MusicStoreEntities();
ActionLog log = new ActionLog()
{
Controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName,
Action = filterContext.ActionDescriptor.ActionName + " (Logged By: Custom
Action Filter)",
IP = filterContext.HttpContext.Request.UserHostAddress,
DateTime = filterContext.HttpContext.Timestamp
};
storeDB.ActionLogs.Add(log);
storeDB.SaveChanges();
this.OnActionExecuting(filterContext);
}
}
More details you can find here on Microsoft site or on this question.
I have a controller called HotelsController to insert and edit hotels.
It has the following setup (method implementation removed for simplicity):
[RoutePrefix("{member_id:int}/hotels")]
public class HotelsController : ApplicationController
{
[Route("delete/{id:int}", Name = NamedRoutes.HotelDelete)]
public ActionResult Delete(int id)
{
}
[Route("new", Name = NamedRoutes.HotelNew)]
public ActionResult New()
{
}
[HttpPost]
[ValidateInput(false)]
public ActionResult New(HotelDataEntry hotel)
{
}
[Route("edit/{id:int}", Name = NamedRoutes.HotelEdit)]
public ActionResult Edit(int id)
{
}
[HttpPost]
[ValidateInput(false)]
public ActionResult Edit(HotelDataEntry hotel)
{
}
}
As you can see the following routes are using attribute routing:
Delete
New (without parameters)
Edit (without parameters)
The following routes use no attribute routing:
New (with parameters)
Edit (with parameters)
The routing is setup in Global.asax.cs as follows:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("{resource}.aspx/{*pathInfo}");
routes.IgnoreRoute("{resource}.ashx/{*pathInfo}");
routes.IgnoreRoute("{resource}.asmx/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
Routen.Standard.ToString(),
"{member_id}/{controller}/{action}/{id}",
new { action = "browse", id = UrlParameter.Optional },
new { id = AllowedIdsRegExOptional }
);
}
Problem: Attribute routing works. I can call the Edit action with http://localhost:54868/301011/hotels/edit but the form on that page should post to the same uri and call the action that uses no attribute routing. But instead the action using the attribute based routing is called again. Why?
The form is supplied with method="post". Do you have any idea why the convention based route is not used? Thank you for your help.
Edit: I tried to add [HttpGet] in front of the attribute-routed New and Edit actions. The result is that on posting the form ASP.NET shows an error that the route is invalid. So for some reasons, the convention based routing is not working on the controller.
It seems that you cannot use both (attribute-based and convention-based) routing techniques in the same controller.
So what I did to resolve the issue is to add attribute-based routes to the two "unreachable" action methods. The route of these methods is the same as the route of the actions with the same name, but the name of the route is different (since route-names must be unique).
[RoutePrefix("{member_id:int}/hotels")]
public class HotelsController : ApplicationController
{
[Route("delete/{id:int}", Name = NamedRoutes.HotelDelete)]
public ActionResult Delete(int id)
{
}
[Route("new", Name = NamedRoutes.HotelNew)]
public ActionResult New()
{
}
[HttpPost]
[ValidateInput(false)]
[Route("new", Name = NamedRoutes.HotelNewPost)]
public ActionResult New(HotelDataEntry hotel)
{
}
[Route("edit/{id:int}", Name = NamedRoutes.HotelEdit)]
public ActionResult Edit(int id)
{
}
[HttpPost]
[ValidateInput(false)]
[Route("edit/{id:int}", Name = NamedRoutes.HotelEditPost)]
public ActionResult Edit(HotelDataEntry hotel)
{
}
}
So I am a little confused as to how to handle some MVC Routing
I have an AdminController
public class AdminController : Controller
{
//
// GET: /Admin/
public ActionResult Index()
{
return View();
}
public ActionResult Users()
{
return View();
}
public ActionResult Books()
{
return View();
}
}
Which works fine. So I can go to /Admin/Books
This is the admin menu for managing books. Now in there I'd like to be able to route like
/Admin/Books/ViewBook/10
or
/Admin/Books/Add
Something like that. I can't seem to grasp how to route these things that way.
I made a controller
AdminBookController
public class AdminBooksController : Controller
{
//
// GET: /AdminBooks/
public ActionResult List()
{
return View();
}
public ActionResult Add()
{
return View();
}
[HttpGet]
public ViewResult BookDetails(Guid guid)
{
return View();
}
[HttpPost]
public ViewResult BookDetails(ModifyBook Book)
{
if (ModelState.IsValid)
return View("Book successfully Edited!");
else
return View();
}
}
}
but I don't want it to be /AdminBooks I feel like /Admin/Books/Action/Param is much nicer.
Thanks in Advance!
If you want those urls to map to your AdminBooks controller, you'll need to map the following routes (in this order):
// maps /Admin/Books/ViewBook/{id} to AdminBooksController.BookDetails(id)
routes.MapRoute(
"AdminBooks_ViewBook", // Route name
"Admin/Books/ViewBook/{id}", // URL with parameters
new { controller = "AdminBooks", action = "BookDetails", id = UrlParameter.Optional } // Parameter defaults
);
// maps /Admin/Books/{action}/{id} to AdminBooksController.{Action}(id)
routes.MapRoute(
"AdminBooks_Default", // Route name
"Admin/Books/{action}/{id}", // URL with parameters
new { controller = "AdminBooks", action = "List", id = UrlParameter.Optional } // Parameter defaults
);
Note: be sure to put these mappings before the default MVC route.
Consider creating an Admin Area and adding a BookController to that Area. See the following link for a walkthrough:
http://msdn.microsoft.com/en-us/library/ee671793.aspx
You can add a new route in your Global.asax file.
See this question:
Use MVC routing to alias a controller