路由 - WebAPI 和 MVC 在一个项目中

Routing - WebAPI and MVC in one project

我有 2 个控制器。两者都与 Bar(Foo 的子级)打交道。

其中一个是针对 MVC 的 returns 视图。

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

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

其中一个用于 API,它存在于 "api" 的 文件夹 下。

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

无需将以下行添加到 FooBarController class,我可以访问 API 控制器(例如:/api/foo/bar/{id})和 MVC 控制器(对于示例:/FooBar/SomeAction)

[Route("Foo/Bar")]

但是当我这样做时,路由被弄乱了,我在尝试访问 /Foo/Bar/SomeAction

时收到以下错误

AmbiguousMatchException: The request matched multiple endpoints. Matches:

Controllers.MVC.FooBarController.Index

Controllers.MVC.FooBarController.SomeAction

当您没有应用该属性时,FooBarController 使用常规路由,您可能已经默认使用了该路由。它通常将 Index 作为默认操作,并允许您指定操作名称,因此一切正常。

应用属性时,此控制器中的所有操作现在都使用属性路由。这两种路由类型没有组合,您只能使用其中一种。请注意

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

(docs source)。所以这两个动作变得相同,它们的名字被忽略了,并且无法弄清楚该使用哪个。所以你需要帮助它:

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

是的,它更冗长。来自同一来源:

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.

对于您的 API 控制器,我建议将 Route 更改为 RoutePrefix["api"],这样您所有的 api 方法将只包含路由(希望是不同的名称比您的家庭控制器名称更重要。

[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
    }
}

同样的建议也适用于您的 HomeController(如果您真的想为其添加路由,那么 class 中的每个方法都将根据它们的方法名称进行路由,例如 Index

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

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

如果你想用 /Foo/Bar/SomeAction 路由 FooBarController.SomeAction,你可以尝试

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

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

我设法使用以下方法使其(看起来)正常工作。

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

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

}