2015-08-27 24 views
21

Ho un progetto ASP.NET che prevede l'invio di richieste HTTP tramite il framework API Web. La seguente eccezione viene sollevata solo durante il debug:WebException su richiesta HTTP durante il debug

Il server ha commesso una violazione del protocollo. Section = ResponseStatusLine

Il progetto funziona perfettamente se "Avvia senza debug".

Come devo risolvere questa eccezione?

Qualsiasi aiuto è apprezzato!


Aggiornamento

Il problema sembra legato alla ASP.NET MVC Identity Framework.

per accedere ad altri metodi di Web-API, l'applicazione client deve POST prima un login richiesta (la richiesta di accesso non ha bisogno di essere ancora sicuro, e così io mando le corde username e password direttamente al Web -Punto POST API). Se commento la richiesta di accesso, non viene sollevata alcuna eccezione.

Qui di seguito sono i frammenti di codice in materia:

il metodo POST:

UserManager<ApplicationUser> UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())); 
AccountAccess ac = new AccountAccess(); 

public async Task<HttpResponseMessage> Post() 
{ 
    string result = await Request.Content.ReadAsStringAsync(); 
    LoginMessage msg = JsonConvert.DeserializeObject<LoginMessage>(result); 
    HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK); 
    var user = UserManager.Find(msg.username, msg.password); 
    if (user == null) 
     return response; 
    if (user.Roles == null) 
     return response; 
    var role = from r in user.Roles where (r.RoleId == "1" || r.RoleId == "2") select r; 
    if (role.Count() == 0) 
    { 
     return response; 
    } 
    bool task = await ac.LoginAsync(msg.username, msg.password); 
    response.Content = new StringContent(task.ToString()); 
    return response; 
} 

La classe account di accesso (simulando l'AccountController default nel modello MVC):

public class AccountAccess 
{ 
    public static bool success = false; 
    public AccountAccess() 
     : this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()))) 
    { 
    } 

    public AccountAccess(UserManager<ApplicationUser> userManager) 
    { 
     UserManager = userManager; 
    } 

    public UserManager<ApplicationUser> UserManager { get; private set; } 

    public async Task<bool> LoginAsync(string username, string password) 
    { 
     var user = await UserManager.FindAsync(username, password); 
     if (user != null) 
     { 
      await SignInAsync(user, isPersistent: false); 
      return true; 
     } 
     else 
     { 
      return false; 
     } 
    } 
    ~AccountAccess() 
    { 
     if (UserManager != null) 
     { 
      UserManager.Dispose(); 
      UserManager = null; 
     } 
    } 

    private IAuthenticationManager AuthenticationManager 
    { 
     get 
     { 
      return HttpContext.Current.GetOwinContext().Authentication; 
     } 
    } 

    private async Task SignInAsync(ApplicationUser user, bool isPersistent) 
    { 
     AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie); 
     var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie); 
     AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity); 
    } 
} 

Qui di seguito sono i frammenti importanti di codice:

In applicazione client:

public static async Task<List<T>> getItemAsync<T>(string urlAction) 
{ 
    message = new HttpRequestMessage(); 
    message.Method = HttpMethod.Get; 
    message.RequestUri = new Uri(urlBase + urlAction); 
    HttpResponseMessage response = await client.SendAsync(message); 
    string result = await response.Content.ReadAsStringAsync(); 
    List<T> msgs = JsonConvert.DeserializeObject<List<T>>(result); 
    return msgs; 
} 

Nel controller Web-API:

public HttpResponseMessage Get(string id) 
{ 
    HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK); 
    if (id == "ItemA") 
    { 
     List<ItemAMessage> msgs = new List<ItemAMessage>(); 

     // some code... 

     response.Content = new StringContent(JsonConvert.SerializeObject(msgs)); 
    } 
    else if (id == "ItemB") 
    { 
     List<ItemBMessage> msgs = new List<ItemBMessage>(); 

     // some code... 

     response.Content = new StringContent(JsonConvert.SerializeObject(msgs)); 
    } 
    return response; 
} 

alcune osservazioni che ho:

  1. I t ho pensato che potrei aver bisogno di inviare la richiesta in modo asincrono (con la sintassi async-await), ma l'eccezione persiste in questo modo.
  2. Se passo il codice, la richiesta fa immette il metodo HTTP, ma il codice si interrompe in corrispondenza della riga casuale (Perché ?!) prima di restituire la risposta, quindi presumo che non venga inviata alcuna risposta.
  3. Ho provato le seguenti soluzioni, come suggerito in risposte a domande simili, nessuno dei quali lavora per me:
    • Impostazione useUnsafeHeaderParsing a true
    • Aggiunta l'intestazione Keep-Alive: false
    • Cambiare l'impostazione della porta di Skype (non ho Skype, e la porta 80 e 443 non sono occupati)

