2016-03-23 2 views
1

Мой вопрос: Как это сделать?Преобразование содержимого HttpResponseMessage в объект

Итак, я ничего не трогал .Net через 6 лет до этой недели. Я забыл и многое другое, что я никогда не знал, и, хотя мне нравится идея ключевых слов async/wait, у меня небольшая проблема, реализующая следующие требования для реализации API-интерфейса клиента:

  1. Класс ServerAPI имеет метод для каждого из методов API, используя соответствующие входные параметры (например, метод Login принимает в id и password, вызывает вызов API и возвращает результат вызывающему абоненту).
  2. Я хочу абстрагироваться от JSON, так что мои методы API возвращают сам объект вы выборка (например, метод Login выше возвращает User объекта с токеном аутентификации, UID и т.д.)
  3. Некоторых методов API вернуться 204 (без значимого в моей работе), возможно, я только забочусь об успехе/неудаче), для этого я хотел бы вернуть либо bool (true = успех), либо код состояния.
  4. Я хотел бы сохранить асинхронный/ожидаемый (или эквивалентный) дизайн, потому что он действительно работает до сих пор.
  5. Для некоторых методов мне может потребоваться просто вернуть объект HttpResponseMessage и позволить вызывающему абоненту справиться с ним.

Это примерно то, что у меня есть до сих пор, и я не уверен, как сделать его совместимым с вышеизложенным. Или я даже делаю это правильно. Любое руководство ценится (пламя, однако, нет).

// 200 (+User JSON) = success, otherwise APIError JSON 
internal async Task<User> Login (string id, string password) 
{ 
    LoginPayload payload = new LoginPayload() { LoginId = id, Password = password}; 
    var request = NewRequest(HttpMethod.Post, "login"); 
    JsonPayload<LoginPayload>(payload, ref request); 

    return await Execute<Account>(request, false); 
} 

// 204: success, anything else failure 
internal async Task<Boolean> LogOut() 
{ 
    return await Execute<Boolean>(NewRequest(HttpMethod.Delete, "login"), true); 
} 

internal async Task<HttpResponseMessage> GetRawResponse() 
{ 
    return await Execute<HttpResponseMessage>(NewRequest(HttpMethod.Get, "raw/something"), true); 
} 

internal async Task<Int32> GetMeStatusCode() 
{ 
    return await Execute<Int32>(NewRequest(HttpMethod.Get, "some/intstatus"), true); 
} 

private async Task<RESULT> Execute<RESULT>(HttpRequestMessage request, bool authenticate) 
{ 
    if (authenticate) 
     AuthenticateRequest(ref request); // add auth token to request 

    var tcs = new TaskCompletionSource<RESULT>(); 
    var response = await client.SendAsync(request);  

    // TODO: If the RESULT is just HTTPResponseMessage, the rest is unnecessary   

    if (response.IsSuccessStatusCode) 
    { 
     try 
     { 
      // TryParse needs to handle Boolean differently than other types 
      RESULT result = await TryParse<RESULT>(response); 
      tcs.SetResult(result); 
     } 
     catch (Exception e) 
     { 
      tcs.SetException(e); 
     } 

    } 
    else 
    { 
     try 
     { 
      APIError error = await TryParse<APIError>(response); 
      tcs.SetException(new APIException(error)); 
     } 
     catch (Exception e) 
     { 
      tcs.SetException(new APIException("Unknown error")); 
     } 
    } 

    return tcs.Task.Result; 
} 

Это APIError JSON структуры (это код состояния + пользовательский код ошибки).

{ 
    "status": 404, 
    "code":216, 
    "msg":"User not found" 
} 

Я предпочел бы остаться с System.Net, но это в основном потому, что я не хочу, чтобы переключить все мой код более. Если то, что я хочу, проще сделать другими способами, то это, безусловно, стоит дополнительной работы.

Спасибо.

+0

* Мой вопрос: ** Как мне это сделать? *** Я бы сказал * Обзор кода * для кода, который вы считаете полным, а не кода, который вы не знаете, как завершить. – copolii

+0

Попробуйте изучить '[JsonProperty (« myPropertyName »)]', это может вам помочь. – Lifes

+0

Итак, вы имеете в виду, что вы уже выполнили весь проект UWP и протестировали его работу, но хотите знать, соответствуют ли эти коды вышеуказанным требованиям, и если кто-то знает лучший способ реализовать эти требования? –

ответ

0

