12

私の美しいRESTWebサービスはうまく機能します。のようなページにアクセスした場合を除き~/ます。これは、デフォルトのIIS 403 Forbiddenページを返します(Fiddlerを使用し、のみを指定した場合でもAccept: application/json)。JSONまたはXMLエラーだけが必要です。カスタム例外ハンドラーですべての例外をオーバーライドする方法はありますか?または、すべての不明な要求を処理するデフォルトのコントローラーですか?クライアントがRESTAPI対応のXMLデータグラムまたはJSONBLOBのみを解析する必要があるように、これを処理するための最も簡単で最も正しい(異なる場合)方法は何ですか?

リクエストの例:

GET http://localhost:7414/ HTTP/1.1
User-Agent: Fiddler
Host: localhost:7414
Accept: application/json, text/json, text/xml

応答:(私は気に入らないtext/htmlのですが、受け入れられた応答タイプの1つではなかったことに注意してください)

HTTP/1.1 403 Forbidden
Cache-Control: private
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/8.0
X-SourceFiles: =?UTF-8?B?QzpcaWNhcm9sXENoYXJpdHlMb2dpYy5pQ2Fyb2wuQXBp?=
X-Powered-By: ASP.NET
Date: Fri, 25 Jan 2013 21:06:21 GMT
Content-Length: 5396

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<title>IIS 8.0 Detailed Error - 403.14 - Forbidden</title> 
<style type="text/css"> 
<!-- 
...

応答(私が好む):

HTTP/1.1 403 Forbidden
Cache-Control: private
Content-Type: application/json; charset=utf-8
Date: ...
Content-Length: ....

{
  "error":"forbidden",
  "status":403,
  "error_description":"Directory listing not allowed."
}
4

3 に答える 3

12

2014 年 1 月 26 日編集: Microsoftは、最新の WebAPI 2.1 アップデートに「グローバル エラー処理」を追加しました。


わかりました、私はそれを手に入れたと思います。それにはいくつかの部分があります。

最初: エラー用のコントローラーを作成します。HTTP エラー コードに従ってアクションに名前を付けました。

public class ErrorController : ApiController {
    [AllowAnonymous]
    [ActionName("Get")]
    public HttpResponseMessage Get() {
        return Request.CreateErrorInfoResponse(HttpStatusCode.InternalServerError, title: "Unknown Error");
    }

    [AllowAnonymous]
    [ActionName("404")]
    [HttpGet]
    public HttpResponseMessage Status404() {
        return Request.CreateErrorInfoResponse(HttpStatusCode.NotFound, description: "No resource matches the URL specified.");
    }

    [AllowAnonymous]
    [ActionName("400")]
    [HttpGet]
    public HttpResponseMessage Status400() {
        return Request.CreateErrorInfoResponse(HttpStatusCode.BadRequest);
    }

    [AllowAnonymous]
    [ActionName("500")]
    [HttpGet]
    public HttpResponseMessage Status500() {
        return Request.CreateErrorInfoResponse(HttpStatusCode.InternalServerError);
    }
}

次に、GenericExceptionFilterAttributeHttpActionExecutedContext.Exception が設定されているかどうか、および応答がまだ空であるかどうかを確認する を作成しました。両方のケースが true の場合、応答が生成されます。

public class GenericExceptionFilterAttribute : ExceptionFilterAttribute {
    public GenericExceptionFilterAttribute()
        : base() {
        DefaultHandler = (context, ex) => context.Request.CreateErrorInfoResponse(System.Net.HttpStatusCode.InternalServerError, "Internal Server Error", "An unepected error occoured on the server.", exception: ex);
    }

    readonly Dictionary<Type, Func<HttpActionExecutedContext, Exception, HttpResponseMessage>> exceptionHandlers = new Dictionary<Type, Func<HttpActionExecutedContext, Exception, HttpResponseMessage>>();

    public Func<HttpActionExecutedContext, Exception, HttpResponseMessage> DefaultHandler { get; set; }

    public void AddExceptionHandler<T>(Func<HttpActionExecutedContext, Exception, HttpResponseMessage> handler) where T : Exception {
        exceptionHandlers.Add(typeof(T), handler);
    }

    public override void OnException(HttpActionExecutedContext context) {
        if (context.Exception == null) return;

        try {
            var exType = context.Exception.GetType();
            if (exceptionHandlers.ContainsKey(exType))
                context.Response = exceptionHandlers[exType](context, context.Exception);

            if(context.Response == null && DefaultHandler != null)
                context.Response = DefaultHandler(context, context.Exception);
        }
        catch (Exception ex) {
            context.Response = context.Request.CreateErrorInfoResponse(HttpStatusCode.InternalServerError, description: "Error while building the exception response.", exception: ex);
        }
    }
}

私の場合は、主要な例外の種類ごとにサポートを登録し、それらの例外の種類を特定の HTTP 応答コードにマップできる単一の汎用ハンドラーを使用しました。例外タイプとハンドラーをこのフィルターでグローバルに登録しますglobal.asax.cs

