拋出C#異常 在大多數情況下,您不需要關心ServiceStack的錯誤處理,因為它為拋出C#異常的正常用例提供本機支持,例如: HTTP錯誤C#異常的預設映射 預設C#例外: ArgumentException使用HTTP StatusCode為400 BadRequest返回繼承 NotImpl ...
拋出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), 403 }, { typeof(CustomerNotFoundException), 404 }, } });
返回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 401; } } }
同樣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 > 0 ? argEx.Message.Substring(0, 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 } }