Owin在MVC Web API中给出CancelationToken异常

时间:2018-04-26 07:27:25

标签: c# asp.net-web-api owin cancellation-token

环境:.NET 4.6.1,ASP.NET MVC 2,Microsoft.Owin

申请情况

  • Owin已配置为基于OAuth的身份验证(自己的数据库)。
  • 尚未添加授权属性 。下一步是

代码库

启动

    public void Configuration(IAppBuilder app)
    {
        OAuthConfig oAuthConfig = new OAuthConfig(app, AppConfiguration);
        oAuthConfig.ConfigureOAuthTokenGeneration();
        oAuthConfig.ConfigureOAuthTokenConsumption();

        WebApiConfig.Register(AppConfiguration);
        app.UseWebApi(AppConfiguration);

        // No further configuration is now allowed. 
        AppConfiguration.EnsureInitialized();
    }

的OAuthConfig

    public OAuthConfig(IAppBuilder app, HttpConfiguration HttpConfiguration)
    {
        this.app = app;
        if (OAuthConfig.HttpConfiguration == null)
            OAuthConfig.HttpConfiguration = HttpConfiguration;

        this.app.Use(async (ctx, next) =>
        {
            try
            {
                await next();
            }
            catch (OperationCanceledException)
            {
                // Eat this exception
            }
        });
    }

    public void ConfigureOAuthTokenGeneration()
    {
        var userStore = HttpConfiguration.DependencyResolver.GetService(typeof(IUserStore<ExtendedUser, string>)) as IUserStore<ExtendedUser, string>;
        UserService.UserStore = userStore;
        this.app.CreatePerOwinContext<UserService>(UserService.Create);
        this.app.CreatePerOwinContext<SignInService>(SignInService.Create);

        var issuer = ConfigurationManager.AppSettings["as:IssuerServer"];
        var tokenEndpoint = ConfigurationManager.AppSettings["as:OwinTokenEndpoint"];    // "/oauth/token"
        OAuthAuthorizationServerOptions oAuthServerOptions = new OAuthAuthorizationServerOptions()
        {
            ////For Dev enviroment only (on production should be AllowInsecureHttp = false)
            AllowInsecureHttp = true,
            TokenEndpointPath = new Microsoft.Owin.PathString(tokenEndpoint),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
            Provider = new CustomOAuthProvider(),
            AccessTokenFormat = new CustomJwtFormat(issuer)
        };

        // OAuth 2.0 Bearer Access Token Generation
        this.app.UseOAuthAuthorizationServer(oAuthServerOptions);
    }

    public void ConfigureOAuthTokenConsumption()  {
        string audienceId = ConfigurationManager.AppSettings["as:AudienceId"];
        var issuer = ConfigurationManager.AppSettings["as:IssuerServer"]; // Should have the Url of the auth server http://localhost:53025/";
        byte[] audienceSecret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["as:AudienceSecret"]);
        this.app.UseJwtBearerAuthentication(
            new JwtBearerAuthenticationOptions
            {
                AuthenticationMode = AuthenticationMode.Active,
                AllowedAudiences = new[] { audienceId },
                IssuerSecurityKeyProviders = new IIssuerSecurityKeyProvider[]
                {
                    new SymmetricKeyIssuerSecurityKeyProvider(issuer, audienceSecret)
                }
            });
    }

CancelledTaskHandler

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        HttpResponseMessage response = await base.SendAsync(request, cancellationToken);

        // Try to suppress response content when the cancellation token has fired; 
        //    ASP.NET will log to the Application event log if there's content in this case.
        if (cancellationToken.IsCancellationRequested)
        {
            return new HttpResponseMessage(HttpStatusCode.InternalServerError);
        }

        return response;
    }

WebApiConfig

    public static void Register(HttpConfiguration config)
    {
    // routes and other config
        // Handle CancellationToken which sometimes causes the app to hang due to orphaned responses
        config.MessageHandlers.Add(new CancelledTaskMessageHandler());
     }

