2

我试图弄清楚如何以标准方式返回我的数据。我的意思是当我返回 json 或 xml 时,最好为所有内容(成功和错误)提供一种格式。

假设我有以下 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。

我一直在使用foursquare,似乎他们总是将相同的格式返回给用户,但我不知道如何在我这样做时获得相同的东西。

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

我想做类似的事情

这将是一个好的要求。

{
    "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% 确定这仍然是最好的方法。至少它应该是一种格式呢?

编辑 @Erik Philips

当我只做 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; }
}

接下来,可能有大量不同类型的结果要返回,这里泛型就派上用场了:

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; }
}

所以现在如果你需要返回 aPerson你可以返回:

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 回答