2016-04-29 21 views
1

ASP.NET Erhalten MVC5 WebAPI Token kam es manchmal nichtGet ASP.NET MVC5 WebAPI Token versagt manchmal

-Code

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

Fehler

ein oder mehrere Fehler. ---> System.AggregateException: Ein oder mehr Fehler aufgetreten. ---> System.Threading.Tasks.TaskCanceledException: Eine Aufgabe wurde abgebrochen.
--- Ende der Ausnahmestapelüberwachung --- bei System.Threading.Tasks.Task.ThrowIfExceptional (Boolean includeTaskCanceled Ausnahmen) bei System.Threading.Tasks.Task 1.GetResultCore(Boolean waitCompletionNotification) at System.Threading.Tasks.Task 1.get_Result()

Die WebAPi-Anmeldemethode hat standardmäßig keine Änderungen.

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

Startup Klasse ist nicht standardmäßig ändert

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

Jeder Hinweis?

+1

es ist nicht ein Timeout? nach 60 Sekunden? Warum führen Sie den Post bei einer Aufgabe aus? Dann blockieren Sie den aufrufenden Thread, der darauf wartet, dass die Aufgabe beendet wird? –

+0

@BrunoGarcia Ich denke, 60 Sekunden ist ok, Token zu bekommen. Bitte berate etwas, das du in Betracht ziehen solltest ... Danke! –

+0

Ich meine: Ist das nicht das Problem, weil Ihr Anruf Timeout ist? Entweder weil es den Server nicht erreichen kann oder es zu lange dauert (über 60 Sekunden), um es zu beenden? Besonders, wenn es manchmal scheitert nur –

Antwort

6

Es ist schwer zu sagen, aber die Art, wie Sie HttpClient-Anrufe blockieren, kann nicht helfen. HttpClient ist eine asynchrone Bibliothek; Sie haben möglicherweise eine Deadlock-Situation. Ich schlage vor, alle .Result s und .Wait() s loszuwerden und alles asynchron zu schreiben, unter Verwendung async/await. Und deine Task.Run erreicht nichts, also sollte das gehen.

Ich verstehe, dass dies Topshelf-App über eine Konsolen-App portiert ist. Topshelf ist mir nicht sehr vertraut, aber ich gehe davon aus, dass Sie wie Konsolen-Apps irgendwo blockieren müssen oder Ihre App einfach beendet wird. Der Ort dafür ist ganz oben - der Einstiegspunkt der App.

Dies zeigt das Muster, zusammen mit einem Umschreiben Ihrer GetApiToken Methode:

// 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; 
    } 
}