【问题标题】:ServiceStack InProcessServiceGateway throws ValidationExceptionServiceStack InProcessServiceGateway 抛出 ValidationException
【发布时间】:2017-07-11 16:12:27
【问题描述】:

我正在尝试根据Service Gateway 教程实现服务网关模式,以支持通过 InProcessServiceGateway 进行进程内处理和通过 JsonServiceClient 进行外部调用,以防 ServiceStack 服务独立部署。我使用的是 ServiceStack 4.5.8 版本。

验证功能可以正常工作,但是使用 InProcessServiceGateway,失败的验证会引发 ValidationException,这会直接导致客户端中的 ServiceStack.FluentValidation.ValidationException,而不是填充 MyResponseDto 的 ResponseStatus 属性。而且我还尝试了 GlobalRequestFilters 和 ServiceExceptionHandlers,它们似乎都可以正常工作,仅使用 JsonHttpClient 但使用 InProcessServiceGateway 捕获 ValidationException。

有什么方法可以让 InProcessServiceGateway 抛出的 ValidationException 被捕获并转化为 Dto 的 ResponseStatus?谢谢。

我的应用主机:

   //Register CustomServiceGatewayFactory
   container.Register<IServiceGatewayFactory>(x => new CustomServiceGatewayFactory()).ReusedWithin(ReuseScope.None);

   //Validation feature
   Plugins.Add(new ValidationFeature());

   //Note: InProcessServiceGateway cannot reach here.
   GlobalRequestFilters.Add((httpReq, httpRes, requestDto) =>
   {
     ...
   });

   //Note: InProcessServiceGateway cannot reach here.
   ServiceExceptionHandlers.Add((httpReq, request, ex) =>
   {
     ...
   });

我的 CustomServiceGatewayFactory:

public class CustomServiceGatewayFactory : ServiceGatewayFactoryBase
{
    private IRequest _req;

    public override IServiceGateway GetServiceGateway(IRequest request)
    {
        _req = request;

        return base.GetServiceGateway(request);
    }

    public override IServiceGateway GetGateway(Type requestType)
    {
        var standaloneHosted = false;
        var apiBaseUrl = string.Empty;

        var apiSettings = _req.TryResolve<ApiSettings>();
        if (apiSettings != null)
        {
            apiBaseUrl = apiSettings.ApiBaseUrl;
            standaloneHosted = apiSettings.StandaloneHosted;
        }

        var gateway = !standaloneHosted
            ? (IServiceGateway)base.localGateway
            : new JsonServiceClient(apiBaseUrl)
            {
                BearerToken = _req.GetBearerToken()
            };
        return gateway;
    }
}

我的客户端控制器(ASP.NET Web API):

    public virtual IServiceGateway ApiGateway
    {
        get
        {
            var serviceGatewayFactory = HostContext.AppHost.TryResolve<IServiceGatewayFactory>();
            var serviceGateway = serviceGatewayFactory.GetServiceGateway(HttpContext.Request.ToRequest());
            return serviceGateway;
        }
    }

我的客户端控制器操作(ASP.NET Web API):

    var response = ApiGateway.Send<UpdateCustomerResponse>(new UpdateCustomer
    {
        CustomerGuid = customerGuid,
        MobilePhoneNumber = mobilePhoneNumber 
        ValidationCode = validationCode
    });

    if (!response.Success)
    {
        return this.Error(response, response.ResponseStatus.Message);
    }

我的 UpdateCustomer 请求 DTO:

[Route("/customers/{CustomerGuid}", "PUT")]
public class UpdateCustomer : IPut, IReturn<UpdateCustomerResponse>
{
    public Guid CustomerGuid { get; set; }

    public string MobilePhoneNumber { get; set; }

    public string ValidationCode { get; set; }
}

我的 UpdateCustomerValidator:

public class UpdateCustomerValidator : AbstractValidator<UpdateCustomer>
{
    public UpdateCustomerValidator(ILocalizationService localizationService)
    {
        ValidatorOptions.CascadeMode = CascadeMode.StopOnFirstFailure;

        RuleFor(x => x.ValidationCode)
            .NotEmpty()
            .When(x => !string.IsNullOrWhiteSpace(x.MobilePhoneNumber))
            .WithErrorCode(((int)ErrorCode.CUSTOMER_VALIDATIONCODE_EMPTY).ToString())
            .WithMessage(ErrorCode.CUSTOMER_VALIDATIONCODE_EMPTY.GetLocalizedEnum(localizationService, Constants.LANGUAGE_ID));
    }
}

我的 UpdateCustomerResponse DTO:

public class UpdateCustomerResponse
{
    /// <summary>
    /// Return true if successful; return false, if any error occurs.
    /// </summary>
    public bool Success { get; set; }

    /// <summary>
    /// Represents error details, populated only when any error occurs.
    /// </summary>
    public ResponseStatus ResponseStatus { get; set; }
}

ServiceStack 4.5.8 的 InProcessServiceGateway 源码:

private TResponse ExecSync<TResponse>(object request)
{
    foreach (var filter in HostContext.AppHost.GatewayRequestFilters)
    {
        filter(req, request);
        if (req.Response.IsClosed)
            return default(TResponse);
    }

    if (HostContext.HasPlugin<ValidationFeature>())
    {
        var validator = ValidatorCache.GetValidator(req, request.GetType());
        if (validator != null)
        {
            var ruleSet = (string)(req.GetItem(Keywords.InvokeVerb) ?? req.Verb);
            var result = validator.Validate(new ValidationContext(
                request, null, new MultiRuleSetValidatorSelector(ruleSet)) {
                Request = req
            });
            if (!result.IsValid)
                throw new ValidationException(result.Errors);
        }
    }

    var response = HostContext.ServiceController.Execute(request, req);
    var responseTask = response as Task;
    if (responseTask != null)
        response = responseTask.GetResult();

    return ConvertToResponse<TResponse>(response);
}

【问题讨论】:

    标签: servicestack validationexception


    【解决方案1】:

    ServiceStack 的服务网关现在将验证异常从 this commit 转换为 WebServiceExceptions,从 v4.5.13 开始提供,现在是 available on MyGet。

    【讨论】:

    • 谢谢你,@mythz,:),你总是给出一个迅速而明智的答案。我迫不及待地想试试你的解决方案。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-10-20
    • 1970-01-01
    • 2020-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多