Ulteriori informazioni rmazioni, nel caso in cui la materia:

  • Mac OS con sistema operativo Windows 8.1 con VMware Fusion
  • Visual Studio 2013
  • .NET Framework 4.5
  • IIS Express Server

Aggiornamento 2

L'eccezione è stata risolta, ma non sono sicuro di quale modifica abbia funzionato. Per quanto ne so, uno o entrambi i seguenti fisso è:

  • Ho un metodo checkConnection(), che invia in pratica una richiesta GET e tornare vero in caso di successo. Ho aggiunto await al metodo HttpClient.SendAsync() e applicato async fino a.
  • Ho ritirato tutto il codice nel costruttore MainWindow, ad eccezione del metodo InitializeComponent(), nel gestore eventi Inizializzazione finestra.

Qualche idea?

Questi sono codice relativo alle modifiche illustrate sopra:

metodo checkConnectionAsync:

public static async Task<bool> checkConnectionAsync() 
{ 
    message = new HttpRequestMessage(); 
    message.Method = HttpMethod.Get; 
    message.RequestUri = new Uri(urlBase); 
    try 
    { 
     HttpResponseMessage response = await client.SendAsync(message); 
     return (response.IsSuccessStatusCode); 
    } 
    catch (AggregateException) 
    { 
     return false; 
    } 
} 

Finestra gestore eventi inizializzata (retratto dal costruttore MainWindow):

private async void Window_Initialized(object sender, EventArgs e) 
{ 
    if (await checkConnectionAsync()) 
    { 
     await loggingIn(); 
     getItemA(); 
     getItemB(); 
    } 
    else 
    { 
     logMsg.Content = "Connection Lost. Restart GUI and try again."; 
    } 
} 

Update 3

Anche se questo può essere un po 'off-topic, vorrei aggiungere una nota a margine in caso qualcuno altro cade in questo - ho utilizzato il metodo di autenticazione sbagliato per Web-API iniziare con. Il modello di progetto Web-API dispone già di un framework Identity incorporato e in qualche modo "lo sostituisco" con un approccio piuttosto semplice ma interrotto ...

This video è un bel tutorial per iniziare.

This article fornisce una spiegazione più completa.

+1

Cosa succede se si rimuove la chiamata a riposo in LoginAsync? –

+0

@Brianfromstatefarm In realtà ho aggiunto questa riga per testare qualcos'altro e ho dimenticato di rimuoverlo durante la pubblicazione del codice. Grazie per aver segnalato! –

+0

È possibile che la VM stia aggiungendo al problema? –

risposta

6

Nell'applicazione client non si è in attesa di task. Accedere ai risultati senza attendere può causare errori imprevedibili. Se fallisce solo durante la modalità Debug, non posso dirlo con certezza, ma certamente non è lo stesso programma (aggiunte verifiche aggiuntive, in genere le ottimizzazioni non sono abilitate). Indipendentemente da quando è attivo Debugging, se si ha un errore di codice, è necessario correggerlo e dovrebbe funzionare in entrambe le modalità.

Quindi, rendere la funzione asincrona e chiamare l'attività con il modificatore await o chiamare task.WaitAndUnwrapException() sull'attività in modo che blocchi in modo sincrono finché il risultato non viene restituito dal server.

+0

In realtà ho entrambe le versioni (con e senza attesa) del metodo getItem, se è quella a cui ti riferisci. Sfortunatamente, l'attesa e l'assicurazione "async fino in fondo/su" non risolve l'eccezione che sto ottenendo. Grazie per aver segnalato comunque! Ho modificato il codice per evitare confusione. –

+0

Ho assegnato il premio a questa risposta poiché indica la causa più probabile del mio problema. –

1

Assicurarsi che l'URL abbia una stringa di query ID con valore come Articolo A o Articolo B. Altrimenti, non si restituirà alcun contenuto con il codice di stato Http 200 che potrebbe portare alla violazione del protocollo.

1

Quando si utilizza SendAsync, è necessario fornire personalmente tutte le intestazioni dei messaggi pertinenti, ad esempio message.Headers.Authorization = new AuthenticationHeaderValue("Basic", token);.
È possibile utilizzare invece GetAsync (e chiamare un metodo get specifico sul server).
Inoltre, sei sicuro che l'eccezione sia stata risolta? Se si dispone di un metodo di livello elevato async che restituisce un valore Task e non vuoto, tale eccezione potrebbe essere ignorata in modo silenzioso.

+0

Potresti approfondire la tua ultima affermazione? Come affermato nel secondo aggiornamento, ho modificato il codice in modo che fosse "asincrono fino in fondo", con il livello più alto (i gestori di eventi) che restituiva "void". Ogni altro metodo asincrono restituisce 'Task'. –

+0

Per quanto riguarda l'altro punto, dovrei utilizzare GetAsync con richieste GET (o HEAD) e SendAsync per il resto? –

+0

Volevo assicurarmi che in effetti non ci siano gestori di eventi che restituiscono "Task" o altri compiti che non sono attesi. –