ServiceStack 错误处理
抛出C#异常
在大多数情况下,您不需要关心ServiceStack的错误处理,因为它为抛出C#异常的正常用例提供本机支持,例如:
public object Post(User request)
{
if (string.IsNullOrEmpty(request.Name))
throw new ArgumentNullException("Name");
}
HTTP错误C#异常的默认映射
默认C#例外:
ArgumentException
使用HTTP StatusCode为400 BadRequest返回继承NotImplementedException
或者NotSupportedException
作为405 MethodNotAllowed返回AuthenticationException
以401 Unauthorized身份返回UnauthorizedAccessException
以403 Forbidden返回OptimisticConcurrencyException
返回409冲突- 其他正常的C#异常作为500 InternalServerError返回
可以使用用户定义的映射扩展此列表Config.MapExceptionToStatusCode
。
WebServiceException
所有异常都被注入到ResponseStatus
响应DTO 的属性中,该属性被序列化到ServiceClient的首选内容类型中,使得错误处理变得透明,无论您的首选格式如何 - 即,相同的C#错误处理代码可用于所有ServiceClient。
try
{
var client = new JsonServiceClient(BaseUri);
var response = client.Send<UserResponse>(new User());
}
catch (WebServiceException webEx)
{
/*
webEx.StatusCode = 400
webEx.StatusDescription = ArgumentNullException
webEx.ErrorCode = ArgumentNullException
webEx.ErrorMessage = Value cannot be null. Parameter name: Name
webEx.StackTrace = (your Server Exception StackTrace - in DebugMode)
webEx.ResponseDto = (your populated Response DTO)
webEx.ResponseStatus = (your populated Response Status DTO)
webEx.GetFieldErrors() = (individual errors for each field if any)
*/
}
其中
StatusCode
和StatusDescription
是HTTP StatusCode和Description,显示所有HTTP客户端看到的顶级HTTP层详细信息。StatusDescription通常很短,用于指示返回的错误类型,默认情况下是抛出的异常类型。HTTP客户端通常会检查StatusCode
以确定如何在客户端上处理错误。
所有服务客户端还可以访问错误响应DTO主体中返回的应用程序级错误详细信息,其中ErrorCode
包含异常类型,客户端将检查以确定和处理异常类型,同时ErrorMessage
保存服务器异常消息它提供了一个人性化的,更长和描述性的错误描述,可以显示给最终用户。在DebugMode中,StackTrace
使用Server StackTrace填充,以帮助前端开发人员识别错误的原因和位置。
如果错误引用特定字段(如字段验证异常),则GetFieldErrors()
保留每个具有错误的字段的错误信息。
可以通过以下各种选项更改这些默认值以提供进一步的自定义错误响应:
启用StackTraces
默认情况下,在响应DTO中显示StackTraces仅在调试版本中启用,尽管此行为可以通过以下方式覆盖:
SetConfig(new HostConfig { DebugMode = true });
错误响应类型
抛出异常时返回的错误响应取决于是否存在常规命名的{RequestDto}Response
DTO。
如果存在:
将{RequestDto}Response
返回,而不管服务方法的响应类型的。如果{RequestDto}Response
DTO具有ResponseStatus属性,则会填充它,否则将不返回ResponseStatus。(如果{ResponseDto}Response
使用[DataContract]/[DataMember]
属性修饰了类和属性,则还需要对ResponseStatus进行修饰以填充)。
否则:
通过ErrorResponse
填充的ResponseStatus属性返回泛型。
该服务客户端透明地处理不同的错误响应类型,并为无模式格式,如JSON / JSV /等有返回之间没有实际明显的区别ResponseStatus自定义或通用的ErrorResponse
-因为它们都输出电线上的同样的反应。
自定义例外
最终,所有ServiceStack WebServiceExceptions都只是Response DTO,其中包含一个填充的ResponseStatus,它返回HTTP错误状态。有多种不同的方法可以自定义异常的返回方式,包括:
自定义C#异常到HTTP错误状态的映射
您可以通过以下方式配置为不同的异常类型更改返回的HTTP错误状态:
SetConfig(new HostConfig {
MapExceptionToStatusCode = {
{ typeof(CustomInvalidRoleException), },
{ typeof(CustomerNotFoundException), },
}
});
返回HttpError
如果你想要对你的HTTP错误进行更细粒度的控制,你可以抛出或返回一个HttpError,让你自定义Http Headers和Status Code和HTTP Response Body,以便在线上获得你想要的内容:
public object Get(User request)
{
throw HttpError.NotFound("User {0} does not exist".Fmt(request.Name));
}
以上内容在线路上返回404 NotFound StatusCode,是以下方面的简写:
new HttpError(HttpStatusCode.NotFound,
"User {0} does not exist".Fmt(request.Name));
具有自定义响应DTO的HttpError
它HttpError
还可用于返回更结构化的错误响应:
var responseDto = new ErrorResponse {
ResponseStatus = new ResponseStatus {
ErrorCode = typeof(ArgumentException).Name,
Message = "Invalid Request",
Errors = new List<ResponseError> {
new ResponseError {
ErrorCode = "NotEmpty",
FieldName = "Company",
Message = "'Company' should not be empty."
}
}
}
}; throw new HttpError(HttpStatusCode.BadRequest, "ArgumentException") {
Response = responseDto,
};
实现IResponseStatusConvertible
您还可以通过实现IResponseStatusConvertible
接口来覆盖自定义异常的序列化,以返回您自己填充的ResponseStatus。这是ValidationException
允许通过让ValidationException实现IResponseStatusConvertible接口来自定义Response DTO 的方法。
例如,这是一个自定义的Exception示例,它返回填充的字段错误:
public class CustomFieldException : Exception, IResponseStatusConvertible
{
...
public string FieldErrorCode { get; set; }
public string FieldName { get; set; }
public string FieldMessage { get; set; } public ResponseStatus ToResponseStatus()
{
return new ResponseStatus {
ErrorCode = GetType().Name,
Message = Message,
Errors = new List<ResponseError> {
new ResponseError {
ErrorCode = FieldErrorCode,
FieldName = FieldName,
Message = FieldMessage
}
}
}
}
}
实现IHasStatusCode
除了使用IResponseStatusConvertible自定义C#Exceptions的HTTP Response Body之外,您还可以通过实现IHasStatusCode
以下内容来自定义HTTP状态代码:
public class Custom401Exception : Exception, IHasStatusCode
{
public int StatusCode
{
get { return ; }
}
}
同样IHasStatusDescription
可以用于自定义StatusDescription
和IHasErrorCode
自定义ErrorCode
返回的,而不是其异常类型。
覆盖AppHost中的OnExceptionTypeFilter
您还可以ResponseStatus
通过覆盖OnExceptionTypeFilter
AppHost 来捕获和修改返回的返回值,例如ServiceStack使用它来自定义返回的ResponseStatus以自动为ArgumentExceptions
指定的字段添加自定义字段错误ParamName
,例如:
public virtual void OnExceptionTypeFilter(
Exception ex, ResponseStatus responseStatus)
{
var argEx = ex as ArgumentException;
var isValidationSummaryEx = argEx is ValidationException;
if (argEx != null && !isValidationSummaryEx && argEx.ParamName != null)
{
var paramMsgIndex = argEx.Message.LastIndexOf("Parameter name:");
var errorMsg = paramMsgIndex >
? argEx.Message.Substring(, paramMsgIndex)
: argEx.Message; responseStatus.Errors.Add(new ResponseError
{
ErrorCode = ex.GetType().Name,
FieldName = argEx.ParamName,
Message = errorMsg,
});
}
}
自定义HTTP错误
在任何请求或响应过滤器中,您可以通过发出自定义HTTP响应并结束请求来短路请求管道,例如:
this.PreRequestFilters.Add((req,res) =>
{
if (req.PathInfo.StartsWith("/admin") &&
!req.GetSession().HasRole("Admin"))
{
res.StatusCode = (int)HttpStatusCode.Forbidden;
res.StatusDescription = "Requires Admin Role";
res.EndRequest();
}
});
在自定义HttpHandler中结束请求使用
res.EndHttpHandlerRequest()
后备错误页面
使用IAppHost.GlobalHtmlErrorHttpHandler
用于指定后备HttpHandler的所有错误状态代码,例如:
public override void Configure(Container container)
{
this.GlobalHtmlErrorHttpHandler = new RazorHandler("/oops"),
}
要获得更细粒度的控制,请使用IAppHost.CustomErrorHttpHandlers
指定自定义HttpHandler以与特定错误状态代码一起使用,例如:
public override void Configure(Container container)
{
this.CustomErrorHttpHandlers[HttpStatusCode.NotFound] =
new RazorHandler("/notfound");
this.CustomErrorHttpHandlers[HttpStatusCode.Unauthorized] =
new RazorHandler("/login");
}
注册处理服务异常的处理程序
ServiceStack及其API设计提供了一种拦截异常的灵活方法。如果你需要的所有服务异常的单一入口点,您可以将处理程序添加到AppHost.ServiceExceptionHandler
在Configure
。要处理服务之外发生的异常,您可以设置全局AppHost.UncaughtExceptionHandlers
处理程序,例如:
public override void Configure(Container container)
{
//Handle Exceptions occurring in Services: this.ServiceExceptionHandlers.Add((httpReq, request, exception) => {
//log your exceptions here
...
return null; //continue with default Error Handling //or return your own custom response
//return DtoUtils.CreateErrorResponse(request, exception);
}); //Handle Unhandled Exceptions occurring outside of Services
//E.g. Exceptions during Request binding or in filters:
this.UncaughtExceptionHandlers.Add((req, res, operationName, ex) => {
res.Write($"Error: {ex.GetType().Name}: {ex.Message}");
res.EndRequest(skipHeaders: true);
});
}
异步异常处理程序
如果您的处理程序需要进行任何异步调用,则可以使用Async版本:
this.ServiceExceptionHandlersAsync.Add(async (httpReq, request, ex) =>
{
await LogServiceExceptionAsync(httpReq, request, ex); if (ex is UnhandledException)
throw ex; if (request is IQueryDb)
return DtoUtils.CreateErrorResponse(request, new ArgumentException("AutoQuery request failed")); return null;
}); this.UncaughtExceptionHandlersAsync.Add(async (req, res, operationName, ex) =>
{
await res.WriteAsync($"UncaughtException '{ex.GetType().Name}' at '{req.PathInfo}'");
res.EndRequest(skipHeaders: true);
});
使用自定义ServiceRunner进行错误处理
如果您想为不同的操作和服务提供不同的错误处理程序,您可以告诉ServiceStack在您自己的自定义IServiceRunner中运行您的服务,并在AppHost中实现HandleExcepion事件挂钩:
public override IServiceRunner<TRequest> CreateServiceRunner<TRequest>(
ActionContext actionContext)
{
return new MyServiceRunner<TRequest>(this, actionContext);
}
其中MyServiceRunner就是实现你感兴趣的,如自定义挂钩的自定义类:
public class MyServiceRunner<T> : ServiceRunner<T>
{
public MyServiceRunner(IAppHost appHost, ActionContext actionContext)
: base(appHost, actionContext) {} public override object HandleException(IRequest request,
T request, Exception ex) {
// Called whenever an exception is thrown in your Services Action
}
}
ServiceStack 错误处理的更多相关文章
- 航空概论(历年资料,引之百度文库,PS:未调格式,有点乱)
航空航天尔雅 选择题1. 已经实现了<天方夜谭>中的飞毯设想.—— A——美国2. 地球到月球大约—— C 38 万公里3. 建立了航空史上第一条定期空中路线—— B——德国4. 对于孔明 ...
- ServiceStack.Redis订阅发布服务的调用(Z)
1.Redis订阅发布介绍Redis订阅发布是一种消息通信模式:发布者(publisher)发送消息,订阅者(Subscriber)接受消息.类似于设计模式中的观察者模式.发布者和订阅者之间使用频 ...
- 使用 ServiceStack 构建跨平台 Web 服务
本文主要来自MSDN杂志<Building Cross-Platform Web Services with ServiceStack>,Windows Communication Fou ...
- ServiceStack.Redis订阅发布服务的调用
1.Redis订阅发布介绍 Redis订阅发布是一种消息通信模式:发布者(publisher)发送消息,订阅者(Subscriber)接受消息.类似于设计模式中的观察者模式. 发布者和订阅者之间使用频 ...
- 采访ServiceStack的项目领导Demis Bellot——第1部分(网摘)
ServiceStack是一个开源的.支持.NET与Mono平台的REST Web Services框架.InfoQ有幸与Demis Bellot深入地讨论了这个项目.在这篇两部分报道的第1部分中,我 ...
- ServiceStack Web Service 创建与调用简单示列
目录 ServiceStack 概念 ServiceStack Web Service 创建与调用简单示列 上篇文章介绍了ServiceStack是什么,本章进入主题,如何快速简单的搭建Service ...
- 在使用Redis的客户端连接工具ServiceStack.Redis要注意的问题
在使用Redis的客户端连接工具ServiceStack.Redis要注意的问题 Redis是一个非常NB的内存级的数据库,我们可以把很多”热数据“(即读写非常多的数据)放入其中来操作,这样就减少 ...
- 采访ServiceStack的项目领导Demis Bellot——第1部分(转)
ServiceStack是一个开源的.支持.NET与Mono平台的REST Web Services框架.InfoQ有幸与Demis Bellot深入地讨论了这个项目.在这篇两部分报道的第1部分中,我 ...
- ServiceStack.Redis 破解
在github上下载了ServiceStack.Redis,做测试发现有限制,居然从v4开始就收费,无聊时,做了个源码分析 废话不多,上测试代码 try { ; i < ; i++) { red ...
随机推荐
- 高负载PHP调优
高负载PHP调优 针对PHP的Linux调优 调整文件描述符限制 # ulimit -n 1000000 # vi /etc/security/limits.conf # Setting Shell ...
- Git使用基础篇(zz)
Git使用基础篇 您的评价: 收藏该经验 Git是一个分布式的版本控制工具,本篇文章从介绍Git开始,重点在于介绍Git的基本命令和使用技巧,让你尝试使用Git的同时,体 ...
- 【C++】c++中栈 队列 的应用
C++中提供了STL模板statck 在使用的时候更为方便 除了一般的队列外 还有STL更有双向队列可以使用 #include<deque> 声明:deque <type > ...
- Socket 学习笔记 01 常用函数
常用方法 创建套接字: socket() 绑定本机端口: bind() 建立连接: connect(),accept() 侦听端口: listen() 数据传输: send() ...
- html--笔记day03
html--笔记day03 1.结构标记 1.<header>元素 <header></header> ==> <div id="header ...
- jsp里面不能使用${pageContext.request.contextPath}解决方案
1.在jsp中使用${pageContext.request.contextPath}获取相对路径,可是最后路径变为:http://localhost:8080/oneself/$%7BpageCon ...
- 2019.01.19 洛谷P2787 语文1(chin1)- 理理思维(ODT)
传送门 ODTODTODT水题. 题意:有一个字母序列,支持区间赋值,查询区间某个字母的数量,区间按字母序排序. 思路: 可以开262626棵线段树搞过去,然而也可以用ODTODTODT秒掉. 如果用 ...
- 2018.11.07 NOIP模拟 异或(数位dp)
传送门 对于每个二进制位单独考虑贡献. 然后对于两种情况分别统计. 对于第二种要用类似数位dpdpdp的方法来计算贡献. 代码
- Servlet WebSocket的简易聊天室
添加依赖 <!-- websocket --> <dependency> <groupId>javax.websocket</groupId> < ...
- Java 学习之集合类(Collections)
Collection(集合类) 我们是使用数组来保存数据,但是他的长度一旦创建,就已经确定了,当我们要动态传入穿值,数组就有些局限了,集合类就孕育而生:所谓集合,就是来保存,盛装数据,也可称为容器类: ...