X-HTTP-Method-Override предоставляет NotFound (404) в веб-API ASP.NET
Я пытаюсь реализовать переопределение метода HTTP, следуя шагам, описаннымВот, По сути, я создаю DelegatingHandler, похожий на следующий, и добавляю его в качестве обработчика сообщений.Application_Start
public class MethodOverrideHandler : DelegatingHandler
{
readonly string[] _methods = { "DELETE", "HEAD", "PUT" };
const string _header = "X-HTTP-Method-Override";
protected override Task 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}
, ПОЛОЖИЛpersons/{id}
, УДАЛЯТЬЯ могу позвонить им через ихродные" методы и они работают как положено. Тем не менее, когда я пытаюсь вызвать их через POST, отправкаX-HTTP-Method-Override
заголовок с "УДАЛЯТЬ" или же "ПОЛОЖИЛ"ДаетНе найден (404) ошибка. Важно добавить, что, когда он дает эту ошибку, он никогда не достигаетMethodOverrideHandler
- Я поставил точку останова, которая никогда не срабатывает; он достигает точки останова, когда я вызываю обычные команды DELETE и PUT.
Я даже попытался добавить другой метод:
persons/{id}
, СООБЩЕНИЕКогда я делаю это, я получаюМетод не разрешен (405) вместо.
Я думал, что обработчики сообщений запускались ДО диспетчеров маршрутизации и контроллера. Почему это дает мне 404?
Я не думаю, что это связано, но я не использую маршрутизацию веб-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();
if (MethodHasAttribute())
{
verbs.Add(HttpMethod.Get);
}
else if (MethodHasAttribute())
{
verbs.Add(HttpMethod.Post);
}
else if (MethodHasAttribute())
{
verbs.Add(HttpMethod.Delete);
}
else if (MethodHasAttribute())
{
verbs.Add(HttpMethod.Put);
}
else
{
throw new ServiceModelException("HTTP method attribute should be used");
}
return verbs.Select(v => v.Method).ToArray();
}
private bool MethodHasAttribute() where T : Attribute
{
return GetMethodAttribute() != null;
}
private T GetMethodAttribute() where T : Attribute
{
return _methodInfo.GetCustomAttributes(typeof(T), true).FirstOrDefault() as T;
}
}