如何将同一 url 上的不同 HTTP 方法映射到不同的控制器?

How to map different HTTP methods on the same url to different controllers?

我的 API 我的应用程序的一小部分分为两个控制器,因为(外部)对 API 中 JSON 数据的外壳的要求(一些requests 应该使用 camelCasing,而其他的应该使用 PascalCasing)。

现在,我有一个 url,我想将其映射为 GET 的 PascalCasing,但 PUT 的驼峰式,因此我尝试了以下操作:

[PascalCasing] // custom attribute, part of our code
               // We configure all controllers that *don't* have this to use
               // camelCasing
public class PascalCasedController : ApiController
{
    [HttpGet]
    [Route("url/to/my/resource/{id}")]
    public IHttpActionResult(int id)
    {
        return Ok(GetResource(id));
    }
}

public class CamelCasedController : ApiController
{
    [HttpPut]
    [Route("url/to/my/resource/{id}")]
    public IHttpActionResult(int id, Resource resource)
    {
        SaveResource(id, resource);
        return Ok();
    }
}

GET 请求按预期工作,但如果我尝试使用 Fiddler PUT 那里的东西,我收到以下错误消息:

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:
MyProject.PascalCaseController
MyProject.CamelCaseController

我意识到这可能是因为 WebAPI 首先将路由映射到控制器,然后再映射到操作,但是如果考虑 HTTP 方法,这里确实没有任何歧义。有什么方法可以告诉 WebAPI 如何做到这一点,而不必在同一个控制器中使用这些方法?

@Tomas - System.Web.Http 程序集中公开了一个接口 "System.Web.Http.Dispatcher.IHttpControllerSelector"。您可以使用该接口并创建您自己的 HttpControllerSelector。然后,您可以在 AreaRegistration 期间将 DefaultControllerSelector 替换为 HttpConfiguration 中的自定义控制器选择器。

httpConfig.Services.Replace(typeof(IHttpControllerSelector), new CustomControllerSelector(services.GetHttpControllerSelector()));

在此自定义控制器选择器中,您可以编写自己的 IHttpControllerSelector 的 SelectController() 方法的实现,您可以在其中调用 IHttpControllerSelector 的 GetControllerMapping() 方法。这将为您提供所有已注册控制器的列表。对于每个控制器,您可以检查 DeclaredMethods 并检查每个 DeclaredMethods 的 CustomAttributes。在您的情况下,它将是 HttpGetAttribute 或 HttpPutAttribute。

检查传入的 HttpRequestMessage (GET/PUT) 的方法类型并将其与 CustomAttributes 的值进行比较。如果您找到传入请求 URL 和相应的 Http Verb 组合的匹配项,那么您可以从 SelectController() 方法中获取 HttpControllerDiscriptor 和 return 它。

这将允许您在两个不同的控制器中使用相同的 URL 和不同的方法。