Remove controller name from URL with Attribute routing - c#

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() { ... }
}

Related

Prevent default attribute routing behaviour when no route attribute is specified on a controller method

When using only attribute routing in an ASP.NET Core 5.0 app, for example, with the following in Startup:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
A controller class with a single public method that has no route attribute will still be matched to a GET request that otherwise matches that controller.
For example GET api/test will match the Index method below (it doesn't matter what the method is called) even though the method doesn't have a route attribute:
[ApiController]
[Route("api/[controller]")]
public class TestController
{
public IActionResult Index()
{
return Ok();
}
}
If, however, another public method is added to that controller class, also without an attribute, an AmbiguousMatchException is thrown for GET api/test because routing can't otherwise figure out which method to use.
If a [HttpGet] attribute is added to one of those methods, the issue is resolved.
I understand that one solution is to just ensure that all methods have appropriate route attributes, or that only one method on the controller is public.
Is there a way to prevent this default/fallback behaviour where methods that don't have explicit attributes are still matched?
Can attribute routing be configured to only match methods that have explicit routing attributes on them?
Setting the route to:
[Route("api/{controller}/{action}")]
Will indicate that action has to be specified, it will not default to anything unless you specify default action like this:
[Route("api/{controller}/{action=Index}")]
Edit:
If you want to be forced to be explicit about the routes, then you can remove the [Route()] attribute from controller level and add it above every action like this
[ApiController]
public class TestController
{
[Route("api")]
[Route("api/[controller]")]
[Route("api/[controller]/index")]
public IActionResult Index()
{
return Ok();
}
[Route("api/[controller]/asdf")]
[Route("api/[controller]/asdf/{id?}")]
public IActionResult Index2(int? id)
{
return Ok();
}
}
The code will then not compile unless every action has a Route() attribute or other similar attributes like HttpGet(), HTTpPost() etc.

How can I make Url.Action() recognize areas defined in referenced Razor Class Libraries?

In my ASP.NET Core project's Razor, I'm using Url.Action() like this:
#Url.Action("Index", "Foo", new {Area = "MyArea"})
The controller action is defined in a referenced Razor Class Library like so:
[Route("MyArea/Foo")]
[Authorize]
public class FooController : Controller
{
[HttpPost]
public IActionResult Index()
{
return Ok();
}
}
Back in my ASP.NET Core project, in the Visual Studio code editor, MyArea appears in red, and the hover tooltip states "Cannot resolve area 'MyArea'". And of course, my call to Url.Action() returns string.Empty....
But the route is valid.
What change(s) could I apply to either my ASP.NET Core project or the referenced RCL to cause MyArea to be recognized as a valid area, and make the call Url.Action() return the expected URL?
[Area("MyArea")]
[Authorize]
public class FooController : Controller
{
[HttpPost]
public IActionResult Index()
{
return Ok();
}
}
Areas are different part of the route since they are named.. So you have to use the [Area()] otherwise it doesn't know what you are asking for the route. By convention the Foo route is already know since it's the name of the controller. This assumes you have the route mapped correctly in the Configure() method in your startup.cs. This also assumes that you are using the Area folder with a folder called MyArea in it.
https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/areas?view=aspnetcore-3.1

Questions about routing in Asp.net Core MVC 2

I can't wrap my mind around the routing mechanism in asp.net core MVC 2.
Here's what I have:
I already built a functioning page to add 'Materials' to a 'Application'.
The URL to call this page is:
http://localhost:33333/AddMaterial/Index/57
which uses the default route:
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}"
);
Whereby 57 is the application id, so that I know what 'Application' gets the new 'Material'. The Index Method in the controller looks like this, and works like expected:
[HttpGet] public IActionResult Index(string id)
{
var model = GetModel(context, int.Parse(id));
return View("Index", model);
}
So far so good... Now here's my problem:
I want to use the same controller and view to also edit 'Materials'. But for that i'd need two parameters in Index(). I'd need:
string applicationId
string materialId
as parameters.
So I added a new route:
routes.MapRoute(
name: "whatImTryingToAchieve",
template: "{controller}/{action}/{applicationId?}/{materialId?}"
);
And of course I updated the controller:
public IActionResult Index(string applicationiId, string materialId)
{
// Do stuff with materialId etc.
// ...
return View("Index", model);
}
I know that the routing system has a specific order. So I tried defining my new route before the default route. That didn't work.
I then tried to put it after the default route, which didn't work either.
I read through a lot of information about the routing system, but I didn't seem to find the answer to my simple question:
How can I add another, specific route?
Help would be much appreciated :)
EDIT: Attribute based routing as suggested by Igors Ĺ utovs
Controller:
[Authorize(Roles = "Admins")]
[Route("AddMaterial")]
public class AddMaterialController : Controller
{
//....
[Route("AddMaterial/{applicationId}/{otherid}")] // Nope. Nothing.
[HttpGet] public IActionResult Index(string applicationId, string otherid)
{
return View();
}
[Route("AddMaterial/Index/{applicationId}")] // Didn't work.
[Route("AddMaterial/{applicationId}")] // Didn't work either...
[HttpGet] public IActionResult Index(string applicationId)
{
return View();
}
}
So much for Attribute base routing.
I will provide my subjective opinion on the essence of the described problem. It seems that the question could be rephrased as: "What is the correct ASP.NET Core way to design routes and resolve such situations with multiple parameters?"
TLDR: use Attribute-Based Routing
This is the new type of routing which was added in ASP.MVC 5. Since then the old routing mechanism has been regarded as "Conventional-Based". ASP.NET Core currently allows mixing of both. I will provide a detailed example of how to solve the described problem using the Attribute-Based Routing only because the new approach provides the following advantages:
Route information is moved closer to controller actions, hence code is easier to understand and troubleshoot
Controller names and their method are decoupled from route names
Moving on to the solution. Let's assume that we have 2 models: Application and Material. I would create 2 distinct controllers to manage each. From your example, I understand that the relationship between these domain entities is one-to-many e.g. one Application could have many Materials. This suggests the following routes:
GET: applications/{applicationId}
GET: applications/{applicationId}/materials/{materialId}
...with a look back at principles of Restful Routing.
The ApplicationController would then look like this:
[Route("applications")]
public class ApplicationController
{
[HttpGet("{applicationId}")]
public IActionResult Index(string applicationId)
{
// return your view
}
}
While the MaterialController would look like this:
[Route("applications/{applicationId}/materials")]
public class MaterialController
{
[HttpGet("{materialId}")]
public IActionResult Index(string applicationId, string materialId)
{
// return your view
}
}
The call to UseMvc(..) extension method in Configure method of Startup.cs can now be simplified to just:
app.UseMvc();
Hope you'll find it useful!

