2

標準的な方法でデータを返す方法を見つけようとしています。これが意味することは、json または xml を返すときに、すべて (成功とエラー) に対して 1 つの形式があればよいということです。

次のjson結果があるとします。

{
  "person": {
    "id": 12345,
    "firstName": "John",
    "lastName": "Doe",
    "phones": {
      "home": "800-123-4567",
      "work": "888-555-0000",
      "cell": "877-123-1234"
    },
    "email": [
      "jd@example.com",
      "jd@example.org"
    ],
    "dateOfBirth": "1980-01-02T00:00:00.000Z",
    "registered": true,
    "emergencyContacts": [
      {
        "name": "",
        "phone": "",
        "email": "",
        "relationship": "spouse|parent|child|other"
      }
    ]
  }
}

これで問題ありませんが、検証エラーが発生した場合はどうなりますか

組み込みメソッド CreateErrorResponse を使用できます

{
  "Message": "The request is invalid.",
  "ModelState": {
    "item": [
      "Required property 'Name' not found in JSON. Path '', line 1, position 14."
    ],
    "item.Name": [
      "The Name field is required."
    ],
    "item.Price": [
      "The field Price must be between 0 and 999."
    ]
  }
}

*はい、データが意味をなさず、異なっていることは知っていますが、データは構造だけでは意味がありません。

エラーが発生した場合はどうなるでしょうか。この場合、カスタム エラー コードがあります。

このようなものを返すことができました(HttpErrorを使用)

{
  "Message": "My custom error message",
  "CustomErrorCode": 37
}

これで、3 つの異なる形式の json が戻ってくることがわかります。今クライアントで私はこれをしなければならないでしょう

  1. HttpStatusCode を確認する
    • 200 の場合、この場合は Person の形式を使用して json を解析します。
    • 400 の場合は、検証エラーまたはサーバー エラーである可能性があります。
    • カスタマー エラーが見つかった場合は、その形式を使用します。それ以外の場合は、modlestate を使用します。

私はフォースクエアで作業しており、常に同じ形式をユーザーに返すようですが、同じようなものを取得する方法がわかりません。

  {
          "meta": {
            "code": 200,
             ...errorType and errorDetail...
          },
          "notifications": {
             ...notifications...
          },
          "response": {
             ...results...
          }
        }

私はそれに似たようなことをしたいと思います

リクエストはOKでしょう。

{
    "meta": {
        "code": 200,
         "ModelState": {}
    },
    "response": {
        "person": {
            "id": 12345,
            "firstName": "John",
            "lastName": "Doe",
            "phones": {
                "home": "800-123-4567",
                "work": "888-555-0000",
                "cell": "877-123-1234"
            },
            "email": [
                "jd@example.com",
                "jd@example.org"
            ],
            "dateOfBirth": "1980-01-02T00:00:00.000Z",
            "registered": true,
            "emergencyContacts": [
                {
                    "name": "",
                    "phone": "",
                    "email": "",
                    "relationship": "spouse|parent|child|other"
                }
            ]
        }
    }
}

サーバーエラーは次のようになります

{
    "meta": {
        "code": 500,
        "message": "this is a server error",
        "ModelState": {}
    },
    "response": {}
}

検証は次のようになります

{
    "meta": {
        "code": 400,
        "message": "validation errors",
        "Message": "The request is invalid.",
        "ModelState": {
            "item": [
                "Required property 'Name' not found in JSON. Path '', line 1, position 14."
            ],
            "item.Name": [
                "The Name field is required."
            ],
            "item.Price": [
                "The field Price must be between 0 and 999."
            ]
        }
    },
    "response": {}
}

しかし、私が言ったように、このようなことを行う方法がわからず、これが最善の方法であると100%確信しているわけではありません. 少なくともそれは1つのフォーマットであるべきですか?

@エリック・フィリップスを編集

asp.net mvc プロジェクトだけを行っていたときは、このようなことをしていました。

public readonly IValidation validation;

public PersonService(IValidation validation)
{
    this.validation = validation;
}

public Person GetPerson(int id)
{

    try
    {
       return FindPerson(id);
    }
    catch(Exception ex)
    {
        //log real error with elmah
        validation.addError("internal", "Something went wrong");
    }
}


public class PersonController
{
     public readonly IPersonService personService;
     public PersonController(IPersonService personService)
     {
       this.personService = personService;
     }

    public ActionResult GetPerson(int id)
    {
        personService.GetPerson(id);

        if(personService.Validation.IsValid)
        {
          // do something
        }
        else
        { 
          // do something else
        }

        return View();
    }
}

設定方法は気に入っていますが、このままにしておきたいと思います。インターフェースは使えないと思いますが、こんなものを考えていました

public PersonService()
{

}

public ResponseResult<Person> GetPerson(int id)
{
    var result = ResponseResult<Person>();
    try
    {
       return FindPerson(id);
    }
    catch(Exception ex)
    {
       result.Errorcode = 200;
       result.Msg = "Failed";
    }
}


public class PersonController
{
     public readonly IPersonService personService;
     public PersonController(IPersonService personService)
     {
       this.personService = personService;
     }

