Introduction
Accessing Protrack API (https://www.protrack365.com/api.html) is fairly simple, it’s all about creating simple HTTP requests and handling the results which are represented in JSON. The trick here is using OOP concepts to make our code more reusable and more maintainable.
Full Code Listing
This package has been posted to,
Requirements
A Protrack account with one or more GPS devices available for testing. If you are facing permission problems contact your vendor or Protrack technical support.
Dependencies
Start by adding a reference to Newtonsoft JSON.NET (https://www.newtonsoft.com/json) library to your project. This can be directly downloaded from the vendor’s website or installed through NuGet Manager.
Abstraction
Base Request
Referring to Protrack API we can see that all functions (except the authorization function) accept an access token as a parameter. This token is valid for a certain time (2 hours) and it can be received through authorization only.
We will start by creating a base class that will represent the request data. The source code is fairly self-explanatory:
- internal abstract class ProtrackRequest {
- /// <summary>
- /// Path to function.
- /// </summary>
- public abstract string BaseUri { get; }
- /// <summary>
- /// This is used by all functions except 'authorization'.
- /// </summary>
- public string AccessToken { get; set; }
- /// <summary>
- /// Returns the list of request parameters packaged in a dictionary. Where Key is parameter name and Value is parameter value.
- /// </summary>
- public virtual IDictionary<string, object> GetParams() {
- var list = new Dictionary<string, object>();
- if (AccessToken != null) // adding access token only if necessary
- list.Add("access_token", AccessToken);
- return list;
- }
- /// <summary>
- /// Returns the list of request parameters as a query string.
- /// </summary>
- public virtual string GetParamsQueryString() {
- string queryString = string.Empty;
- foreach (var itm in GetParams()) {
- // This will keep empty parameters. You can skip them if you like.
- string valueStr = string.Empty;
- if (itm.Value != null)
- valueStr = System.Uri.EscapeDataString(itm.Value.ToString());
- queryString += string.Format("{0}={1}&", itm.Key, valueStr);
- }
- return queryString;
- }
- /// <summary>
- /// Returns full request signature (request URI along with parameter query string.)
- /// </summary>
- public virtual string GetRequestUri() {
- return BaseUri + "?" + GetParamsQueryString();
- }
- public override string ToString() {
- return GetRequestUri();
- }
- }
From the above code we can see that every request class derived from the base will have to fill its path (BaseUri property; mandatory) and parameter list (GetParams() method; optional).
Base Response
Referring to the Protrack API again we can see that every call response returned from the server, besides being in JSON format, has two common attributes: code and message. The code may refer to one of the error codes available as a list in the API reference, while the message is the description. Keeping those two attributes in mind, we can create our response class,
- internal class ProtrackResponse {
- [JsonProperty("code")]
- public int Code { get; set; }
- [JsonIgnore]
- public ProtrackResponseCode ResponseCode { get { return (ProtrackResponseCode)Code; } }
- [JsonProperty("message")]
- public string Message { get; set; }
- }
- internal enum ProtrackResponseCode {
- Success = 0,
- SystemError = 10000,
- UnknownRequest = 10001,
- LoginTimeout = 10002,
- Unauthorized = 10003,
- ParameterError = 10004,
- MissingParameter = 10005,
- ParamOutOfRange = 10006,
- PermissionDenied = 10007,
- RequestLimit = 10009,
- AccessTokenNotExist = 10010,
- AccessTokenInvalid = 10011,
- AccessTokenExpired = 10012,
- ImeiUnauthorized = 10013,
- RequestTimeError = 10014,
- LoginFailed = 20001,
- TargetNotExist = 20005,
- DeviceOffline = 20017,
- SendCommandFailed = 20018,
- NoData = 20023,
- TargetExpired = 20046,
- Unsupported = 20048
- }
As response class needs to be instantiated, we cannot just mark it as abstract. Abstract classes cannot be instantiated.
API Wrapper
Now for the actual code that connects things together. This code represents the wrapper itself. The code is very generic. We will add function wrappers later.
- class ProtrackWrapper {
- protected string Account { get; set; }
- protected string Password { get; set; }
- /// <summary>
- /// API base URI
- /// </summary>
- protected string BaseUri { get { return "http://api.protrack365.com"; } }
- /// <summary>
- /// This will be used for all requests
- /// </summary>
- public string AccessToken { get; protected set; }
- /// <summary>
- /// Access token expiry date
- /// </summary>
- public DateTime? AccessTokenExpiresOnUtc { get; protected set; }
- public ProtrackWrapper(string account, string password) {
- this.Account = account;
- this.Password = password;
- }
- /// <summary>
- /// Returns a response from a web resource.
- /// </summary>
- /// <returns>Response represented as string.</returns>
- protected static string GetResponse(string requestUri) {
- HttpWebRequest req = WebRequest.CreateHttp(requestUri);
- using (var rsp = req.GetResponse())
- using (var stm = rsp.GetResponseStream())
- using (var rdr = new StreamReader(stm)) {
- return rdr.ReadToEnd();
- }
- }
- public virtual T GetResponse<T>(ProtrackRequest req) where T : ProtrackResponse {
- var requestUrl = new Uri(new Uri(BaseUri), req.GetRequestUri()).ToString();
- var rspStr = GetResponse(requestUrl);
- T rsp = JsonConvert.DeserializeObject<T>(rspStr);
- // should not throw a generic exception,
- // and should not throw an exception for just everything
- // we will just keep this for now!
- if (rsp.Code != 0)
- throw new Exception(rsp.Message);
- return rsp;
- }
- }
As you can see in the above code, we used static polymorphism to create two versions of GetResponse(), one that returns a bare response for the bare request URI, and another one that accepts a typed request object and returns a typed response object. In fact, the other version is a generic one that returns only objects that derive from ProtrackResponse.
Authorization
To call any API function you need an access token, and this can be retrieved through the authorization function. The authorization function accepts three arguments: request time (in Unix format), the username (i.e. account), and signature.
Unix Time
As described by Wikipedia, Unix time (also known as POSIX time or UNIX Epoch time) is a system for describing a point in time. It is the number of seconds that have elapsed since 00:00:00 Thursday, 1 January 1970, Coordinated Universal Time (UTC), minus leap seconds.
Unix time format will be used throughout the API, so we have created a helper class for it,
- static class UnixTimeHelper {
- /// <summary>
- /// Converts DateTime to Unix time.
- /// </summary>
- public static long ToUnixTime(this DateTime time) {
- var totalSeconds = (long)(time.Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
- if (totalSeconds < 0)
- throw new ArgumentOutOfRangeException("Unix time starts Jan 1 1970 00:00:00");
- return totalSeconds;
- }
- /// <summary>
- /// Converts Unix time to DateTime.
- /// </summary>
- public static DateTime ToDateTime(long unixTime) {
- return new DateTime(1970, 1, 1).Add(TimeSpan.FromSeconds(unixTime));
- }
- }
Signature
Signature is an MD5 hash of a combination of MD5 password hash and request time (in Unix format). In other words:
signature = MD5 ( MD5(password) + unix_time )
Signature is represented as 32 bytes lower-case string.
Authorization Request
The authorization request class is as follows,
- internal class ProtrackAuthorizationRequest : ProtrackRequest {
- /// <summary>
- /// Path to function.
- /// </summary>
- public override string BaseUri { get { return "api/authorization"; } }
- public string Account { get; protected set; }
- protected string Password { get; set; }
- public DateTime RequestTimeUtc { get; private set; }
- public ProtrackAuthorizationRequest() { }
- public ProtrackAuthorizationRequest(string account, string password) {
- this.Account = account;
- this.Password = password;
- }
- public override IDictionary<string, object> GetParams() {
- RequestTimeUtc = DateTime.UtcNow;
- var unixTime = UnixTimeHelper.ToUnixTime(RequestTimeUtc);
- string signature = GetSignature(unixTime);
- var list = base.GetParams(); // retrieving base parameters (if any)
- list.Add("time", unixTime);
- list.Add("account", this.Account);
- list.Add("signature", signature);
- return list;
- }
- private string GetSignature(long unixTime) {
- // signature is md5(md5(password) + time) encoded as a 32 bytes lower-case characters.
- var signature = ProtrackHelper.HashMD5(this.Password);
- signature = ProtrackHelper.HashMD5(signature + unixTime.ToString());
- return signature;
- }
- }

Join the conversation! Your thoughts help the community grow.