【发布时间】:2013-07-15 19:24:41
【问题描述】:
我正在尝试按照here 描述的步骤实现 HTTP 方法覆盖。基本上,我正在创建一个 DelegatingHandler,类似于以下内容,并将其添加为 Application_Start 上的消息处理程序。
public class MethodOverrideHandler : DelegatingHandler
{
readonly string[] _methods = { "DELETE", "HEAD", "PUT" };
const string _header = "X-HTTP-Method-Override";
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
// Check for HTTP POST with the X-HTTP-Method-Override header.
if (request.Method == HttpMethod.Post && request.Headers.Contains(_header))
{
// Check if the header value is in our methods list.
var method = request.Headers.GetValues(_header).FirstOrDefault();
if (_methods.Contains(method, StringComparer.InvariantCultureIgnoreCase))
{
// Change the request method.
request.Method = new HttpMethod(method);
}
}
return base.SendAsync(request, cancellationToken);
}
}
我的控制器上定义了以下方法:
-
persons/{id},获取 -
persons/{id},PUT -
persons/{id},删除
我可以通过它们的“本地”方法调用它们,它们按预期工作。但是,当我尝试通过 POST 调用它们,发送带有“DELETE”或“PUT”的 X-HTTP-Method-Override 标头时,它会给出 Not Found (404) 错误。重要的是要补充一点,当它给出这个错误时,它永远不会到达MethodOverrideHandler——我已经放置了一个永远不会命中的断点;当我调用正常的 DELETE 和 PUT 时,它确实会到达断点。
我什至尝试添加另一种方法:
-
persons/{id},发帖
当我这样做时,我得到一个 Method Not Allowed (405) 代替。
我认为消息处理程序是在路由和控制器调度程序之前运行的。为什么这给了我 404?
我不认为这是相关的,但我没有使用默认的 Web API 路由。相反,我使用分配给每个方法的自定义属性进行映射,如下所示:
routes.MapHttpRoute(
String.Format("{0}_{1}", operation.Name, service.ServiceId),
String.Format("{0}/{1}", service.RoutePrefix, routeTemplateAttribute.Template),
defaults,
new { httpMethod = GetHttpMethodConstraint(operation) });
[HttpDelete, RouteTemplate("persons/{id}")]
public HttpResponseMessage DeletePerson(string id)
{
// ...
}
编辑:GetHttpMethodConstraint 代码如下。
private static HttpMethodConstraint GetHttpMethodConstraint(MethodInfo methodInfo)
{
var methodResolver = HttpMethodResolver.FromMethodInfo(methodInfo);
return new HttpMethodConstraint(methodResolver.Resolve());
}
internal class HttpMethodResolver
{
private MethodInfo _methodInfo;
private HttpMethodResolver(MethodInfo methodInfo)
{
_methodInfo = methodInfo;
}
public static HttpMethodResolver FromMethodInfo(MethodInfo methodInfo)
{
return new HttpMethodResolver(methodInfo);
}
public string[] Resolve()
{
var verbs = new List<HttpMethod>();
if (MethodHasAttribute<HttpGetAttribute>())
{
verbs.Add(HttpMethod.Get);
}
else if (MethodHasAttribute<HttpPostAttribute>())
{
verbs.Add(HttpMethod.Post);
}
else if (MethodHasAttribute<HttpDeleteAttribute>())
{
verbs.Add(HttpMethod.Delete);
}
else if (MethodHasAttribute<HttpPutAttribute>())
{
verbs.Add(HttpMethod.Put);
}
else
{
throw new ServiceModelException("HTTP method attribute should be used");
}
return verbs.Select(v => v.Method).ToArray();
}
private bool MethodHasAttribute<T>() where T : Attribute
{
return GetMethodAttribute<T>() != null;
}
private T GetMethodAttribute<T>() where T : Attribute
{
return _methodInfo.GetCustomAttributes(typeof(T), true).FirstOrDefault() as T;
}
}
【问题讨论】:
-
您是否尝试将您的操作方法从
DeletePerson更改为Delete? -
我在下面回答了,但是如果您可以压缩您的应用程序并将其放在某个地方,我可以看看,因为我无法重现您的问题。
-
仅作记录,我的问题已通过这个答案得到澄清:stackoverflow.com/a/13959148/1288760。
标签: c# asp.net-web-api