2012-05-06 1 views
1

Je crée une application simple pour me connecter à un webservice, et j'ai des problèmes pour gérer les requêtes asynchrones.Comment retourner une HttpWebResponse asynchrone sans bloquer le thread principal?

Le problème est avec la fonction ProcessRequest, qui effectue fondamentalement un HttpWebRequest asynchrone, et renvoie le HttpWebResponse. Comme la requête est asynchrone, j'ai des problèmes avec la valeur de retour, et avec les fonctions appelant la méthode ProcessRequest et attendant l'objet HttpWebResponse. Par ailleurs, la requête en elle-même fonctionne parfaitement, déjà testé dans les fonctions, donc je n'ai pas besoin de retourner une réponse HttpWebResponse.

J'espère que je suis clair, comme je l'ai dit, c'est la première fois que je touche au développement de C#, .NET et Windows Phone. (Et WebRequests pour cette question)

Les erreurs que Visual Studio est de lancer sont:

1: Since 'System.AsyncCallback' returns void, a return keyword must not be followed by an object expression 
2: Cannot convert lambda expression to delegate type 'System.AsyncCallback' because some of the return types in the block are not implicitly convertible to the delegate return type  

Et ceci est le code:

namespace SimpleNoteConcept 
{ 
public sealed class SimpleNote 
{ 
    private static readonly SimpleNote _instance = new SimpleNote(); 
    private static string _authToken = string.Empty; 
    private static string _email = string.Empty; 
    private static string _authQsParams 
    { 
     get 
     { 
      if (string.IsNullOrEmpty(_authToken)) throw new SimpleNoteConceptAuthorisationException(); 
      return string.Format("auth={0}&email={1}", _authToken, _email); 
     } 
    } 

    private SimpleNote() { } 

    public static SimpleNote Instance 
    { 
     get { return _instance; } 
    } 

    public bool Connect(string email, string password) 
    { 
     try 
     { 
      StringParamCheck("email", email); 
      StringParamCheck("password", password); 

      var data = string.Format("email={0}&password={1}", email, password); 
      var bytes = Encoding.GetEncoding("utf-8").GetBytes(data); 
      data = Convert.ToBase64String(bytes); 

      using (var resp = ProcessRequest(loginPath, "POST", content: data)) 
      { 
       if (resp != null) 
       { 
        _authToken = resp.Cookies["auth"].Value; 
        _email = email; 
        System.Diagnostics.Debug.WriteLine("Connection established! -> " + _authToken); 
        return true; 
       } 
       return false; 
      } 
     } 
     catch (Exception) 
     { 
      throw; 
     } 

    } 

    public void GetIndex(int length = 100, string mark = null, DateTimeOffset? since = null) 
    { 
     try 
     { 
      string sinceString = null; 
      if (since.HasValue) 
       sinceString = Json.DateTimeEpochConverter.DateToSeconds(since.Value); 

      var queryParams = string.Format("{0}&length={1}&mark={2}&since={3}", _authQsParams, length, mark, sinceString); 
      using (var resp = ProcessRequest(indexPath, "GET", queryParams)) 
      { 
       var respContent = ReadResponseContent(resp); 
       System.Diagnostics.Debug.WriteLine("GetIndex: " + respContent.ToString()); 
       //var notes = JsonConvert.DeserializeObject<Objects.NoteEnumerable<T>>(respContent); 
       //return notes; 
      } 
     } 
     catch (WebException ex) 
     { 
      var resp = (HttpWebResponse)ex.Response; 
      switch (resp.StatusCode) 
      { 
       //401 
       case HttpStatusCode.Unauthorized: 
        throw new SimpleNoteConceptAuthorisationException(ex); 
       default: 
        throw; 
      } 
     } 
     catch (Exception) { throw; } 
    } 

