Оуэн в MVC Web API дает исключение CancelationToken - PullRequest
0 голосов
/ 26 апреля 2018

Среда : .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()
    

Вопрос

  • Не уверен, почему Оуэн даже активируется, когда не запрашивается аутентификация, только обработчикибыли установлены.
  • Какое-либо влияние на производительность из-за этого?

Уже предприняты попытки

Исключение покушения в конвейере Оуэна (в пределахКонструктор OAuthConfig)

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

Хотя у нас сейчас не включена Авторизация, но интересно, может ли это привести к другим проблемам с дизайном, которые могутпривести к проблемам позже.

При необходимости может быть включен любой другой конкретный артефакт.Спасибо

1 Ответ

0 голосов
/ 01 мая 2018

Позвольте мне начать с краткого описания того, как работает 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)
            }
        });

Это промежуточное ПО всегда будет работать.Не имеет значения, есть ли у вас какие-либо атрибуты.

Вы сказали, что ошибка не возникает на локальных машинах разработки, что означает, что у вас, скорее всего, есть проблема с конфигурацией.Вы передаете настройки в ваше промежуточное ПО в указанных выше строках.

Исходя из этого, я подозреваю, что у вас проблема с переменными issuer или tokenEndpoint или (что более вероятно) с audienceId илиaudienceSecret переменных.

Надеюсь, это поможет.

...