有时获取 ASP.NET MVC5 WebAPI 令牌失败 [英] Get ASP.NET MVC5 WebAPI token fails sometimes

查看:38
本文介绍了有时获取 ASP.NET MVC5 WebAPI 令牌失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

获取 ASP.NET MVC5 WebAPI 令牌有时会失败

Get ASP.NET MVC5 WebAPI token fails sometimes

代码

string GetAPITokenSync(string username, string password, string apiBaseUri)
        {
            var token = string.Empty;

            using (var client = new HttpClient())
            {
                client.BaseAddress = new Uri(apiBaseUri);
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                client.Timeout = TimeSpan.FromSeconds(60);  

                //setup login data
                var formContent = new FormUrlEncodedContent(new[]
                {
                 new KeyValuePair<string, string>("grant_type", "password"),
                 new KeyValuePair<string, string>("username", username),
                 new KeyValuePair<string, string>("password", password),
                 });

                //send request               
                Task t = Task.Run(() =>
                {
                    HttpResponseMessage responseMessage = client.PostAsync("/Token", formContent).Result;
                    var responseJson = responseMessage.Content.ReadAsStringAsync().Result;
                    var jObject = JObject.Parse(responseJson);
                    token = jObject.GetValue("access_token").ToString();
                });

                t.Wait();
                t.Dispose();
                t = null;
                GC.Collect();

                return token;
            }
        }

错误

发生了一个或多个错误.---> System.AggregateException: 一或发生了更多错误.--->System.Threading.Tasks.TaskCanceledException:任务被取消.
--- 内部异常堆栈跟踪结束 --- 在 System.Threading.Tasks.Task.ThrowIfExceptional(BooleanincludeTaskCanceled Exceptions) 在System.Threading.Tasks.Task1.GetResultCore(Boolean等待完成通知)在System.Threading.Tasks.Task1.get_Result()

One or more errors occurred. ---> System.AggregateException: One or more errors occurred. ---> System.Threading.Tasks.TaskCanceledException: A task was canceled.
--- End of inner exception stack trace --- at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceled Exceptions) at System.Threading.Tasks.Task1.GetResultCore(Boolean waitCompletionNotification) at System.Threading.Tasks.Task1.get_Result()

WebAPI 登录方式默认没有变化.

WebAPi login method is by default has no changes.

[HttpPost]
[AllowAnonymous]
[Route("Login")]
public HttpResponseMessage Login(string username, string password)
    {
        try
        {
            var identityUser = UserManager.Find(username, password);

            if (identityUser != null)
            {
                var identity = new ClaimsIdentity(Startup.OAuthOptions.AuthenticationType);
                identity.AddClaim(new Claim(ClaimTypes.Name, username));

                AuthenticationTicket ticket = new AuthenticationTicket(identity, new AuthenticationProperties());
                var currentUtc = new SystemClock().UtcNow;
                ticket.Properties.IssuedUtc = currentUtc;
                ticket.Properties.ExpiresUtc = currentUtc.Add(TimeSpan.FromMinutes(1440));

                var token = Startup.OAuthOptions.AccessTokenFormat.Protect(ticket);

                var response = new HttpResponseMessage(HttpStatusCode.OK)
                {
                    Content = new ObjectContent<object>(new
                    {
                        UserName = username,
                        ExternalAccessToken = token
                    }, Configuration.Formatters.JsonFormatter)
                };

                return response;


            }
        }
        catch (Exception)
        {
        }

        return new HttpResponseMessage(HttpStatusCode.BadRequest);
    }
}

启动类默认没有变化

 public partial class Startup
    {
        public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }

        public static string PublicClientId { get; private set; }


        public void ConfigureAuth(IAppBuilder app)
        {
            // Configure the db context and user manager to use a single instance per request
            app.CreatePerOwinContext(ApplicationDbContext.Create);
            app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);

            // Enable the application to use a cookie to store information for the signed in user
            // and to use a cookie to temporarily store information about a user logging in with a third party login provider
            app.UseCookieAuthentication(new CookieAuthenticationOptions());
            app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

            // Configure the application for OAuth based flow
            PublicClientId = "self";
            OAuthOptions = new OAuthAuthorizationServerOptions
            {
                TokenEndpointPath = new PathString("/Token"),
                Provider = new ApplicationOAuthProvider(PublicClientId),
                AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
                AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
                // In production mode set AllowInsecureHttp = false
                AllowInsecureHttp = true
            };

            // Enable the application to use bearer tokens to authenticate users
            app.UseOAuthBearerTokens(OAuthOptions);
        }
    }

有什么线索吗?

推荐答案

很难确定,但是您阻止 HttpClient 调用的方式无济于事.HttpClient 是一个仅异步的库;您可能会遇到死锁情况.我建议摆脱所有 .Result.Wait() 并使用 async/await 异步编写所有内容.而你的 Task.Run 一事无成,所以应该去.

It's hard to say for certain, but the way you're blocking HttpClient calls can't be helping. HttpClient is an async-only library; you may have a deadlock situation. I suggest getting rid of all .Results and .Wait()s and write everything asynchronously, using async/await. And your Task.Run is achieving nothing, so that should go.

我知道这是从控制台应用程序移植过来的 Topshelf 应用程序.我对 Topshelf 不是很熟悉,但我认为,就像控制台应用程序一样,您需要阻止某处,否则您的应用程序将直接退出.执行此操作的位置位于最顶部 - 应用程序的入口点.

I understand this is Topshelf app ported over from a console app. I'm not very familiar with Topshelf, but I assume that, like console apps, you need to block somewhere or your app will simply exit. The place to do that is at the very top - the entry point of the app.

这演示了该模式,并重写了您的 GetApiToken 方法:

This demonstrates the pattern, along with a re-write of your GetApiToken method:

// app entry point - the only place you should block
void Main()
{
    MainAsync().Wait();
}

// the "real" starting point of your app logic. do everything async from here on
async Task MainAsync()
{
    ...
    var token = await GetApiTokenAsync(username, password, apiBaseUri);
    ...
}

async Task<string> GetApiTokenAsync(string username, string password, string apiBaseUri)
{
    var token = string.Empty;

    using (var client = new HttpClient())
    {
        client.BaseAddress = new Uri(apiBaseUri);
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        client.Timeout = TimeSpan.FromSeconds(60);  

        //setup login data
        var formContent = new FormUrlEncodedContent(new[]
        {
         new KeyValuePair<string, string>("grant_type", "password"),
         new KeyValuePair<string, string>("username", username),
         new KeyValuePair<string, string>("password", password),
         });

        //send request               
        HttpResponseMessage responseMessage = await client.PostAsync("/Token", formContent);
        var responseJson = await responseMessage.Content.ReadAsStringAsync();
        var jObject = JObject.Parse(responseJson);
        token = jObject.GetValue("access_token").ToString();

        return token;
    }
}

这篇关于有时获取 ASP.NET MVC5 WebAPI 令牌失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