0

주어진 [Web API] 작업 메서드에 전달 된 지정된 문자열 매개 변수의 길이를 검사하는 ActionFilter를 작성했으며 길이가 올바르지 않으면 ActionContext.Response를 HttpStatusCode.BadRequest로 설정합니다 (actionContext.Request.CreateErrorResponse())를 호출하지만 여전히 액션 메소드 코드에서 끝나고있다. 이것은 기본적으로 사람들이 Action 메서드 외부에서 ModelState의 유효성 검사를 처리하기 위해 만드는 모든 ActionFilterAttribute 클래스처럼 작동하도록되어 있지만 로거를 사용할 수 있도록 Dependency Injection도 필요합니다. 내 Attribute/ActionFilter 시험 할 만하다.OnActionExecuting() 메서드에서 ActionContext.Response를 BadRequest로 설정하지 않아서 호출자에게 바로 반환되는 이유는 무엇입니까?

"수동 속성"(Attrib은 기본적으로 DTO 임)과 해당 속성의 동작을 구현하는 '검색'ActionFilter를 갖는 방법을 설명하는 블로그 게시물이 내 검색에 나타납니다. https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=98

내가 겪고있는 문제는 다음과 같습니다.

나는했습니다

(죄송합니다 여러분, 나는 C#을 수년 간의 경험을 많이 가지고 있지만, 이것이 나의 첫번째 진짜 특성 (들에 forray) 및/또는 ActionFilter (들)입니다. 나와 함께 곰하시기 바랍니다) (애트리뷰트가 DTO 인 경우) Passive로 내 애트리뷰트를 작성하고, 위의 블로그 포스트 쇼에서 예제로 IActionFilter < CheckStringParamLengthAttribute>를 상속하는 ActionFilter를 작성했다.

여기 내 속성 코드입니다.

[AttributeUsage(AttributeTargets.Method, Inherited = true)] 
public class CheckStringParamLengthAttribute : Attribute 
{ 
    private int _minLength; 
    private int _maxLength; 
    private string _errorMessage; 
    private string _paramName; 

    public CheckStringParamLengthAttribute(
     int MinimumLength, 
     string parameterName, 
     string ErrorMessage = "", 
     int MaximumLength = 0) 
    { 
     if (MinimumLength < 0 || MinimumLength > int.MaxValue) 
     { 
      throw new ArgumentException("MinimumLength parameter value out of range."); 
     } 
     _minLength = MinimumLength; 

     if (string.IsNullOrEmpty(parameterName)) 
     { 
      throw new ArgumentException("parameterName is null or empty."); 
     } 
     _paramName = parameterName; 

     // these two have defaults, so no Guard check needed. 
     _maxLength = MaximumLength; 
     _errorMessage = ErrorMessage; 
    } 

    public int MinLength { get { return _minLength; } } 
    public int MaxLength { get { return _maxLength; } } 
    public string ErrorMessage { get { return _errorMessage; } } 
    public string ParameterName { get { return _paramName; } } 
} 

및 IActionFilter 선언. 내 ActionFilter는 '오류 응답'에 ActionContext.Response을 설정하면서 실현 될 때까지

public interface IActionFilter<TAttribute> where TAttribute : Attribute 
{ 
    void OnActionExecuting(TAttribute attr, HttpActionContext ctx); 
} 

모든

actionContext.Response = actionContext.Request.CreateErrorResponse(
    HttpStatusCode.BadRequest, "my error msg"); 

그것은 다시 호출자에게 대신 BadRequest 말했다 반환하지 않는 것 ... 잘 보였다 마치 필터가 실행되지 않은 것처럼 액션 메소드의 코드에서 끝납니다.

여기 내 ActionFilter/'행동'코드의 핵심입니다.

public class CheckStringParamLengthActionFilter : IActionFilter<CheckStringParamLengthAttribute> 
{ 
    ... 
    public void OnActionExecuting(
     CheckStringParamLengthAttribute attribute, 
     HttpActionContext actionContext) 
    { 
     Debug.WriteLine("OnActionExecuting (Parameter Name being checked: " + attribute.ParameterName + ")"); 

     // get the attribute from the method specified in the ActionContext, if there. 
     var attr = this.GetStringLengthAttribute(
      actionContext.ActionDescriptor); 

     if (actionContext.ActionArguments.Count < 1) { 
      throw new Exception("Invalid number of ActionArguments detected."); 
     } 

     var kvp = actionContext.ActionArguments 
      .Where(k => k.Key.Equals(attr.ParameterName, StringComparison.InvariantCulture)) 
      .First(); 
     var paramName = kvp.Key; 
     var stringToCheck = kvp.Value as string; 
     string errorMsg; 

     if (stringToCheck.Length < attr.MinLength) { 
      errorMsg = string.IsNullOrEmpty(attr.ErrorMessage) 
       ? string.Format(
        "The {0} parameter must be at least {1} characters in length.", 
        paramName, attr.MinLength) 
       : attr.ErrorMessage; 

      // SEE HERE 
      actionContext.Response = actionContext.Request.CreateErrorResponse(
       HttpStatusCode.BadRequest, errorMsg); 
      actionContext.Response.ReasonPhrase += " (" + errorMsg + ")"; 

      return; 
     } 
     ... 
    } 
    ... 
} 

여기 생각

protected void Application_Start() 
{ 
    // DI container spin up. (Simple Injector) 
    var container = new Container(); 
    container.Options.DefaultScopedLifestyle = new WebApiRequestLifestyle(); 

    container.Register<ILogger, Logger>(Lifestyle.Scoped); 

    container.RegisterWebApiControllers(GlobalConfiguration.Configuration); 

    GlobalConfiguration.Configuration.Filters.Add(
     new ActionFilterDispatcher(container.GetAllInstances)); 

    container.RegisterCollection(typeof(IActionFilter<>), typeof(IActionFilter<>).Assembly); 

    container.Verify(); 

    GlobalConfiguration.Configuration.DependencyResolver = 
     new SimpleInjectorWebApiDependencyResolver(container); 

    // the rest of this is 'normal' Web API registration stuff. 
    AreaRegistration.RegisterAllAreas(); 
    GlobalConfiguration.Configure(WebApiConfig.Register); 
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 
    RouteConfig.RegisterRoutes(RouteTable.Routes); 
    BundleConfig.RegisterBundles(BundleTable.Bundles); 
} 

단순 인젝터 등록 코드를 나타내는 (Global.asax.cs에서)를 위해 Application_Start() 법 등의 그 I 단순히에 actionContext.Response 설정하면 'ErrorResponse'는 '잘못된 요청'이 호출자에게 되돌려 보내지고 내 속성이 배치 된 작업 메소드는 실행되지 않습니다. 실망스럽게도 그렇지 않습니다.

그래서 잘못된 요청을 처리 방법에 들어 가지 않고 발신자에게 곧바로 보내려면 무엇을 놓치고 있습니까? 아니면 그 일이 가능할까요?

푸시가 발생하면 컨트롤러에 다른 '서비스 계층'클래스 인스턴스를 주입 할 수 있으며 문자열 매개 변수 길이 유효성 검사기를 호출해야하는 각 동작 메서드에 코드가 있습니다. 적어도 내가 시작했을 때, 더 낫고, 더 깨끗한 방법이었습니다.

업데이트 : Well dang me! 나는 가장 중요한 부분을 분명히 잊었다.

나는 아래 내용을 잘 알고 있기 때문에 이것을 안다.

한편, Global.asax.cs의 Application_Start() 메소드에 등록 된 ActionFilterDispatcher은 다음과 같습니다. 예 :

protected void Application_Start() 
{ 
    ... 
    GlobalConfiguration.Configuration.Filters.Add(
     new ActionFilterDispatcher(container.GetAllInstances)); 
    ... 
} 

등록 된 ActionFilter (s)는이 클래스의 ExecuteActionFilterAsync() 메서드에서 호출됩니다. 이 일이 중요합니다. 위해 신용 어디 예정이다, [efnet에 #을 asp.net 채널에서] 좋은 매우 도움이 개발자 신용을 제공하기에

public sealed class ActionFilterDispatcher : IActionFilter 
{ 
    private readonly Func<Type, IEnumerable> container; 

    public ActionFilterDispatcher(Func<Type, IEnumerable> container) 
    { 
     this.container = container; 
    } 

    public Task<HttpResponseMessage> ExecuteActionFilterAsync(
     HttpActionContext context, 
     CancellationToken cancellationToken, 
     Func<Task<HttpResponseMessage>> continuation) 
    { 
     var descriptor = context.ActionDescriptor; 
     var attributes = descriptor.ControllerDescriptor.GetCustomAttributes<Attribute>(true) 
      .Concat(descriptor.GetCustomAttributes<Attribute>(true)); 

     foreach (var attribute in attributes) 
     { 
      Type filterType = typeof(IActionFilter<>).MakeGenericType(attribute.GetType()); 
      IEnumerable filters = this.container.Invoke(filterType); 

      foreach (dynamic actionFilter in filters) 
      { 
       actionFilter.OnActionExecuting((dynamic)attribute, context); 
      } 
     } 

     return continuation(); 
    } 

    public bool AllowMultiple { get { return true; } } 
} 

답변

0

나에게이 문제에 대한 답을 주었다.

ActionFilter가이 클래스의 ExecuteActionFilterAsync() 메서드에서 호출되었으므로 매우 간단한 if 문을 추가하여 HttpActionContext.Response 개체가 채워 졌는지 확인하고 필요하면 바로 종료해야합니다. 그런 다음 생성 된 응답을 호출자에게 다시 보냅니다.

다음은 고정 된 방법입니다.

public sealed class ActionFilterDispatcher : IActionFilter 
{ 
    ... 

    public Task<HttpResponseMessage> ExecuteActionFilterAsync(
     HttpActionContext context, 
     CancellationToken cancellationToken, 
     Func<Task<HttpResponseMessage>> continuation) 
    { 
     var descriptor = context.ActionDescriptor; 
     var attributes = descriptor.ControllerDescriptor.GetCustomAttributes<Attribute>(true) 
      .Concat(descriptor.GetCustomAttributes<Attribute>(true)); 

     foreach (var attribute in attributes) 
     { 
      Type filterType = typeof(IActionFilter<>).MakeGenericType(attribute.GetType()); 
      IEnumerable filters = this.container.Invoke(filterType); 

      foreach (dynamic actionFilter in filters) 
      { 
       actionFilter.OnActionExecuting((dynamic)attribute, context); 

       // ADDED THIS in order to send my BadRequest response right 
       // back to the caller [of the Web API endpoint] 
       if (context.Response != null) 
       { 
        return Task.FromResult(context.Response); 
       } 
      } 
     } 

     return continuation(); 
    } 
    ... 
} 
관련 문제