1

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);
    }
}

相互作用するメソッドで検証を行うと、サービスクラスのコードがよりクリーンになります。要求/応答フィルターについても考えましたが、フィルタータグをどこにでも書き込む必要があります。ServiceRunnerは、検証プロセス全体(または一般的にはAOP)を他のサービスに対して透過的にするため、より優れています。

4

3 に答える 3

5

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を見ると、ほぼ同じことができることが示されているようです...したがって、別の応答を記述できるようです。応答ストリームを実行してから閉じます。これは、応答フィルターがサービスの後、応答ストリームへのシリアル化が発生する前に実行されることを意味します。したがって、別の応答を書き出すことができるはずであり、サービスの応答は無視されます。ServiceStackRequestFilterからサービスを見つける方法に投稿したコードスニペットが役立つ場合があります。

検証から返すエラーがある場合は、実行しているのと同じように、例外をスローするか、エラー応答を直接書き込むことができます。ただし、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を実装する必要があります。これを行う簡単な方法の1つは、既存のValidationFeature.csをhttps://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.ServiceInterface/Validation/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はRequestDTOではなくResponseDTOに渡されるため、RequestFilterとは微妙に異なる必要がある場合があります。RequestFilterからの次のスニペットに注意してください。

  object errorResponse = DtoUtils.CreateErrorResponse(requestDto, ValidationResultExtensions.ToErrorResult(result));

DtoUtilsコードに示されているように、ServiceStackはrequestDtoを取得し、適切なResponse DTOを構築してデータを入力しようとするため、このコードは正しく機能しません。

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に送信することができます。

于 2013-03-10T15:48:11.147 に答える
1

(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>>();
}
于 2015-01-02T16:36:29.870 に答える
0

私はこれを醜い方法で機能させます。私のelseifステートメントは良く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または経験豊富な誰かが私が解決策を改善するのを助けることができることをまだ望んでいます。

于 2013-03-09T11:19:55.680 に答える