hereの手順に従ってHTTPメソッドオーバーライドを実装しようとしています。基本的には、次のようなDelegatingHandlerを作成し、Application_Start
にメッセージハンドラとして追加しています。X-HTTP-Method-Overrideは、ASP.NET Web APIでNotFound(404)を返します。
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}
をGET、persons/{id}
をPUT、私は彼らを介してそれらを呼び出すことができます
をDELETE "ネイティブ"メソッドと期待どおりに動作します。しかし、POSTで呼び出すときに、X-HTTP-Method-Override
ヘッダーを「DELETE」または「PUT」で送信すると、が見つかりません(404)エラーが発生します。それは、このエラーを与えるとき、それがMethodOverrideHandler
に到達しないことを追加することが重要です - 私は決してヒットしないブレークポイントを置いた;通常のDELETEとPUTを呼び出すと、ブレークポイントにヒットします。
私も別の方法を追加してみました:
persons/{id}
、POST
私はこれを行うと、私が代わりに(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)
{
// ...
}
EDIT: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''にあなたのアクションメソッドを変更しようとしたことがありますか? –
私は以下のように回答しましたが、アプリケーションを解凍してどこかに置くことができれば、問題を再現することができなかったように見えます。 – tugberk
ちょうど記録のために、私の問題はこの答えによって明確になった:http://stackoverflow.com/a/13959148/1288760。 – alextercete