I have two controllers, one Web API controller and the other a MVC controller. These two controllers have the same name and the same action name, though the one in API controller is a post while the one in MVC is a get, as shown here.
This is the API controller:
[Route("api/[controller]")]
public class SameNameController : ControllerBase
{
[HttpPost]
public IEnumerable<*className*> SameNameAction([FromBody] *typeName*[] data)
{
// Detail implementation
}
}
And this is the MVC controller:
public class SameNameController: Controller
{
public ActionResult SameNameAction(int? page, int? pageSize)
{
//Detail implementation
}
}
I'm using X.PagedList to generate pagination. When I click any of pages, I receive an error
HTTP 405 - this page isn't working
Upon further inspection, I realized that the URL generated is the issue.
Here is the problematic part of the view.
#Html.PagedListPager((IPagedList)Model, page => Url.Action("SameNameAction", "SameNameController", new { page, pageSize= 5}))
Here is the routing definition in program.cs. As you can see, it's just basic stuff.
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
I expect that Url.Action would generate an url to the get action of the MVC controller. Instead, it generates an url to the post action of the Web API controller.
This is what I got:
2
This is what I expect:
2
I've never experienced Url.Action generating routing to an API controller. I'm not sure this is due to pagelist combined with url.action or url.action alone.
I can change the action name of the API controller, and the URL is generated as expected. However, this is not the issue. I would like to know why it map to the API controller and not the MVC controller to begin with.
I expect that Url.Action would generate an url to the get action of
the MVC controller. Instead, it generates an url to the post action of
the Web API controller.
Based on your scenario, I have checked your code between the line. As you know \[Route("")\] Attribute always has the higher precedence in routing therefore, this output/api/SameNameController?page=2&pageSize=5is obvious. However, using Url.RouteUrl HTML helper you can overcome your issue.
Solution:
#Html.PagedListPager((IPagedList)Model, page => Url.RouteUrl("default", new{httproute = "", controller="SameName",action="SameNameAction",page=1, pageSize= 5}))
Note: Please fit the code as per your environment.The exact code should be as folloing:
#Url.RouteUrl("default", new{httproute = "", controller="SameName",action="SameNameAction",page=1, pageSize= 5})
Output:
Hope it will guide you accordingly.
Related
I have the following ActionLink:
#Html.ActionLink("Upload", "Upload", "File")
yet when I hover over the link, the url displayed in the browser is http://localhost:44334/Upload. Where is the controller in this url? Strangely, clicking the url takes me to my File/Upload view, and yet stranger, the url displayed in the browser's address bar is https://localhost:44334/Upload.
The ActionLink is on page Home/Explorer, so AFAIK a controller name is normally necessary.
Why is the ActionLink helper leaving out the controller name, and why is the link still working? I have tried refreshing the page with cache clearing, with no difference. I don't know what else do besides leave it alone because it works, but I'm concerned this is some quirk and it will no longer work when I deploy my site.
My routing is still standard, out-of-box, in the Startup class's Configure method:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
Strangely enough, I have now added a Download link each row of an HTML table of files, looking like this:
#Html.ActionLink("Download", "Download", "File", new { filePath = file.Path })
and this link also renders without the controller name, e.g:
https://localhost/Download?filePath=....
Http{Verb}("{routeTemplae}") is usually used when building a REST API. In order to be able to generate URLs for the default MVC controllers when using attribute routing you will need to use Route attribute.
The original controller probably did not have a route prefix which meant that
//POST /Upload //<-- when there is no prefix on the controller
[HttpPost("Upload")]
on the controller would route to /Upload with no controller prefix.
By including a prefix for the route on the controller it will include the controller name when generating URLs when using attribute routing.
[Route("[controller]"]
public class FileController : Controller {
//POST File/Upload
[HttpPost]
[Route("Upload")]
public async Task<ActionResult> Upload(UploadFilesViewModel model, IEnumerable<IFormFile> files) {
//...
}
//GET File/Download
[HttpGet]
[Route("Download")]
public async Task<IActionResult> Download(string filePath) {
//...
}
}
Reference Routing in ASP.NET Core
Reference Routing to Controller Actions
This is most strange, but in my FileController, I had leftover attribute routing from when I was using the the controller for a Web API instead of in the MVC web application. The methods essentially looked like this:
[HttpPost("Upload")]
public async Task<ActionResult> Upload(UploadFilesViewModel model, IEnumerable<IFormFile> files)
[HttpGet("Download")]
public async Task<IActionResult> Download(string filePath)
Now that I have removed the route names from the attributes, and have just plain [HttpPost] and [HttpGet], the actionlinks work correctly and render URLs that now do contain the controller name.
[HttpPost("Upload")]
Here Using POST method so no need of prefix like '/' on MVC controller
[HttpGet("Download")]
public async Task Download(string filePath)
I'm trying to create a link to the following GET action with Url.Action:
public class FooController : Controller
{
[HttpGet]
[Route("Foo/Apply/BarDetails/Part/{id}")]
public IActionResult BarDetails(int id)
{
}
}
And in my view I'm creating a URL with
Url.Action("BarDetails", "Foo", new {id = 1})
However this generates the URL Foo/Apply/BarDetails/Part/10?id=1 which is definitely not what I want! Removing the values parameter simply removes the query string part of the URL. The only route I have registered at startup is
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
I've had a look at the ASP.NET Core source and I suspect that the "10" part of the URL is coming from a "1" + "0" operation somewhere as the routing code is split across 2+ repos it's hard to follow. Does anyone know how the last part of this URL is being generated and how I can fix it to match the id provided in the route values?
It turns out that routing has changed between MVC5 (where this controller was written) and ASP.NET Core 2. It's also related to the way I've structured my controller. Basically at some point the list of possible routes gets sorted by the URL so Foo/Apply/BarDetails/Part/10 gets sorted to the top of the list and as this matches the requested URL, it gets returned. For the full details, see this GitHub issue.
You can set maximum and minimum range of optional route parameter in attribute based routing. Follow the code below
enter code here [Route("Licence/Apply/Details/Part/{id:int:max(1000):min(10)}}")]
I have upgrade aspx project to mvc. Now some of my old customer calling url with .aspx page and they are getting 404(not found) in mvc project.
So now I have to redirect .aspx to mvc page.
Old URL
www.domain.com/bookshop/pc-58573-53-{product_name}.aspx
New URL
www.domain.com/{product_name}
I am thinking to do via routing mechanism of mvc. like once this type of url come then it should be call my custom mvc action and in string parameter i will get pc-58573-53-{product_name}.aspx
Can you please suggest a best way to do this with minimal code.
Just define an action with route 'bookshop/{pageName}'
Here are examples for 2 scenarios using Route attribute:
In case, you don't want the URL to change:
[Route("bookshop/{pageName}")]
public ActionResult MyAction(string pageName)
{
// add logic according to what you receive in pageName property
return View();
}
or, In case you want to Redirect to a new URL:
[Route("bookshop/{pageName}")]
public ActionResult MyAction(string pageName)
{
// Create and use a method to ExtractProductNameFromPageName
string productName = ExtractProductNameFromPageName(pageName);
return Response.Redirect("~/" + productName);
}
The parameter 'pageName' here should catch the page name past 'bookshop/'.
In case, you don't have route attribute mapping enabled, add code below in RegisterRoutes method of RouteConfig.cs file:
// enable mapping of routes defined using Route attribute on specific actions.
routes.MapMvcAttributeRoutes();
This is regarding defining routes using route attribute. I have two controllers
1st Controller
[AllowAnonymous]
[Route("Member/Login")]
public ActionResult Login(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
2nd Controller
[Route("{CategoryURL}/{Keywords}")]
public ActionResult BrowseProducts(string CategoryURL, string Keywords)
{
}
I am getting below error If try to access URL xyz.net/Member/Login
Multiple controller types were found that match the URL. This can happen if attribute routes on multiple controllers match the requested URL.
The request has found the following matching controller types:
XYZ.Controllers.AccountController
XYZ.Controllers.CoursesController
I am aware that I have Optional Parameters for second controller, hence when I try to access xyz.net/Member/Login, it finds two action methods to go for and getting the Multiple controller error.
My question is, how could I fix this issue without changing my current Routings. I tried Order, Precedence but nothing worked out.
As I commented above, based on your elected tags you are using MVC 4 yet attribute routing is supported in MVC 5. If you really are in MVC 5, then the following reference would be very helpful for you:
https://blogs.msdn.microsoft.com/webdev/2013/10/17/attribute-routing-in-asp-net-mvc-5/
Most importantly, it's crucial that when you register your routes you active attribute routing -->
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapMvcAttributeRoutes();
}
And also ensure you don't have any other routes being defined that could confict with the attributes you've elected to use.
Otherwise your selected attribute should work fine. Pretty simple application.
The web api constraints may also aply for what you are trying to accomplish
http://www.asp.net/web-api/overview/web-api-routing-and-actions
I want to remove controller name from URL for specific Controller.
My Controller name is Product
I found some link to do this
Routing with and without controller name
MVC Routing without controller
But all the above links done in route config file. and those are affecting other controller too. I want to do it using Attribute Routing.
Can it is possible? As I want to do this for only Product controller.
I have tried to do it on action like this
[Route("Sample/{Name}")]
but it is not working.
Gabriel's answer is right, however, it can be a bit misleading since you're asking for MVC and that answer is for Web API.
In any case, what you want is to put the annotation over the class definition instead of an action method. MVC example would be like:
[RoutePrefix("SomethingOtherThanProduct")]
public class ProductController : Controller
{
public ActionResult Index()
{
...
return View();
}
}
I'm also dropping this as an answer since you may find the following article helpful: [Attribute] Routing in ASP.NET MVC 5 / WebAPI 2
Make sure you set the RoutePrefix attribute on the whole controller class, as well as using the Route attribute on the action.
[RoutePrefix("notproducts")]
public class ProductsController : ApiController
{
[Route("")]
public IEnumerable<Product> Get() { ... }
}