ここで説明する手順に従って、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}
、 置くpersons/{id}
、 消去
私はそれらを「ネイティブ」メソッドで呼び出すことができ、期待どおりに機能します。X-HTTP-Method-Override
ただし、 「DELETE」または「PUT」を使用してヘッダーを送信し、POSTを介してそれらを呼び出そうとすると、 Not Found(404)エラーが発生します。このエラーが発生した場合、それが到達することは決してないことを追加することが重要ですMethodOverrideHandler
-私は決してヒットされないブレークポイントを設定しました。通常のDELETEとPUTを呼び出すと、ブレークポイントに到達します。
別のメソッドを追加してみました:
persons/{id}
、 役職
これを行うと、代わりに許可されていないメソッド(405)が表示されます。
メッセージハンドラーは、ルーティングディスパッチャーとコントローラーディスパッチャーの前に実行されると思いました。なぜこれが私に404を与えるのですか?
これは関連しているとは思いませんが、デフォルトのWebAPIルーティングを使用していません。代わりに、次のように、各メソッドに割り当てられたカスタム属性を使用してマッピングしています。
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;
}
}