9

根据我的一篇文章,我能够进行批处理......直到某一点。除了注册路由特定的处理程序,我还有 2 个委托处理程序

  1. 验证用户
  2. 日志记录

批处理处理程序通过委托处理程序对用户进行身份验证并记录请求。当 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 修复了一个问题并引入了另一个问题。我的角色提供者得到一个空引用异常。现在正在调查。

4

2 回答 2

18

该博客文章正确识别了问题,但如果您使用StartuporOwinStartup类配置 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)创建了一个新HttpServer的批处理除了HttpServer处理 HTTP 请求的主要部分;并且两个HttpServers 都使用相同的HttpConfiguration.

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

如果您没有配置任何委托处理程序,那么这个问题不会对您造成影响 - 您可以拥有 2 个HttpServer使用相同HttpConfiguration.

但是,如果您有任何显式或隐式配置的委托处理程序(例如,通过启用 Web API Tracing),则 Web API 无法构建第二个管道 - 委托处理程序已经链接到第一个管道中 - 并且在第一个管道上引发此异常请求第 2 次HttpServer

这个例外绝对应该更清楚地知道发生了什么。更好的是,这个问题甚至不应该是可能的——配置应该是配置,而不是单独的处理程序。配置可以是委托处理程序的工厂。但我离题了...

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

  1. 如果您使用的是 OWIN,请将HttpServer您在批处理处理程序中使用的相同内容通过UseWebApi(this IAppBuilder builder, HttpServer httpServer);
  2. 如果您使用的是 IIS + Web API(没有 OWIN 启动类),请传递GlobalConfiguration.DefaultServer给您的批处理处理程序,以避免创建新的HttpServer

这是一个示例 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()
        {
            // ...
        }
    }

}
于 2015-07-16T04:27:16.990 回答
1

我在没有批处理的情况下遇到了这个错误。我自己做了一个HttpClientFactory,它包含一个HandlerFactory,也是我自己的。

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

每当System.Net.Http.HttpClientFactory.Create(...)工厂需要制作新的HttpClient.

但它只适用于单个调用,因为处理程序本身会被 .NET 代码改变,使它们处于无法重用的状态。

我改变了我的构造函数,使它不会预先创建处理程序,而是每次都创建。现在可以了。

于 2016-01-27T18:18:28.210 回答