// These filters override the default ASP.NET exception handling to create REST-Friendly error responses.
var exceptionFormatter = new GenericExceptionFilterAttribute();
exceptionFormatter.AddExceptionHandler<NotImplementedException>((context, ex) => context.Request.CreateErrorInfoResponse(System.Net.HttpStatusCode.InternalServerError, "Not Implemented", "This method has not yet been implemented. Please try your request again at a later date.", exception: ex));
exceptionFormatter.AddExceptionHandler<ArgumentException>((context, ex) => context.Request.CreateErrorInfoResponse(System.Net.HttpStatusCode.BadRequest, exception: ex));
exceptionFormatter.AddExceptionHandler<ArgumentNullException>((context, ex) => context.Request.CreateErrorInfoResponse(System.Net.HttpStatusCode.BadRequest, exception: ex));
exceptionFormatter.AddExceptionHandler<ArgumentOutOfRangeException>((context, ex) => context.Request.CreateErrorInfoResponse(System.Net.HttpStatusCode.BadRequest, exception: ex));
exceptionFormatter.AddExceptionHandler<FormatException>((context, ex) => context.Request.CreateErrorInfoResponse(System.Net.HttpStatusCode.BadRequest, exception: ex));
exceptionFormatter.AddExceptionHandler<NotSupportedException>((context, ex) => context.Request.CreateErrorInfoResponse(System.Net.HttpStatusCode.BadRequest, "Not Supported", exception: ex));
exceptionFormatter.AddExceptionHandler<InvalidOperationException>((context, ex) => context.Request.CreateErrorInfoResponse(System.Net.HttpStatusCode.BadRequest, "Invalid Operation", exception: ex));
GlobalConfiguration.Filters.Add(exceptionFormatter)

次に、キャッチオール ルートを作成して、すべての未知のリクエストを新しいエラー ハンドラに送信します。

config.Routes.MapHttpRoute(
    name: "DefaultCatchall",
    routeTemplate: "{*url}",
    defaults: new {
        controller = "Error",
        action = "404"
    }
);

そして、すべてをまとめるために、これをに追加して、IIS が ASP.NET を介してすべての要求を処理できるようにしますweb.config

<configuration>
    <system.webServer>
        <modules runAllManagedModulesForAllRequests="true" />
    </system.webServer>
</configuration>

オプションで、 のcustomErrorsセクションを使用して、web.configすべてのエラーを新しいエラー ハンドラにリダイレクトすることもできます。

于 2013-02-01T22:43:26.080 に答える
0

IIS マネージャーでは、カスタム エラーを編集できます。

IIS マネージャーを開き、管理するレベルに移動します。IIS マネージャーを開く方法については、「IIS マネージャーを開く (IIS 7)」を参照してください。UI 内の場所への移動については、「IIS マネージャーでのナビゲーション (IIS 7)」を参照してください。

[機能ビュー] で、[エラー ページ] をダブルクリックします。

[エラー ページ] ページで、変更するエラーをクリックして選択します。

[操作] ウィンドウで、[編集] をクリックします。

[カスタム エラー ページの編集] ダイアログ ボックスで、次のいずれかを選択します。

エラー コンテンツが動的 (.asp ファイルなど) の場合は、このサイトで URL を実行します。

クライアント ブラウザを別の URL にリダイレクトする場合は、302 リダイレクトで応答します。

[ファイル パス] テキスト ボックスに、カスタム エラー ページのパスを入力します (静的ファイルからエラー応答へのコンテンツの挿入) が選択されたパスの種類です。[このサイトで URL を実行する] または [302 リダイレクト パスで応答する] のいずれかを使用する場合は、代わりにカスタム エラー ページの URL を入力します。[OK] をクリックします。

スクリーンショットがいくつかあるRick Strahl の記事も参照してください。

ただし、これは応答の content-type ヘッダーに対処するとは思いません。JSON 形式に変更できるコンテンツ部分を変更する方法を概説しているだけです。よりカスタムなことをせずにそれを変更する方法はわかりませんが、一部のクライアントは、コンテンツがまだ JSON である場合に間違ったコンテンツ タイプを処理できるため、これで十分な場合があります (そうであれば、最も簡単なオプションである可能性があります)。 . したがって、これは完全な答えではありませんが、役立つ場合があります。

カスタム HTTP モジュールや、サーバー側のコード/構成 (.Net など) を使用してすべての要求を処理し、正しい応答とヘッダーを作成するなど、コードを多用するオプションが他にもあります ( ASP.NET の書き換えられたカスタム エラーを参照してください。 content-type ヘッダーまたはhttp://www.iis.net/learn/develop/runtime-extensibility/developing-iis-modules-and-handlers-with-the-net-framework ) を送信します。

于 2013-01-26T00:41:37.093 に答える