The REST web services are quite common thing nowadays. Sure, web application just expose API through HTTP protocol, basically allowing any application to be integrated with. Simple? Yes, this is the power of REST, just simple. But vendor should consume the API somehow. The environment could really be different: .NET, Java, Python, PHP etc., and it it not so convenient to work with HTTP directly from your custom application. Instead of that you expect on “native” API that you work with like the rest of application: having a model, methods that returns of change model state. You expect on having Adapter
- the entity which would adapt REST HTTP methods into your platform/language methods. I’m going to give example of creation such adapter for .NET code.
Doesn’t matter what language you write it the steps of adapting is quite common, here they are:
Learn the API
Let’s have Trackyt.net API as an example. First thing you need to do is to learn API. REST API differs much from site to site, depending of developers tools and choice. All you need to understand the exact methods you need, their signature and data they operate with. Let’s take Authenticate
method: so we see, it takes 2 arguments, email and password and in response it returns JSON object, containing operation result and API token. That means:
URL: http://trackyt.net/api/v1.1/authenticate
Will be transformed in such C# method, like Authenticate
that receives email and password as arguments and return ApiToken as results. Note, ApiToken
is first model class we identified.
And call like this:
http://trackyt.net/api/v1.1/af005f695d300d0dfebbd574a4b1c0fa/tasks/all
Is actually transformed in something like GetAllTasks
that recieves ApiToken object and returns IList<Task>
. Task
is yet another model class we have to deal with.
This is a kind of analysis stage of implementation, you just need to understand interface and model.
Define interface and model
After you done you are ready to define interface:
public interface ITrackytApiAdapter
{
ApiToken Authenticate(string email, string password);
IList<Task> GetAllTasks(ApiToken token);
Task AddTask(ApiToken token, string description);
int DeleteTask(ApiToken apiToken, int taskId);
Task StartTask(ApiToken apiToken, int taskId);
Task StopTask(ApiToken apiToken, int taskId);
void StartAll(ApiToken apiToken);
void StopAll(ApiToken apiToken);
}
You see that all methods, defined in documentation are reflected as interface methods, all data accepted/returned by methods are defined as POCO.
public class ApiToken
{
public ApiToken(string token)
{
Value = token;
}
public String Value { get; private set; }
}
public class Task
{
public int Id { set; get; }
public string Description { set; get; }
public DateTime CreatedDate { set; get; }
public DateTime? StartedDate { set; get; }
public int Status { set; get; }
}
Integration testing
Is it possible to code without tests? I don’t think so. So, what we are going to do - one by one, tests all adapter methods. That’s should not be “super-duper-complex” test (at least during initial implementation), but rather smoke test ones. Do the call and see that results back. Here is just a several examples of tests for Authentication
and GetAllTasks
.
[Test]
public void Authenticate_AuthenicationSuccess_ReturnApiToken()
{
// arrange
var adapter = TrackytApiAdapterFactory.CreateV11Adapter();
// act
var apiToken = adapter.Authenticate(_email, _password);
// assert
Assert.That(apiToken, Is.Not.Null);
Assert.That(apiToken.Value, Is.Not.Null);
}
[Test]
[ExpectedException(typeof(NotAuthenticatedException))]
public void Authenticate_AuthenticationFailed_ExceptionThrown()
{
// arrange
var adapter = TrackytApiAdapterFactory.CreateV11Adapter();
// act
var apiToken = adapter.Authenticate("nouser", "noemail");
}
Implementation
There are 2 very suitable components you might use for any REST API adapters:
-
James Newton JSON.net library - the best framework to handle JSON’s in .NET (imho). I enjoy how easy to serialize and deserialize of data with JSON.net.
-
WebClient object - that is part of .NET framework and encapsulate all basic HTTP functions.
Here we go. Our task is to send HTTP request to server, check it for correctness and transform server reply to .NET objects. To do that is great to model all responses into POCO (as we did with model classes ApiToken
and Task
). The difference is that Responses
are actually internal classes, part of implementation and adapter users should know nothing about them. For instance let’s see AuthenticationResponse
class AuthenticationResponse : BaseResponse
{
internal class ResponseData
{
[JsonProperty("apiToken")]
public string ApiToken { set; get; }
}
[JsonProperty("data")]
public ResponseData Data { set; get; }
}
The base response is some common part of data that every response suppose to contain. In my case:
class BaseResponse
{
[JsonProperty("success")]
internal bool Success { set; get; }
[JsonProperty("message")]
public string Message { set; get; }
}
We should deserialize responses with JSON.net and send requests with WebClient. Let’s just see simple code example:
public ApiToken Authenticate(string email, string password)
{
using (var client = new WebClient())
{
var authenticationJson = JsonConvert.SerializeObject(new { Email = email, Password = password });
client.Headers.Add(HttpRequestHeader.ContentType, "application/json");
var responseString = client.UploadString(ApiUrl + "authenticate", authenticationJson);
var response = JsonConvert.DeserializeObject<AuthenticationResponse>(responseString);
if (!response.Success)
{
throw new NotAuthenticatedException();
}
return new ApiToken(response.Data.ApiToken);
}
}
It simply creates new WebClient instance, UploadString
method perform’s POST by default and places string object as POST payload. Then we receive response as string and try to deserialize to target response type. In case it could not serialize that, exception will be thrown. Next, it check result of operation and return required data back to client.
The implementation of the rest of methods is mostly the same, differing by type of HTTP request (GET, POST, DELETE, PUT) and request/response objects. Let’s see GetAllTasks
method that does GET request and returns all users tasks:
public IList<Task> GetAllTasks(ApiToken token)
{
using (var client = new WebClient())
{
client.Headers.Add(HttpRequestHeader.ContentType, "application/json");
var responseString = client.DownloadString(ApiUrl + token.Value + "/tasks/all");
var getAllTasksResponse = JsonConvert.DeserializeObject<GetAllTasksResponse>(responseString);
if (!getAllTasksResponse.Success)
{
throw new Exception("Can't get users tasks. " + getAllTasksResponse.Message);
}
return getAllTasksResponse.Data.Tasks;
}
}
As reference I’ll give you implementation of trackyt.api.csharp by me and GithubSharp API by Erik Zaadi.