我没有理解为什么使用 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/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 提交拉取请求。