正面临的问题

  • 在开发机器(本地测试)中,运行api并消耗该问题没有问题

  • 在部署时,Windows应用程序日志报告中存在错误,如下所示:

    Exception type: OperationCanceledException 
    Exception message: The operation was canceled.
    at System.Threading.CancellationToken.ThrowOperationCanceledException()
    at System.Web.Http.Owin.HttpMessageHandlerAdapter.<BufferResponseContentAsync>d__27.MoveNext()
    

问题

  • 不确定为什么Owin甚至在没有请求身份验证时才会启动, 只设置了处理程序。
  • 由此造成的对性能的影响?

已尝试

在Owin管道中吃掉异常(在OAuthConfig&#39;构造函数中)

        this.app.Use(async (ctx, next) =>
        {
            try
            {
                await next();
            }
            catch (OperationCanceledException)
            {
                // Eat this exception
            }
        });

虽然我们现在还没有启用授权,但想知道这是否可能会提出一些其他可能导致问题的设计问题。

如果需要,可以包含任何其他特定工件。 感谢

1 个答案:

答案 0 :(得分:2)

首先,让我简单介绍一下OWIN的工作原理。值得您阅读更多相关内容,但这是一个很好的起点。

将OWIN视为请求与应用程序之间的管道,在本例中是一个MVC Web Api。管道的每个部分称为“中间件”,请求流经此管道,直到它到达您的应用程序。当请求到达管道时,流程反转,数据流回管道。因此,当数据进入应用程序并离开应用程序时,此管道的每个部分(“中间件”)都会看到数据两次。中间件可以在请求进入时查看或修改请求,或在请求离开应用程序时查看或修改响应,或者两者兼而有之。

每个中间件组件都会收到一个字典,其中包含有关请求和响应的各种数据以及一个调用下一​​个管道的委托。

现在为您的代码:

您的应用程序是WebApi,由以下行定义:

app.UseWebApi(AppConfiguration);

您有两个使用以下行初始化的中间件:

    var issuer = ConfigurationManager.AppSettings["as:IssuerServer"];
    var tokenEndpoint = ConfigurationManager.AppSettings["as:OwinTokenEndpoint"];    // "/oauth/token"
    OAuthAuthorizationServerOptions oAuthServerOptions = new OAuthAuthorizationServerOptions()
    {
        ////For Dev enviroment only (on production should be AllowInsecureHttp = false)
        AllowInsecureHttp = true,
        TokenEndpointPath = new Microsoft.Owin.PathString(tokenEndpoint),
        AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
        Provider = new CustomOAuthProvider(),
        AccessTokenFormat = new CustomJwtFormat(issuer)
    };

    // OAuth 2.0 Bearer Access Token Generation
    this.app.UseOAuthAuthorizationServer(oAuthServerOptions);

然后这些行:

    string audienceId = ConfigurationManager.AppSettings["as:AudienceId"];
    var issuer = ConfigurationManager.AppSettings["as:IssuerServer"]; // Should have the Url of the auth server http://localhost:53025/";
    byte[] audienceSecret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["as:AudienceSecret"]);
    this.app.UseJwtBearerAuthentication(
        new JwtBearerAuthenticationOptions
        {
            AuthenticationMode = AuthenticationMode.Active,
            AllowedAudiences = new[] { audienceId },
            IssuerSecurityKeyProviders = new IIssuerSecurityKeyProvider[]
            {
                new SymmetricKeyIssuerSecurityKeyProvider(issuer, audienceSecret)
            }
        });

此中间件将始终运行。如果你在任何地方都有任何属性都没关系。

您说本地开发计算机上没有发生此错误,这意味着您很可能遇到配置问题。您正在上面引用的行中将设置传递到中间件。

基于此,我怀疑您对issuertokenEndpoint变量或(更有可能)audienceIdaudienceSecret变量存在问题。

希望有所帮助。