ASP.NET Web API 2.0統一応答フォーマット

4955 ワード

従来の実装


Web APIサービスを構築する際、クライアントの要求に対して、私たちは一般的に応答するJSONフォーマットをカスタマイズします.例えば:
{
    "Data" : {
        "Id" : 100,
        "Name" : "Robin"
    },
    "ErrorMessage" : "    "
}

ASPに基づく.NET Web APIのアプリケーションでは、次のような構造のCクラスを作成します.
public class ApiResult
{  
   public string ErrorMessage { get; set; }
   public object Data { get; set; }
}

ここで、ErrorMessageは空またはnullであり、異常がないことを示す.この場合、Dataは必要なデータである.逆に、ErrorMessageが空またはnullでない場合は、エラーメッセージを表し、Dataはnullになります.
次にActionでクラスのインスタンスを返します.Web APIでは、フォーマットを内部で呼び出して、次のようにオブジェクトをJSONまたはXMLなどのフォーマットにシーケンス化します.
public class UserController : ApiController
{
    public IHttpActionResult GetUser()
    {
        return new ApiResult()
        {
            Data = new User{ Id = 100, Name = "Robin" },
            ErrorMessage = string.Empty
        };
    }
}

public class User
{
    public int Id {get; set;}
    public string Name {get; set;}
}

はい、伝統的なやり方はこのようにして、実現することができます.しかしさらに考えると、非常に多くのアクション方法があれば、毎回reutrn new ApiResult(){......}と書きます.特に煩わしいのではないでしょうか.

に質問


Actionメソッドで本当に必要なデータだけを返す方法はありませんが、クライアントに返すときに約束のJSON構造に統一されるのでしょうか.

ソリューション


もちろん、Web APIが提供するActionFilterを利用すれば実現できる.
まず、CustomActionFilterを新規作成します.
public class CustomActionFilter : ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext context)
    {
        var content = context.Response?.Content as ObjectContent;
        if (content != null)
        {
            content.Value = new ApiResult 
                            { 
                                Data = content.Value, 
                            };
        }
    }
}

アクションメソッドは次のように書きます.
public User GetAll()
{
    return new new User{Id = 100, Name = "Robin"};
}

このような実装のもう1つの利点は、戻り値が強いタイプであるため、これに基づいてAPIドキュメントを生成することができ、方法の可読性もより良いことである.

例外処理


前述した需要が実現し、異常をどのように処理するかをさらに考慮します.コードBUGが未処理の例外を投げ出すため、Web APIは依然としてCustomActionFilterのコードを呼び出すが、この場合Response=nullとなりcontent.Valueは再割り当てされます.この場合、Web APIは、フレームワークで約束されたJSONメッセージをクライアントに返します.これは、Web APIが投げ出した未処理の例外メッセージです.
{
  "Message": "An error has occurred.",
  "ExceptionMessage": "No MessageException parameter",
  "ExceptionType": "Framework.Common.MessageException",
  "StackTrace": "     Controllers.FooController.GetAll()   ......
}

この場合、異常メッセージが業務約定のJSON形式に従うことを望む場合は、どうすればよいでしょうか.ここではいくつかの状況に分けます.

Action内の異常


CustomActionFilterのOnActionExecutedメソッドで直接処理でき、改造後のコードは以下の通りです.
public override void OnActionExecuted(HttpActionExecutedContext context)
{
    var content = context.Response?.Content as ObjectContent;
    if (content != null)
    {
        content.Value = new ApiResult { Data = content.Value };
    }
    
    //           
    if (context.Exception != null)
    {
        context.Response = new HttpResponseMessage(HttpStatusCode.InternalServerError)
        {
            Content = new StringContent(JsonConvert.SerializeObject(
                new ApiResult
                {
                    ErrorMessage = context.Exception.Message
                }), Encoding.UTF8, "application/json")
        };
    }
}

同様に、カスタムExceptionFilterを使用して同様の目的を達成することもできます.ここでは、コードを簡単に貼らないようにします.

その他の例外


ActionFilterAttributeとExceptionFilterAttributeは、Action内の異常などの一部の異常しか処理できませんが、例えば以下のような未処理の異常は、フィルタが助けられません.
  • Controllerコンストラクタからの異常.
  • Message Handlersからの異常.
  • マッチングルーティング中の異常
  • 応答コンテンツのシーケンス化中に発生する異常
  • .
    Web APIは、グローバル範囲内の未処理例外を処理するために、ExceptionHandlerおよびExceptionLoggerを提供する.詳細は私が翻訳した文書:ASP.を参照してください.NET Web API 2におけるグローバルエラー処理では、未処理の例外をキャプチャして処理した後、応答メッセージを再設定できるのはExceptionHandlerのみであり、ExceptionLoggerはできない.
    コードは次のとおりです.
    public class CollectServiceExceptionHandler : ExceptionHandler
    {
        public override Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
        {
            context.Result = new ApiResult { ErrorMessage = context.Exception.Message };
    
            return base.HandleAsync(context, cancellationToken);
        }
    }

    注意:ここでExceptionHandlerContextのResult属性のタイプはIHttpActionResultなので、ApiResultクラスはIHttpActionResultインタフェースを実装します.
    ExceptionHandlerの目的は、グローバル範囲内で処理されていない例外を受信し、カスタムエラーメッセージを返すことです.

    まとめ


    冒頭のニーズを実現するには、3つの実現方法があります.
  • カスタムActionFilterAttribute
  • カスタムExceptionFilterAttribute
  • カスタムExceptionHandler
  • 補足:@ichengzi氏によると、『web api 2.0以前のバージョンではこの処理方法はサポートされていなかった』という.
    転載先:https://www.cnblogs.com/songxingzheng/p/6423697.html