web-dev-qa-db-ja.com

HttpResponseMessageのコンテンツをオブジェクトに変換する

私の質問:これを行うにはどうすればよいですか?

ですから、今週まで約6年間、.Netには何も触れていませんでした。忘れてしまったことや知らなかったことはたくさんあります。async/ awaitキーワードのアイデアは気に入っていますが、クライアントのAPI実装に次の要件を実装するのに少し問題があります。

  1. ServerAPIクラスには、適切な入力パラメーターを受け取る各APIメソッドのメソッドがあります(たとえば、メソッドLoginidpasswordを取ります。 API呼び出しを行い、結果を呼び出し元に返します)。
  2. JSONを抽象化して、APIメソッドがフェッチしている実際のオブジェクトを返すようにします(たとえば、上記のLoginメソッドは認証トークンやuidなどを含むUserオブジェクトを返します)
  3. 一部のAPIメソッドは、成功または意味のあるコンテンツがない場合に204を返します(私のユースケースでは意味がないので、成功/失敗のみを気にします)。これらの場合、ブール値(true =成功)またはステータスコード。
  4. これまでのところ本当にうまく機能しているように見えるので、async/await(または同等の)設計を維持したいと思います。
  5. 一部のメソッドでは、HttpResponseMessageオブジェクトを返し、呼び出し元に処理させる必要がある場合があります。

これは私がこれまでに持っているおおよそのものであり、上記のOR私がこれを正しく行っているかどうか)に準拠させる方法がわかりません。どんなガイダンスもありがたいです(ただし、炎上ではありません)。

// 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のままにしておきたいのですが、それは主に、すべてのコードを切り替えたくないためです。私が望むことが他の方法でより簡単に行われるのであれば、それは明らかに余分な作業の価値があります。

ありがとう。

5
copolii

したがって、最初に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を受信した場合にユーザーをログアウトします。

1
copolii