[英]ServiceStack intercepting request/response for validation
我正在嘗試在ServiceRunner中攔截請求和響應以對它們運行驗證。
1.如果(!result.IsValid),或者如果這完全是正確的方法,我不確定如何中止請求。
2.特別是對於響應,直到動態運行時對象響應才是未知的,這使我很難為其創建IValidator,而在編譯時需要已知類型。
請查看代碼中的注釋:
public class CustomServiceRunner<T> : ServiceRunner<T> {
public CustomServiceRunner(IAppHost appHost, ActionContext actionContext)
: base(appHost, actionContext) { }
public override void BeforeEachRequest(IRequestContext requestContext, T request) {
var validator = AppHost.TryResolve<IValidator<T>>();
var result = validator.Validate(request);
//----------------------
//if (!result.IsValid)
// How to return result.ToResponseDto() and abort the request?
//----------------------
base.BeforeEachRequest(requestContext, request);
}
public override object AfterEachRequest(IRequestContext requestContext, T request, object response) {
//-----------------------
//Validating against response presents a more challenging issue
//I may have multiple response classes and returned response is
//a type object, response class is to be decided dynamically at
//runtime. I am not sure how to fit this to IValidator<T>
//-----------------------
return base.AfterEachRequest(requestContext, request, response);
}
}
在交互方法中進行驗證將使我的Service類代碼更加簡潔。 我也考慮過請求/響應過濾器,但是隨后我需要在各處編寫過濾器標簽。 ServiceRunner更好,因為它使整個驗證過程(或一般來說是AOP)對其余服務透明。
我不了解為什么使用RequestFilter不夠用。 如https://github.com/ServiceStack/ServiceStack/wiki/Validation中所述 ,請在AppHost.Configure()中執行以下操作:
// Enable the validation feature
Plugins.Add(new ValidationFeature());
// This method scans the assembly for validators
container.RegisterValidators(Assemblies);
這將設置一個RequestFilter進行驗證,並將注冊在指定程序集中定義的所有驗證器。 如果發現任何驗證錯誤,RequestFilter將引發異常,而不是調用您的服務。
與ServiceStack可以為您完成的工作相比,您所做的所有布線對我來說都是多余的(而且很脆弱)。
這就是說,如果我遵循請求后的驗證,我就不會這樣做。 我猜您正在嘗試保證服務不會返回無效結果,而不是保證請求無效時無法執行。 在我看來,這似乎是一個極端的情況,您不信任自己的數據存儲庫,而您想要完全失敗,甚至無法看到壞數據(這可能使用戶難以修復)。
我還沒有使用響應過濾器,所以我不確定是否有限制。 但是看看https://github.com/ServiceStack/ServiceStack/wiki/Request-and-response-filters ,似乎表明您可以做同樣的事情...所以聽起來您可以寫一個不同的響應做響應流,然后關閉它。 我收集到這意味着響應過濾器在服務之后但在序列化到響應流之前執行。 因此,您應該能夠寫出不同的響應,並且該服務的響應將被忽略。 我在如何從ServiceStack RequestFilter查找服務中發布的代碼段可能會有所幫助。
如果您有錯誤要從驗證中返回,那么就像您在做的那樣,您可以引發異常或直接編寫錯誤響應。 不過,請看一下ServiceStack提供的代碼:下載項目並在ValidationFilter.cs中借用/修改RequestFilter代碼。 如果有幫助,以下是當前的實現:
public void RequestFilter(IHttpRequest req, IHttpResponse res, object requestDto)
{
IValidator validator = ValidatorCache.GetValidator(req, requestDto.GetType());
if (validator == null)
return;
IRequiresHttpRequest requiresHttpRequest = validator as IRequiresHttpRequest;
if (requiresHttpRequest != null)
requiresHttpRequest.HttpRequest = req;
string httpMethod = req.HttpMethod;
ValidationResult result = validator.Validate(new ValidationContext(requestDto, (PropertyChain) null, (IValidatorSelector) new MultiRuleSetValidatorSelector(new string[1]
{
httpMethod
})));
if (result.IsValid)
return;
object errorResponse = DtoUtils.CreateErrorResponse(requestDto, ValidationResultExtensions.ToErrorResult(result));
ServiceStack.WebHost.Endpoints.Extensions.HttpResponseExtensions.WriteToResponse(res, req, errorResponse);
}
響應過濾器應該看起來類似於請求過濾器,但是您必須安裝自己的過濾器。 為此,您將需要實現自己的IPlugin。 一種簡單的方法就是從https://github.com/ServiceStack/ServiceStack/ServiceStack/blob/master/src/ServiceStack.ServiceInterface/Validation/ValidationFeature.cs復制/粘貼/編輯現有的ValidationFeature.cs。 (該類具有一些私有元素,因此對於子類而言,它不是最優的,否則我會建議這樣做。)
您將需要進行的主要更改將是注冊您自己的過濾器:
/// <summary>
/// Activate the validation mechanism, so every request DTO with an existing validator
/// will be validated.
/// </summary>
/// <param name="appHost">The app host</param>
public void Register(IAppHost appHost)
{
if (Enabled) return;
Enabled = true;
// use my class instead of ServiceStack.ServiceInterface.Validation.ValidationFilters
var filter = new MyValidationFilters();
appHost.RequestFilters.Add(filter.RequestFilter);
appHost.ResponseFilters.Add(filter.RequestFilter);
}
然后,您可以創建自己的MyValidationFilters類。 在這里,您可以從ServiceStack.ServiceInterface.Validation.ValidationFilters派生它,如果有意義的話,請使用它們的RequestFilter。 但是您的ResponseFilter可能需要與RequestFilter稍有不同,因為它通過了Response DTO而不是Request DTO。 請注意RequestFilter中的以下代碼段:
object errorResponse = DtoUtils.CreateErrorResponse(requestDto, ValidationResultExtensions.ToErrorResult(result));
這段代碼無法正常工作,因為ServiceStack會嘗試獲取requestDto,構造一個適當的Response DTO,並進行填充,正如您在DtoUtils代碼中所看到的:
public static object CreateErrorResponse(object request, ValidationErrorResult validationError)
{
ResponseStatus responseStatus = DtoUtils.ToResponseStatus(validationError);
return DtoUtils.CreateErrorResponse(request, (Exception) new ValidationError(validationError), responseStatus);
}
public static object CreateErrorResponse(object request, Exception ex, ResponseStatus responseStatus)
{
object responseDto = DtoUtils.CreateResponseDto(request, responseStatus);
IHttpError httpError = ex as IHttpError;
if (httpError != null)
{
if (responseDto != null)
httpError.Response = responseDto;
return (object) httpError;
}
else
{
string errorCode = ex.GetType().Name;
string errorMessage = ex.Message;
if (responseStatus != null)
{
errorCode = responseStatus.ErrorCode ?? errorCode;
errorMessage = responseStatus.Message ?? errorMessage;
}
return (object) new HttpError(responseDto, HttpRequestExtensions.ToStatusCode(ex), errorCode, errorMessage);
}
}
相反,您將需要繞過CreateResponseDto部分(因為您已經在ResponseFilter中擁有Response DTO),然后進行其余工作。
請注意,通過更改ServiceStack可以避免上述所有復制/粘貼。 您可以自己重構ServiceStack代碼以避免重復,然后將請求請求提交到github。
(僅修復了100多個if語句,由於代碼標簽而不想在注釋中放置)
在AfterEachRequest中,使用動態調用:
dynamic dynamicResponse = response;
IValidator validator = TryResolveValidator(response); // magic happens here
// ... your error handling code ...
public IValidator<T> TryResolveValidator<T>(T response)
{
return AppHost.TryResolve<IValidator<T>>();
}
我讓這個工作很丑陋 。 該否則,如果在我的陳述 AfterEachRequest()
是不是很好 :
public class CustomServiceRunner<T> : ServiceRunner<T> {
public CustomServiceRunner(IAppHost appHost, ActionContext actionContext) : base(appHost, actionContext) { }
//1.
public override void BeforeEachRequest(IRequestContext requestContext, T request) {
var validator = AppHost.TryResolve<IValidator<T>>();
if (validator != null) {
var result = validator.Validate(request);
if (!result.IsValid) throw result.ToException();
}
base.BeforeEachRequest(requestContext, request);
}
//2.
public override object AfterEachRequest(IRequestContext requestContext, T request, object response) {
IValidator validator = null;
if(response.GetType()==typeof(CustomersResponse)) validator= AppHost.TryResolve<IValidator<CustomersResponse>>();
else if(response.GetType()==typeof(OrdersResponse)) validator= AppHost.TryResolve<IValidator<OrdersResponse>>();
else if(response.GetType()==typeof(LoginResponse)) validator= AppHost.TryResolve<IValidator<LoginResponse>>();
//......
// and 100+ more 'ELSE IF' statements o_O ??
//......
if (validator != null) {
var result = validator.Validate(response);
if (!result.IsValid) throw result.ToException();
}
return base.AfterEachRequest(requestContext, request, response);
}
}
:P〜仍然希望Mythz或有經驗的人可以幫助我改善解決方案。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.