webapi批处理和委托处理程序

时间:2012-08-17 15:27:08

标签: asp.net-web-api batch-processing

基于我的last post我能够让批处理工作......直到某一点。除了注册路由特定处理程序之外,我还有2个委派处理程序

  1. 验证用户
  2. 登录
  3. 批处理程序遍历委派处理程序,对用户进行身份验证并记录请求。当messagehandlerinvoker开始发送子/嵌套请求时,抛出以下异常。

    System.ArgumentException was unhandled by user code
      HResult=-2147024809
      Message=The 'DelegatingHandler' list is invalid because the property 'InnerHandler' of 'AuthenticationMessageHandler' is not null.
    Parameter name: handlers
      Source=System.Net.Http.Formatting
      ParamName=handlers
      StackTrace:
           at System.Net.Http.HttpClientFactory.CreatePipeline(HttpMessageHandler innerHandler, IEnumerable`1 handlers)
           at System.Web.Http.HttpServer.Initialize()
           at System.Web.Http.HttpServer.<EnsureInitialized>b__3()
           at System.Threading.LazyInitializer.EnsureInitializedCore[T](T& target, Boolean& initialized, Object& syncLock, Func`1 valueFactory)
           at System.Threading.LazyInitializer.EnsureInitialized[T](T& target, Boolean& initialized, Object& syncLock, Func`1 valueFactory)
           at System.Web.Http.HttpServer.EnsureInitialized()
           at System.Web.Http.HttpServer.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
           at System.Net.Http.HttpMessageInvoker.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
           at RoutingRequest.Service.Startup.BatchMessageHandler.<>c__DisplayClassd.<PrcoessRequest>b__b(Task`1 m) in C:\CEI\Clients\Footlocker.com\FL - Vendor Routing Portal\source\RoutingRequest.Service\Startup\BatchMessageHandler.cs:line 45
           at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()
           at System.Threading.Tasks.Task.Execute()
      InnerException: 
    

    是否有我缺少的配置选项,还是我需要绕过委托处理程序?

    修改 这是我的身份验证处理程序。

    public class AuthenticationMessageHandler
        : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            SetCurrentUser(request);
            return base.SendAsync(request, cancellationToken);
        }
    
        private void SetCurrentUser(HttpRequestMessage request)
        {
            var values = new List<string>().AsEnumerable();
            if (request.Headers.TryGetValues("routingrequest-username", out values) == false) return;
    
            var username = values.First();
    
            var user = Membership.GetUser(username, true);
            if (user == null)
            {
                var message = string.Format("membership information for '{0}' could not be found.", username);
                throw new HttpRequestException(message);
            }
    
            var roles = Roles.GetRolesForUser(username);
    
            Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(user.UserName), roles);
        }
    }
    

    根据Kiran的回答,子类的httpserver修复了一个问题并引入了另一个问题。我的角色提供程序正在获取空引用异常。现在正在调查。

3 个答案:

答案 0 :(得分:13)

该博客文章正确识别问题,但如果您使用StartupOwinStartup类配置OWIN,则有一个更简单的解决方案:

更改OWIN配置调用 UseWebApi(this IAppBuilder builder, HttpConfiguration configuration);UseWebApi(this IAppBuilder builder, HttpServer httpServer); 以便您的批处理程序和OWIN管道使用相同的HttpServer实例。

这样做的根本原因是许多批处理文章/示例(例如http://bradwilson.typepad.com/blog/2012/06/batching-handler-for-web-api.html)除了处理HTTP请求的主HttpServer之外还为批处理创建了新的HttpServer ;并且HttpServer都使用相同的HttpConfiguration

当第一次接收请求时初始化每个HttpServer时,它会通过反转所有已配置的委托处理程序(例如,跟踪处理程序或其他代理类型)来创建处理程序管道(在HttpClientFactory.CreatePipeline中)处理程序),并使用Web API调度程序终止管道。

如果您没有配置任何委派处理程序,那么此问题不会让您感到困惑 - 您可以拥有使用相同HttpServer的2个HttpConfiguration个对象。

但是如果你有任何明确或隐式配置的委托处理程序(例如enabling Web API Tracing),那么Web API就无法构建第二个管道 - 委托处理程序已经在第一个管道中链接了 - 这个例外是第二次HttpServer的第一次请求时抛出。

这个例外应该更清楚地说明发生了什么。更好的是,这个问题甚至不可能 - 配置应该是配置,而不是单个处理程序。配置可以是委派处理程序的工厂。但我离题了......

虽然这个问题有点难以理解,但还有一个非常简单的方法:

  1. 如果您正在使用OWIN,请通过HttpServer
  2. 将批处理程序中使用的UseWebApi(this IAppBuilder builder, HttpServer httpServer);传递给OWIN管道
  3. 如果您使用的是IIS + Web API(没有OWIN Startup类),请将GlobalConfiguration.DefaultServer传递给批处理程序,以避免创建新的HttpServer
  4. 这是一个示例OWIN启动类,它创建一个HttpServer并将其传递给批处理程序和Web API。此示例用于OData批处理程序:

    [assembly: OwinStartup(typeof(My.Web.OwinStartup))]
    namespace My.Web
    {
    
        /// <summary>
        /// OWIN webapp configuration.
        /// </summary>
        public sealed class OwinStartup
        {
    
            /// <summary>
            /// Configure all the OWIN modules that participate in each request.
            /// </summary>
            /// <param name="app">The OWIN appBuilder</param>
            public void Configuration(IAppBuilder app)
            {
                HttpConfiguration webApiConfig = new HttpConfiguration();
                webApiConfig.MapHttpAttributeRoutes();
    
                HttpServer webApiServer = new HttpServer(webApiConfig);
    
                // Configure batch handler
                var batchHandler = new DefaultODataBatchHandler(webApiServer);
                webApiConfig.Routes.MapODataServiceRoute("ODataRoute",
                                                         "odata",
                                                         BuildEdmModel(),
                                                         new DefaultODataPathHandler(),
                                                         ODataRoutingConventions.CreateDefault(),
                                                         batchHandler);
    
                app.UseWebApi(webApiServer);
            }
    
            private EdmModel BuildEdmModel()
            {
                // ...
            }
        }
    
    }
    

答案 1 :(得分:5)

答案 2 :(得分:1)

我没有批处理就遇到了这个错误。我创建了HttpClientFactory我自己的HandlerFactory,也是我自己的。{/ p>

它在构造函数中调用HandlerFactory.Create()方法并存储它所生成的处理程序。

只要工厂需要制作新的System.Net.Http.HttpClientFactory.Create(...),就会将这些传递给HttpClient方法。

但它只适用于单个调用,因为处理程序本身会被.NET代码变异,使它们处于一种状态,这意味着它们无法重用。

我改变了我的构造函数,以便它不会在前面创建处理程序,但每次都是这样。它现在有效。