Skip to content

C# から APIC-EM の REST API にアクセスする

Cisco Japan BlogSDN カテゴリー に「Python を使って APIC-EM の REST API にアクセスする」という記事が掲載されています。

(APIC-EM に限らず) Python や Ruby から REST API にアクセスするサンプルはしばしば見かけるのですが、C# のサンプルはなぜか数が少ないきがします… (探し方が悪い?)。というわけで C# のサンプルを書いてみました。あくまでサンプルなのでエラー処理はしていません。

処理の大まかな流れ

実際に何がしかの REST API を利用する前に「ServiceTicket」と呼ばれる資格情報を取得する必要があります。よって、大まかな処理順序は以下のようになります。

  1. 適切な ID/Password を使って ServiceTicket を取得する
  2. 取得出来た ServiceTicket を使って必要な REST API を呼び出す

サンプルコード

やや長いですが、今回のサンプルコード全体は以下の通りです。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
using System;
using System.Net;
using System.Runtime.Serialization;
using RestSharp;

namespace RestApiSample
{
    class LoginInfo
    {
        public string username { get; set; }
        public string password { get; set; }

        public LoginInfo(string _username, string _password)
        {
            username = _username;
            password = _password;
        }
    }

    [DataContract]
    class ServiceTicket
    {
        [DataMember]
        public ServiceTicketResponse response { get; set; }
        [DataMember]
        public float version { get; set; }
    }

    [DataContract]
    class ServiceTicketResponse
    {
        [DataMember]
        public string serviceTicket { get; set; }
        [DataMember]
        public uint idleTimeout { get; set; }
        [DataMember]
        public uint sessionTimeout { get; set; }
    }

    [DataContract]
    class NetworkDeviceCount
    {
        [DataMember]
        public uint response { get; set; }
        public float version { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // Ignore self-signed SSL certificate warnings.
            ServicePointManager.ServerCertificateValidationCallback += (
                sender,
                certificate,
                chain,
                sslPolicyErrors) => true;

            string baseUrl = "https://10.0.0.1";
            string username = "admin";
            string password = "password";

            // Get Service Ticket.
            string serviceTicket = GetServiceTicket(baseUrl, username, password);

            // Get network device count.
            NetworkDeviceCount networkDeviceCount = GetNetworkDevivceCount(baseUrl, serviceTicket);
            Console.WriteLine(networkDeviceCount.response);
        }

        static string GetServiceTicket(string _baseUrl, string _username, string _password)
        {
            var client = new RestClient(_baseUrl);

            LoginInfo loginInfo = new LoginInfo(_username, _password);

            var request = new RestRequest("api/v1/ticket", Method.POST);
            request.RequestFormat = DataFormat.Json;
            request.AddBody(loginInfo);

            IRestResponse response = client.Execute(request);

            RestSharp.Deserializers.JsonDeserializer deserializer =
                new RestSharp.Deserializers.JsonDeserializer();

            var serviceTicket = deserializer.Deserialize<ServiceTicket>(response).response;
            return serviceTicket.serviceTicket;
        }

        static NetworkDeviceCount GetNetworkDevivceCount(string _baseUrl, string _serviceTicket)
        {
            var client = new RestClient(_baseUrl);

            var request = new RestRequest("api/v1/network-device/count", Method.GET);
            request.AddHeader("X-Auth-Token", _serviceTicket);

            IRestResponse response = client.Execute(request);

            RestSharp.Deserializers.JsonDeserializer deserializer =
                new RestSharp.Deserializers.JsonDeserializer();

            var networkDeviceCount = deserializer.Deserialize<NetworkDeviceCount>(response);
            return networkDeviceCount;
        }
    }
}

補足説明

RestSharp で REST API を操作する

C# で使える REST API 関連のライブラリは幾つかあるようですが、今回は RestSharp を使いました。NuGet からインストールするなり、公式サイトからダウンロードしてプロジェクトへ参照を追加するなり、用意しておきます。

サーバ証明書の警告を無視する

APIC-EM はデフォルトで自己証明書を利用していますので、(当然ですが) そのままクライアントのブラウザからアクセスすると「証明書の正当性を確認出来ない」という警告が表示されます。C# から REST API で APIC-EM にアクセスした場合はエラーとなり、処理が継続出来ません。そこで証明書の警告を無視する為に以下のコードを追加しておきます。

1
2
3
4
5
6
// Ignore self-signed SSL certificate warnings.
ServicePointManager.ServerCertificateValidationCallback += (
    sender,
    certificate,
    chain,
    sslPolicyErrors) => true;

データ構造をデータコントラクトとして定義する

Role Based Access Control の /ticket という API リファレンスを見ると Response Class は以下のように定義されています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
TicketRbacResult {
    version (string, optional),
    response (ServiceTicketRbac, optional)
}

ServiceTicketRbac {
    idleTimeout (integer, optional),
    serviceTicket (string): Service Ticket to be used as authentication Ticket,
    sessionTimeout (integer, optional)
}

実際にこの REST API (/api/v1/ticket) へ適切にアクセスすると、以下のような JSON のレスポンスを得られます。

1
{"response":{"serviceTicket":"ST-59-u9Kxu6WzaXUUejbhfc24-cas","idleTimeout":1800,"sessionTimeout":21600},"version":"1.0"}

これをインデントすると以下のようになります。

1
2
3
4
5
6
7
8
{
    "response": {
        "idleTimeout": 1800,
        "serviceTicket": "ST-59-u9Kxu6WzaXUUejbhfc24-cas",
        "sessionTimeout": 21600
    },
    "version": "1.0"
}

これを C# のデータコントラクトとして表現すると以下のように書けます。適当に書いてしまったのでクラス名が API リファレンスと一致していません… orz゛

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
[DataContract]
class ServiceTicket
{
    [DataMember]
    public ServiceTicketResponse response { get; set; }
    [DataMember]
    public float version { get; set; }
}

[DataContract]
class ServiceTicketResponse
{
    [DataMember]
    public string serviceTicket { get; set; }
    [DataMember]
    public uint idleTimeout { get; set; }
    [DataMember]
    public uint sessionTimeout { get; set; }
}

POST 時のリクエストボディの書き方

今回は POST する際、リクエストボディを以下のように書きました。request は RestSharp.RestRequest クラスのインスタンスです。

1
2
3
4
5
LoginInfo loginInfo = new LoginInfo("admin", "password");

var request = new RestRequest("api/v1/ticket", Method.POST);
request.RequestFormat = DataFormat.Json;
request.AddBody(loginInfo);

これは以下のように書くことも出来ます。

1
2
3
4
5
request.AddParameter(
    "application/json",
    "{\"username\": \"admin\", \"password\": \"password\"}",
    ParameterType.RequestBody
    );