8

Similar to this question, but for the new ASP.NET Core.

I can override an action's routing name:

[ActionName("Bar")]
public IActionResult Foo() {

Can I do that for a controller, using attribute routing?

[?("HelloController")]
public SomeController : Controller {

It should allow generation of links using tag helpers:

<a asp-controller="some" ...      // before
<a asp-controller="hello" ...     // after
grokky
  • 8,537
  • 20
  • 62
  • 96

4 Answers4

25

Such an attribute does not exist. But you can create one yourself:

[AttributeUsage(AttributeTargets.Class)]
public class ControllerNameAttribute : Attribute
{
    public string Name { get; }

    public ControllerNameAttribute(string name)
    {
        Name = name;
    }
}

Apply it on your controller:

[ControllerName("Test")]
public class HomeController : Controller
{
}

Then create a custom controller convention:

public class ControllerNameAttributeConvention : IControllerModelConvention
{
    public void Apply(ControllerModel controller)
    {
        var controllerNameAttribute = controller.Attributes.OfType<ControllerNameAttribute>().SingleOrDefault();
        if (controllerNameAttribute != null)
        {
            controller.ControllerName = controllerNameAttribute.Name;
        }
    }
}

And add it to MVC conventions in Startup.cs:

services.AddMvc(mvc =>
{
    mvc.Conventions.Add(new ControllerNameAttributeConvention());
});

Now HomeController Index action will respond at /Test/Index. Razor tag helper attributes can be set as you wanted.

Only downside is that at least ReSharper gets a bit broken in Razor. It is not aware of the convention so it thinks the asp-controller attribute is wrong.

juunas
  • 54,244
  • 13
  • 113
  • 149
  • I know it's been quite long time ago, but anyway thank you for the answer - it's very helpful. Can you please tell me how to rename controller name, but folder in Views remains in old name? – svstnv Sep 22 '20 at 03:33
  • @svstnv you could try something like this ---- services.Configure(options => options.ViewLocationFormats.Add("/Views/YourLocation/{0}" + RazorViewEngine.ViewExtension)); – user1131926 Oct 26 '20 at 08:06
8

If you don't want to derive the controller name from the Controller class (Classname minus Controller surfix), then just leave out the [controller] place holder.

[Route("/api/hello")]
public SomeController : Controller {
    [HttpGet]
    public IActionResult Get() { }

    [HttpGet("something")]
    public IActionResult GetSomething() { }
}

The overloads in HttpGet will set the action name. Doing so however, you can't use generic routes like

routes.MapRoute("default", "api/{controller}/{id?}");

or you have to register them manually there

routes.MapRoute("hello", "api/hello/{id?}", defaults: new { controller = "Hello" });
routes.MapRoute("default", "api/{controller}/{id?}");
Tseng
  • 61,549
  • 15
  • 193
  • 205
3

Here is code we are using

[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class MembersController : Controller { /* */ }
Craig Selbert
  • 737
  • 1
  • 8
  • 17
2

It should be the same way that you would do it in ASP.NET WebAPI2 before Core:

[Route("Bar")]
public IActionResult Foo() { }

If you're looking to do it on the Controller level as well, there's a different attribute for that:

[RoutePrefix("ControllerFoo")]
public class MyController() { }

Here is a (non-Microsoft) article that goes over how it should be done in .NET Core.

krillgar
  • 12,596
  • 6
  • 50
  • 86
  • Does it matter when you are doing tag helpers? I have some controllers that "don't make sense", but I've modified the routes to look relevant. – ganders Feb 20 '17 at 18:31
  • Unfortunately it matters because I'd need to refactor dozens of views. I was hoping for a way to just perform a rename in one place. Also, all the controller's actions already specify a route. I need this specifically to work with tag helpers. – grokky Feb 20 '17 at 18:33