My Question: How do I do this?
So, I hadn't touched anything .Net in about 6 years until this week. There's a lot that I've forgotten and even more that I never knew and while I love the idea of the async/await keywords, I'm having a slight problem implementing the following requirements for a client's API implementation:
- The
ServerAPI
class has a method for each of the API methods, taking appropriate input parameters (e.g. the methodLogin
takes in anid
and apassword
, makes the API call and returns the result to the caller). - I want to abstract away the JSON so that my API methods return the actual object you're fetching (e.g. the
Login
method above returns aUser
object with your auth token, uid, etc.) - Some API methods return a 204 on success or no meaningful content (not meaningful in my usecase maybe I only care about success/failure), for these I'd like to return either a bool (
true
= success) or the status code. - I'd like to keep the async/await (or equivalent) design, because it seems to really work well so far.
- For some methods, I might need to just return the
HttpResponseMessage
object and let the caller deal with it.
This is roughly what I have so far and I'm not sure how to make it compliant with the above OR whether I'm even doing this right. Any guidance is appreciated (flaming, however, is not).
// 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;
}
This is the APIError
JSON structure (it's the status code + a custom error code).
{
"status": 404,
"code":216,
"msg":"User not found"
}
I would prefer to stay with System.Net
, but that's mostly because I don't want to switch all my code over. If what I want is easier done in other ways then it's obviously worth the extra work.
Thanks.