    public HttpResponseMessage GetPerson(int id)
    {
       var result = personService.GetPerson(id);
       if(result.isValid)
       {
          Request.CreateResponse<ResponseResult<Person>>(HttpStatusCode.OK, result);
       }

         Request.CreateResponse<ResponseResult<Person>>(HttpStatusCode.BadRequest, result);
    }
}
4

1 に答える 1

2

これは、複数の部分を持つデータを送信するための設計であるため、一種の大きな問題ですが、これはかなり簡単で、小さく、エレガントなソリューションだと思います。

これは私が使用しているものとまったく同じではありませんが、良い例です:

最初に、すべての応答に必要なものを表すモデル、または結果データが不要な場合に使用できるモデルを構築しましょう。

public class ResponseResult
{
    public ResponseResult()
    {
    }

    public ResponseResult(ModelStateDictionary modelState)
    {
        this.ModelState = new ModelStateResult (modelState);
    }

    // Is this request valid, in the context of the actual request
    public bool IsValid { get; set; }
    // Serialized Model state if needed
    public ModelStateResult ModelState { get; set; }
}

次に、さまざまな種類の結果が返される可能性が高く、ここで Generics が役立ちます。

public class ResponseResult<T> : ResponseResult
{
    public ResponseResult() : base()
    {
    }

    public ResponseResult(ModelStateDictionary modelState)
        : base(modelState)
    {
    }

    public ResponseResult(T Data, ModelStateDictionary modelState)
        : base (modelState)
    {
        this.Data = Data;
    }

    public T Data { get; set; }
}

したがって、 a を返す必要がある場合は、次のように返すPersonことができます。

var result = ResponseResult<Person>();

result.Data = person;

//serialize result and send to client.

私の API は Javascript で使用できるため、Http ステータス コードを変更し、jQuery を使用してデータをリダイレクトして使用する方法の例を示します。

request = $.ajax({
  type: "POST",
  url: url,
  data: data,
  success: function(data, textStatus, jqXHR)
  {
    processResponseResult(data);
  }
  complete: function(e, xhr, settings)
  {
    if(e.status === 401)
    {
      // login to 
    }
    // else if (e.status == ) 
    else
    {
      // unknown status code
    }
)};

将来 http (WCF) を使用することさえないかもしれないクライアントによって消費されるように、結果を拡張したい場合があります。

public class ResponseResult
{
   ....
   ....
   public int ErrorCode { get; set; }
   public string ErrorMessage { get; set; }
}

またはさらに一歩進めます。

public class ResponseErrorBase
{
   public int ErrorCode { get; set; }
   public string ErrorMessage { get; set; }
}

public class ResponseResult
{
   ....
   ....
   public ResponseErrorBase Error { get; set; }
}

今後、エラーの種類や情報をさらに追加できます。

コメントごとに更新

コメント 1: 人のコレクションがある場合は..

List<Person> persons = new List<Person>();
var result = new ResponseResult<List<Person>>();
result.Data = persons;

コメント 2: 2 つのクラスがあります..

API に呼び出しがあった場合、FileExists(fileName)実際にオブジェクトを返す必要はなく、呼び出しが成功しただけです。

var result = new ResponseResult();
result.IsValid = FileExists(fileName);

API が新しい ID を返したい場合は、Person新しい ID を返すことができます。

var result = new ResponseResult<Guid?>();
result.IsValid = CreatePerson(personInfo);
if (result.IsValid)
{
  result.Data = personInfo.ID;
}

または、成功したオブジェクトを返すPersonか、失敗した場合は null を返すことができます。

var result = new ResponseResult<Person>();
result.IsValid = CreatePerson(personInfo);
if (result.IsValid)
{
  result.Data = Person;
}

コメントごとに更新

私がお勧めするのは、以前に書いたものResponseErrorBaseで、ResponseResultに含めます。

public class ResponseResult
{
  public ResponseResult()
  {
  }

  public ResponseResult(ModelStateDictionary modelState)
  {
    this.ModelState = new ModelStateResult (modelState);
  }

  public bool IsValid { get; set; }
  public ModelStateResult ModelState { get; set; }
  public ResponseErrorBase Error { get; set; }
}

次に、エラーをベースから特定のものに導き出します。

// this isn't abstract because you may want to just return
// non-specific error messages
public class ResponseErrorBase
{
  public int Code { get; set; }
  public string Message { get; set; }
}

public class InternalResponseError : ResponseErrorBase
{
  // A Property that is specific to this error but
  // not for all Errors
  public int InternalErrorLogID { get; set; }
}

次に、それを返します(値を返す例、さらにロジックが必要になります):

var result = new ResponseResult<Person>();

try
{
  result.Data = db.FindPerson(id);
}
catch (SqlException ex)
{
  var error = ResponseErrorBase();
  error.Code = 415;
  error.Message = "Sql Exception";
}
catch (Exception ex)
{
  var error = InternalResponseError();
  error.InternalErrorLogID  = Log.WriteException(ex);
  error.Code = 500;
  error.Message = "Internal Error";
}

// MVC might look like:
return this.Json(result);
于 2013-04-20T00:31:42.593 に答える