    /// <summary> 
    /// Generic method to process a request to Simplenote. 
    /// All publicly expose methods which interact with the store are processed though this. 
    /// </summary> 
    /// <param name="requestPath">The path to the request to be processed</param> 
    /// <param name="method">The HTTP method for the request</param> 
    /// <param name="content">The content to send in the request</param> 
    /// <param name="queryParams">Queryparameters for the request</param> 
    /// <returns>An HttpWebResponse continaing details returned from Simplenote</returns> 
    private static HttpWebResponse ProcessRequest(string requestPath, string method, 
                string queryParams = null, string content = null) 
    { 
     try 
     { 
      var url = string.Format("{0}{1}{2}", "https://", domainPath, requestPath); 
      if (!string.IsNullOrEmpty(queryParams)) url += "?" + queryParams; 
      var request = WebRequest.Create(url) as HttpWebRequest; 
      request.CookieContainer = new CookieContainer(); 
      request.Method = method; 

      request.BeginGetRequestStream((e) => 
      { 
       using (Stream stream = request.EndGetRequestStream(e)) 
       { 
        // Write data to the request stream 
        var bytesBody = Encoding.GetEncoding("utf-8").GetBytes(content); 

        stream.Write(bytesBody, 0, bytesBody.Length); 
        stream.Close(); 
        stream.Dispose(); 
       } 
       request.BeginGetResponse((callback) => 
       { 
        try 
        { 
         HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(callback); 
         return response; 

        } 
        catch (WebException ex) 
        { 
         using (WebResponse Exresponse = ex.Response) 
         { 
          HttpWebResponse httpResponse = (HttpWebResponse)Exresponse; 
          System.Diagnostics.Debug.WriteLine("Error code: {0}", httpResponse.StatusCode); 
          using (Stream str = Exresponse.GetResponseStream()) 
          { 
           string text = new StreamReader(str).ReadToEnd(); 
           System.Diagnostics.Debug.WriteLine(text); 
          } 
         } 
        } 
        catch (Exception ex) 
        { 
         System.Diagnostics.Debug.WriteLine("Message: " + ex.Message); 
        } 
       }, request); 
      }, request); 

     } 
     catch (Exception) 
     { 
      throw; 
     } 
    } 

    /// <summary> 
    /// Reads the content from the response object 
    /// </summary> 
    /// <param name="resp">The response to be processed</param> 
    /// <returns>A string of the response content</returns> 
    private static string ReadResponseContent(HttpWebResponse resp) 
    { 
     if (resp == null) throw new ArgumentNullException("resp"); 
     using (var sr = new StreamReader(resp.GetResponseStream())) 
     { 
      return sr.ReadToEnd(); 
     } 
    } 

    /// <summary> 
    /// String parameter helper method. 
    /// Checks for null or empty, throws ArgumentNullException if true 
    /// </summary> 
    /// <param name="paramName">The name of the paramter being checked</param> 
    /// <param name="value">The value to check</param> 
    private void StringParamCheck(string paramName, string value) 
    { 
     if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(paramName, "Value must not be null or string.Empty"); 
    } 

} // Class End 

} // Namespace End 

Merci à l'avance!

+0

Votre méthode ProcessRequest se terminerait avant même que votre délégué s'exécute, n'est-ce pas? Je pense que vous devrez utiliser les nouvelles fonctions async/await dans C# 5, mais je ne les ai jamais utilisées personnellement et je ne suis pas sûr qu'elles soient disponibles dans WP7 7.1 ... http://www.codeproject.com/Articles/127291/C-5-0-vNext-Nouveau-Asynchronous-Pattern –

+1

Cela pourrait aider à simplifier le code asynchrone, mais parce que c'est toujours CTP je ne vais pas l'implémenter. Merci quand même. Et oui, il est disponible pour la plateforme WP7. – asendra

Répondre

1

Vous ne pouvez pas effectuer la programmation asynchrone de la même manière que la normale. Ici .Net exécute les parties asynchrones dans différents threads, comment peuvent-elles être communiquées? Donc, ce que vous pouvez faire est de passer un délégué avec votre méthode ProcessRequest. qui prendra un paramètre de HttpWebResponse.

Appelez votre méthode comme ceci:

Action<HttpWebResponse> actionDelegate = DoAfterGettingResponse; 
ProcessRequest(indexPath, "GET", actionDelegate, queryParams); 

Fonction qui gère la réponse

public static void DoAfterGettingResponse(HttpWebResponse resp) 
{ 
    if (resp != null) 
    { 
     _authToken = resp.Cookies["auth"].Value; 
     _email = email; 
     System.Diagnostics.Debug.WriteLine("Connection established! -> " + _authToken); 

    } 

    //Do anything else with the response 
}  

ProcessRequest aura la nouvelle signature.

private static HttpWebResponse ProcessRequest(
    string requestPath, string method, Action<HttpWebResponse> reponseDelegate, 
    string queryParams = null, string content = null) 
{ 
    //Do what you are already doing   
} 

Et l'endroit où vous êtes réponse retournerez, il suffit de faire

HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(callback); 
responseDelegate(response); 

Vous pouvez le faire de cette façon ou utiliser des événements, déclencher un événement qui passe HttpWebResponse comme paramètre et gérer la réponse à l'auditeur.

+0

Cela fonctionne vraiment, et c'est essentiellement ce que je pensais aussi. Passez un rappel à la méthode ProcessRequest et exécutez ce rappel lorsque la requête est terminée. Merci! En tout cas, si quelqu'un connaît une meilleure approche s'il vous plaît partager! – asendra

Questions connexes