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
表示错误被全局异常处理程序捕获。
几天以来,我一直在尝试弄清楚如何从我的网络 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
表示错误被全局异常处理程序捕获。