Beakie
Beakie

Reputation: 2009

Routing - WebAPI and MVC in one project

I have 2 controllers. Both dealing with Bar (a child of Foo).

One of them is for MVC which returns views.

[Route("Foo/Bar")]
public class FooBarController : ControllerBase
{
    public IActionResult Index()
    {
        // ...
    }

    public IActionResult SomeAction()
    {
        // ...
    }
}

One of them is for the API, which exists under a folder of "api".

[Route("api/Foo/Bar")]
[ApiController]
public class FooBarAPIController : ControllerBase
{
    //
}

Without adding the following line to the FooBarController class, I can access the API controller (for example: /api/foo/bar/{id}), and the MVC controller (for example: /FooBar/SomeAction)

[Route("Foo/Bar")]

But when I do, routing is messed up and I get the following error trying to access /Foo/Bar/SomeAction

AmbiguousMatchException: The request matched multiple endpoints. Matches:

Controllers.MVC.FooBarController.Index

Controllers.MVC.FooBarController.SomeAction

Upvotes: 0

Views: 359

Answers (4)

Beakie
Beakie

Reputation: 2009

I managed to get it (to be appear to be) working using the following.

[Route("Foo/Bar")]
[Route("Foo/Bar/[action]")]
public class FooBarController : Controller
{
    [HttpGet("")]
    public IActionResult Index()
    {
        return RedirectToAction("SomeAction");
    }

    public IActionResult SomeAction()
    {
        return View();
    }

}

Upvotes: 0

Edward
Edward

Reputation: 30056

If you want to route FooBarController.SomeAction with /Foo/Bar/SomeAction, you could try something like

    [Route("Foo/Bar/[action]")]
    public class FooBarController : ControllerBase
    {
            public IActionResult Index()
            {
                    // ...
            }

            public IActionResult SomeAction()
            {
                    // ...
            }
    }

Upvotes: 1

jPhizzle
jPhizzle

Reputation: 497

For your API controller, I recommend changing Route to RoutePrefix["api"], this way all of your api methods would then contain just the route (hopefully a different name than what you have for your Home Controller.

[RoutePrefix("api")]
public class MyApiController : ApiController 
{
    [HttpGet, Route("foo/bar")]
    public IHttpActionResult GetSomething()
    {
        //do something when routing to /api/foo/bar
    }

    [HttpPost, Route("foobar/foobar")]
    public IHttpActionResult GetSomething()
    {
        //do something when routing to /api/foobar/foobar
    }
}

Same advice goes to your HomeController (if you really want to add a route to it, and every method within that class will be routed according to their method name eg. Index)

[RoutePrefix("foo/bar")]
public class MyController : BaseController
{
    public IActionResult Index()
    {
        return View(); //or something
    }

    public IHttpActionResult Contact()
    {
        return View(); //or something
    }
}

Upvotes: 1

Andrei
Andrei

Reputation: 56726

When you don't have the attribute applied, FooBarController uses conventional routing, which you probably have already by default. It normally has Index as the default action, and allow you to also specify the action name, thus everything works.

When you apply the attribute, all actions in this controller now use attribute routing. These two routing types aren't combined, you can only use one or the other. Note that

"With attribute routing the controller name and action names play no role in which action is selected."

(docs source). So these two actions become the same, their names ignored, and it is impossible to figure out which one to use. So you need to help it:

[Route("Foo/Bar")]
public class FooBarController : ControllerBase
{
    [Route("")]      // To make it default for "Foo/Bar"
    [Route("Index")] // Combines to define the route template "Foo/Bar/Index"
    public IActionResult Index()
    {
        // ...
    }

    [Route("SomeAction")] // Combines to define the route template "Foo/Bar/SomeAction"
    public IActionResult SomeAction()
    {
        // ...
    }
}

Yes, it is more verbose. From the same source:

Attribute routing requires more input to specify a route; the conventional default route handles routes more succinctly. However, attribute routing allows (and requires) precise control of which route templates apply to each action.

Upvotes: 1

Related Questions