Итак, первый к адресу вам нужно Newtonsoft.Json комментарии, я действительно не чувствовал потребность еще.Я нашел встроенную поддержку хорошо работать до сих пор (с использованием APIError JSon в моем оригинальный вопрос:

[DataContract] 
internal class APIError 
{ 
    [DataMember (Name = "status")] 
    public int StatusCode { get; set; } 
    [DataMember (Name = "code")] 
    public int ErrorCode { get; set; } 
} 

Я также определил JsonHelper класс (де) сериализации:

public class JsonHelper 
{ 
    public static T fromJson<T> (string json) 
    { 
     var bytes = Encoding.Unicode.GetBytes (json); 

     using (MemoryStream mst = new MemoryStream(bytes)) 
     { 
      var serializer = new DataContractJsonSerializer (typeof (T)); 
      return (T)serializer.ReadObject (mst); 
     } 
    } 

    public static string toJson (object instance) 
    { 
     using (MemoryStream mst = new MemoryStream()) 
     { 
      var serializer = new DataContractJsonSerializer (instance.GetType()); 
      serializer.WriteObject (mst, instance); 
      mst.Position = 0; 

      using (StreamReader r = new StreamReader(mst)) 
      { 
       return r.ReadToEnd(); 
      } 
     } 
    } 
} 

Вышеупомянутые биты у меня уже работали. Что касается одного метода, который обрабатывал бы выполнение каждого запроса в зависимости от типа ожидаемого результата, в то время как он облегчал изменение того, как я обрабатываю вещи (например, ошибки и т. Д.), Он также добавляет сложность и, следовательно, удобочитаемость моего кода. В результате я создал отдельные методы (все варианты метода Execute в оригинальном вопросе:

// execute and return response.StatusCode 
private static async Task<HttpStatusCode> ExecuteForStatusCode (HttpRequestMessage request, bool authenticate = true) 
// execute and return response without processing 
private static async Task<HttpResponseMessage> ExecuteForRawResponse(HttpRequestMessage request, bool authenticate = true) 
// execute and return response.IsSuccessStatusCode 
private static async Task<Boolean> ExecuteForBoolean (HttpRequestMessage request, bool authenticate = true) 
// execute and extract JSON payload from response content and convert to RESULT 
private static async Task<RESULT> Execute<RESULT>(HttpRequestMessage request, bool authenticate = true) 

я могу переместить неавторизованные ответы (которые мой текущий код не обрабатывают прямо сейчас в любом случае) в новый метод CheckResponse, что будет (например) журнал пользователь, если получен 401.

2

Вот пример того, как я это сделал, используя MVC API 2 в качестве бэкэнд. Мой бэкэнд возвращает результат json, если учетные данные верны. UserCredentials класс точно такая же модель, как JSon result.You будет использовать System.Net.Http.Formatting ведьму можно найти в Microsoft.AspNet.WebApi.Client NugetPackage

public static async Task<UserCredentials> Login(string username, string password) 
{ 
    string baseAddress = "127.0.0.1/"; 
    HttpClient client = new HttpClient(); 

    var authorizationHeader = Convert.ToBase64String(Encoding.UTF8.GetBytes("xyz:secretKey")); 
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authorizationHeader); 



    var form = new Dictionary<string, string> 
    { 
     { "grant_type", "password" }, 
     { "username", username }, 
     { "password", password }, 
    }; 

    var Response = await client.PostAsync(baseAddress + "oauth/token", new FormUrlEncodedContent(form)); 
    if (Response.StatusCode == HttpStatusCode.OK) 
    { 
     return await Response.Content.ReadAsAsync<UserCredentials>(new[] { new JsonMediaTypeFormatter() }); 
    } 
    else 
    { 
     return null; 
    } 
} 

и вы также должны Newtonsoft.Json пакет.

public class UserCredentials 
    { 
    [JsonProperty("access_token")] 
    public string AccessToken { get; set; } 

    [JsonProperty("token_type")] 
    public string TokenType { get; set; } 

    [JsonProperty("expires_in")] 
    public int ExpiresIn { get; set; } 

    //more properties... 
    } 
+0

Многословные методы - именно то, чего я хочу избежать. Кроме того, существует ли причина, по которой вы создаете новый «HttpClient» в каждом методе или вы включили его только для полноты? Я объявил один экземпляр «HttpClient» в своем классе «ServerAPI» и планирую маршрутизировать все запросы через него. Благодарю. – copolii

+1

Правильно, это было просто ради полноты. Я был также между многословным или somethink более универсальным, но я решил обрабатывать каждый вызов api по-разному, потому что мне легче справляться с тем, что я должен делать. Также моим товарищам по команде легче понять и внести изменения. – Stamos

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