2016-04-29 41 views
1

獲取ASP.NET MVC5的WebAPI令牌失敗有時獲取ASP.NET MVC5的WebAPI令牌失敗有時

代碼

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(布爾 includeTaskCanceled例外)在 System.Threading.Tasks.Task 1.GetResultCore(Boolean waitCompletionNotification) at System.Threading.Tasks.Task 1.get_Result()

WebAPi登錄方法默認情況下沒有更改。

[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); 
     } 
    } 

任何線索?

+1

是不是超時? 60秒後?你爲什麼在任務上運行帖子?然後阻止您的調用線程等待任務完成? –

+0

@BrunoGarcia我猜60秒就可以獲得令牌。請建議一些考慮...謝謝! –

+0

我的意思是:這不是問題,因爲你的電話超時?是因爲它無法到達服務器,或者它需要很長時間(超過60秒)才能完成?特別是如果它有時只失敗 –

回答

6

確實很難說,但阻止HttpClient調用的方式無法幫助。 HttpClient是一個異步專用庫;你可能會陷入僵局。我建議擺脫所有.Result s和.Wait() s並使用async/await異步書寫所有內容。你的Task.Run沒有任何成就,所以應該去。

我知道這是從控制檯應用程序移植過來的Topshelf應用程序。我對Topshelf不是很熟悉,但我認爲,就像控制檯應用程序一樣,您需要阻止某處或者您的應用程序將退出。要做到這一點的地方是最高端 - 應用程序的入口點。

這證明了模式,你GetApiToken方法的重寫一起:

// 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; 
    } 
} 
相關問題