How to override an Action method in Mvc

I'm using a Cms for Mvc. This Cms has the following Controller:
public class OrderController : Controller
{
public ActionResult Index()
{
return View();
}
}
For customization needs, I'd like to override the behaviour of this controller and return something different when the same URL is visited by the user. What's the best approach in order to achieve this result?
I tried to inherit the Cms Controller and make the ActionResult an override, following this answer: How to override controller actionresult method in mvc3?
public class OrderController : Cms.Areas.Admin.Controllers.OrderController
{
public override ActionResult Index(Guid orderItemId)
{
// Do extra stuff
return View();
}
}
But this doesn't work. When I try to navigate "admin/order" I still enter in the Cms Controller/Action.
Any suggestion?
NOTE: The Controller I'm trying to override is in another assembly and the action is set to virtual. It's in an Area, therefore the Route is configured inside AreaRegistration.
Your request need to use OrderController instead EcommerceOrderController, take a look on your MVC routes
This seems to me to be a routing question. It doesn't matter if you override the controller if your route still points to the original. If you want a URL to invoke your action, you need to add a route with a higher priority than the one that is currently resolving to the original.

Access to controller method

I have Controller with some method GET :
public class TestController : ApiController
{
public List<T> Get(){...}
[ActionName("GetById")]
public T Get(int id){...}
}
Can i access second Get method as /Get?id=1 even if i have different ActionName?
ActionName for generating cache with different names
Updated because my previous answer was related to standard MVC controllers not Web API because that is what the ActionName attribute is for. I am not sure what if anything it would do on a web api controller. Without attributes or a change from the deault routing your actions would have the following routes "/api/test/" Get() "/api/test/id" Get(int id) where id is an int.
If you want more flexibility MVC5 supports attribute routing

Categories