2012-05-06 7 views
1

Я делаю простое приложение для подключения к веб-сервису, и у меня возникают проблемы с управлением асинхронными запросами.Как вернуть async HttpWebResponse без блокировки основного потока?

Проблема заключается в функции ProcessRequest, которая в основном делает асинхронный HttpWebRequest и возвращает HttpWebResponse. Поскольку запрос является асинхронным, у меня возникают проблемы с возвращаемым значением и с функциями, вызывающими метод ProcessRequest и ожидающими объект HttpWebResponse.

Кстати, запрос сам по себе отлично работает, уже проверен внутри функций, поэтому мне не нужно возвращать HttpWebResponse.

Надеюсь, я проясняю, как я уже сказал, это мой первый раз, когда-либо касающийся разработки C#, .NET и Windows Phone. (И WebRequests по этому вопросу)

Ошибки, которые Visual Studio является метание являются:

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  

И это код:

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 

Заранее спасибо!

+0

Ваш метод ProcessRequest завершится до того, как ваш делегат выполнит выполнение, верно? Я думаю, вам нужно будет использовать новые функции async/await на C# 5, но я никогда лично не использовал их самостоятельно и не уверен, что они доступны в WP7 7.1 ... http://www.codeproject.com/Статьи/127291/C-5-0-vNext-New-Asynchronous-Pattern –

+1

Это может помочь упростить асинхронный код, но поскольку он по-прежнему является CTP, я не собираюсь его внедрять. Спасибо, в любом случае. И да, он доступен для платформы WP7. – asendra

ответ

1

Вы не можете выполнять асинхронное программирование так же, как и в обычном режиме. Здесь .Net запускает асинхронные части в разных потоках, как их можно обменивать? Итак, что вы можете сделать, это передать делегат с помощью метода ProcessRequest. который примет параметр HttpWebResponse.

Вызов ваш метод, как это:

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

Функция, которая обрабатывает ответ

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 будет иметь новую подпись.

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

И место, где вы возвращаете ответ, просто сделать

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

Вы можете сделать это так или использовать событие, огнь события проходящего HttpWebResponse в качестве параметра и обработать ответ слушателя.

+0

Это определенно работает, и это в основном то, о чем я тоже думал. Передайте обратный вызов методу ProcessRequest и выполните этот обратный вызов, когда запрос завершен. Благодаря! В любом случае, если кто-то знает лучший подход, пожалуйста, поделитесь! – asendra

Смежные вопросы