Come integrare le API di terze parti da un’applicazione C#

Proprio mentre stai lavorando su nuovi progetti e pensi che i progetti precedenti sono stabili, ti arriva un’email dove ti dicono che se non effettui l’upgrade delle API di un servizio di terze parti, il progetto X non funzionerà più… Non resta che aprire il progetto e capire quale è questa implementazione.

Logicamente aprendo un progetto di qualche anno fà troverai sempre delle cose da cambiare/aggiornare ma spesso il tempo a disposizione non è quello che vorresti 🙁

Per l’implementazione è stato deciso di utilizzare HttpClient (fornisce una classe di base per l’invio di richieste HTTP e la ricezione di risposte HTTP da una risorsa identificata da un URI) per consumare le API.

L’implementazione delle API prevedeva:

  • autenticazione tramite API passando nell’header le credenziali;
  • ricezione di un Token (da passare a tutte le chiamate Read/Write/Delete/Update);
  • perform della richiesta passando il token nell’header ed eventuali parametri nell’url e nel content della richiesta.

Ho creato una classe che verrà inizializzata con le credenziali:

public class AuthConfig
{
  public string ClientID { get; set; }
  public string ClientSecret { get; set; }
  public string AccountId { get; set; }
}

In seguito ho creato la classe che contiene l’integrazione con le API tramite una richiesta HttpClient ed i metodi pubblici per effettuare l’inizializzazione delle credenziali ed in seguito l’update:

public class Api
{
  public AuthToken Token { get; private set; }
    public AuthConfig Config { get; private set; }
  
  public Api(AuthConfig config)
  {
    if (null == config)
      throw new ArgumentNullException("config");
    Config = config;
  }
  /// <summary>
  /// inizializza la configurazione
  /// </summary>
  public static AuthConfig InitApiConfig()
  {
    var config = new AuthConfig()
    {
      ClientID = "username",
      ClientSecret = "password",
      AccountId = "accountid"
    };
    return config;
  }
  /// <summary>
  /// metodo pubblico richiamato dal controller per l'update tramite le API
  /// </summary>
  public Task<Video> UpdateVideo(Video video,long videoId)
  {
    if (null == video)
      throw new ArgumentNullException("video");


    var requestUri = string.Format("https://terzepartiapiurl.com/accounts/{0}/videos/{1}", Config.AccountId, videoId);
    var method = new HttpMethod("PATCH");
    var request = new HttpRequestMessage(method, requestUri)
    {
      Content = new StringContent(JsonConvert.SerializeObject(video), Encoding.UTF8, "application/json")
    };
    var a = PerformRequestHttp(request);

    return null;
  }
  /// <summary>
  /// effettua la chiamata alle API con le info valorizzate nell' HttpRequestMessage tramite una /// richiesta HTTPClient
  /// </summary>
  private string PerformRequestHttp(HttpRequestMessage request)
  {
    this.InitToken();
    using (var client = new HttpClient())
    {
      client.SetAuthTokenHeader(Token);

      var response =  client.SendAsync(request).Result;
      if (!response.IsSuccessStatusCode)
      {
        if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
          throw new UnauthorizedAccessException("User not authorized");
        if (response.StatusCode == System.Net.HttpStatusCode.BadRequest)
          throw new ArgumentNullException("accountData", "Not all the parameters have been provided, account name has a wrong length or email is not valid");
        if (response.StatusCode == System.Net.HttpStatusCode.Conflict)
          throw new InvalidOperationException("Name already assigned to a existing account");

        var exMsg = string.Empty;
        if (null != response.Content)
          exMsg = response.Content.ReadAsStringAsync().Result;

        throw new Exception("Unknown error: " + exMsg);
      }

      if (null == response.Headers.Location)
        throw new Exception("Invalid location");

      return null;
    }
  }
  
  /// <summary>
  /// usato per il refresh OAuth token. Bisogna chiamarlo prima di ogni chiamata API
  /// </summary>
  private void InitToken()
  {
    if (null != Token && DateTime.Now < Token.ExpiresDate)
      return;

    var request = WebRequest.Create("https://terzepartiapiurl.com/access_token");
    SetBasicAuthHeader(request, Config.ClientID, Config.ClientSecret);

    request.Method = "POST";
    request.ContentType = "application/x-www-form-urlencoded";

    string postData = "grant_type=client_credentials";
    byte[] byteArray = Encoding.UTF8.GetBytes(postData);
    request.ContentLength = byteArray.Length;

    using (var dataStream = request.GetRequestStream())
    {
      dataStream.Write(byteArray, 0, byteArray.Length);
      dataStream.Close();
    }

    var response = (HttpWebResponse)request.GetResponse();
    var responseEncoding = Encoding.GetEncoding(response.CharacterSet);

    using (var sr = new StreamReader(response.GetResponseStream(), responseEncoding))
    {
      var jsonData = sr.ReadToEnd();
      Token = Newtonsoft.Json.JsonConvert.DeserializeObject<AuthToken>(jsonData);
    }
  }
  /// <summary>
  /// imposta nell'header le credenziali per l'autorizzazione
  /// </summary>
  private void SetBasicAuthHeader(WebRequest request, string userName, string userPassword)
  {
    string authInfo = userName + ":" + userPassword;
    authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
    request.Headers["Authorization"] = "Basic " + authInfo;
  }
}

Alla fine non resta che inizializzare la configurazione delle API e chiamare il metodo:

//inizializzo la configurazione
var config = Api.InitApiConfig();
//instanzio la classe delle API passando la configurazione
var api = new Api(config);

Video video = new Video();
video.Name = "Lorem ipsum API";
//effettuo la chiamata alle API per l'update
api.UpdateVideo(video, 423423);