c# Web Api - 所有 api 响应的通用包装器 class

c# Web Api - generic wrapper class for all api responses

几天以来,我一直在尝试弄清楚如何从我的网络 api 通用响应中 return - 包装器 class 其中一个 属性 将具有动态尖型.

下面的代码片段显示了我想要实现的目标:

[RoutePrefix("api")]
public class TestController : ApiController
{
    [HttpGet]
    [Route("test")]
    public HttpResponseMessage Test3()
    {
        Smth smth = new Smth()
        {
            Something = "dsfdsfdsfs"
        };

        object apiResponse = this.GetResponse(true, smth);

        return base.Request.CreateResponse(HttpStatusCode.OK, apiResponse);
    }

    public object GetResponse(bool isSuccess, dynamic responseObject, string[] messages = null)
    {
        return new
        {
            is_success = isSuccess,
            response_object = responseObject,
            messages = messages
        };
    }
}

不幸的是,这个方法不起作用 - 我仍然得到:

异常消息

无法执行 <>f__AnonymousType0`3[System.Boolean,System.Object,System.String[]] ...

类型的序列化

异常类型

System.Runtime.Serialization.InvalidDataContractException

堆栈跟踪

System.Runtime.Serialization.DataContract.DataContractCriticalHelper.ThrowInvalidDataContractException(String message, Type type) w System.Runtime.Serialization.DataContract.DataContractCriticalHelper.CreateDataContract(Int32 id, RuntimeTypeHandle typeHandle, Type type) w System.Runtime.Serialization.DataContract.DataContractCriticalHelper.GetDataContractSkipValidation(Int32 id, RuntimeTypeHandle typeHandle, Type type) w System.Runtime.Serialization.DataContractSerializer.GetDataContract(DataContract declaredTypeContract, Type declaredType, Type objectType) w System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) w System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) w System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) w System.Runtime.Serialization.DataContractSerializer.WriteObject(XmlWriter writer, Object graph) w System.Net.Http.Formatting.XmlMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content) w System.Net.Http.Formatting.XmlMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken) --- Koniec śladu stosu z poprzedniej lokalizacji, w której wystąpił wyjątek --- w System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) w System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) w System.Net.Http.HttpContent.d__49.MoveNext() --- Koniec śladu stosu z poprzedniej lokalizacji, w której wystąpił wyjątek --- w System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) w System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) w System.Web.Http.Owin.HttpMessageHandlerAdapter.d__13.MoveNext()

在我的研究过程中,我在论坛上发现了一些效果很好的例子:

[RoutePrefix("api")]
public class TestController : ApiController
{
    [HttpGet]
    [Route("test")]
    public HttpResponseMessage Test3()
    {
        Smth smth = new Smth()
        {
            Something = "dsfdsfdsfs"
        };

        var apiReponse = new
        {
            is_success = true,
            response_object = smth,
            messages = new string[] { "dsfsfdds" }
        };

        return base.Request.CreateResponse(HttpStatusCode.OK, apiReponse);
    }
}

上面的示例有效并且 returns 正确格式化了数据,但是这种方法会导致与命名相关的错误(这样我每次 return 时都必须指定响应结构)。

从我的角度来看,这两种方法没有区别,除了第一种情况我们得到匿名类型,第二种情况我们使用对象。

所以问题是:

我的第一种方法是否可行?

基本上将对象序列化为字符串 json 表示和 return 内容类型 "application/json" 有效。

Smth smth = new Smth()
{
    Something = "dsfdsfdsfs"
};
var serializer = new JavaScriptSerializer();
string json = serializer.Serialize(this.GetResponse(true, smth));
var response = this.Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(json, Encoding.UTF8, "application/json");
return response;

Returns

{"is_success":true,"response_object":{"Something":"dsfdsfdsfs"},"messages":null}

注意:虽然这不是您问题的答案,但我想发表评论,以免其他人被误导。

我强烈建议对每个场景都使用 200(OK)响应是不合适的,RESTful。

如果响应代码始终为 200(正常),即使出现错误,客户端也必须检查每个响应的 is_success = true,

正如 MDN 引用的那样:(LINK)

The HTTP response codes should be used properly for e.g. HTTP response status codes indicate whether a specific HTTP request has been successfully completed. Responses are grouped into five classes:

  • 信息回复 (100–199)
  • 成功回复 (200–299)
  • 重定向 (300–399)
  • 客户端错误 (400–499)
  • 服务器错误 (500–599)

例如,

  • 如果请求包含无效数据或不包含所需数据 字段你应该 return 400 (Bad Request).
  • 如果服务器出现意外错误则使用 500(内部服务器错误)
  • 如果在服务器上创建了新资源,则使用 201(已创建)

我还建议阅读有关正确使用 HTTP 方法的内容。 (LINK)

至于一般响应,您可以 return 在成功响应中使用类似这样的内容:(这完全是我的结构,可能不是最佳做法)

{
    ResponseCode:1,
    Message:"User created",
    Data:{//Any complex object
         purchases:[
            {data 1},
            {data 2}
          ]
       },
    Exception:null
}

万一服务器出错(仅用于开发):

{
    ResponseCode:2,
    Message:"Caught in Global exception filter",
    Data:null,
    Exception: {//Do not send this in production
        Message: "An error has occurred.",
        ExceptionMessage:,
        ExceptionType":,
        StackTrace: ,
        InnerException: {
     }
   }
}

这里 ResponseCode 是您的自定义代码,可用于更详细地描述问题,例如上面例子中的ResponseCode:2表示错误被全局异常处理程序捕获。