OneDrive API をバッチプログラムなどユーザー操作なしで使用する (.NET C# 版) (追加使用ライブラリなし)

頁面創建日期 :

當前顯示的頁面不支援所選的顯示語言。

動作確認環境

Visual Studio
  • Visual Studio 2022
.NET
  • .NET 8
Microsoft 認証 API
  • 2.0 OAuth 2
Microsoft OneDrive API
  • 1.0
Microsoft アカウントの種類
  • 職場または学校アカウント

動作必須環境

Visual Studio
  • いずれかのバージョン
ASP.NET Core
  • いずれかのバージョン
Microsoft 認証 API
  • 2.0 OAuth 2
Microsoft OneDrive API
  • 1.0
Microsoft アカウントの種類
  • 職場または学校アカウント

今回の目的について

今回 OneDrive の API を使用してプログラムで OneDrive にファイルをアップロードしたりダウンロードしたりするための下準備を行います。 今回は OneDrive にアクセスできるところを確認するまでの説明になるので、その先の OneDrive API でどのようなことができるかまでは説明しません。 ただ接続さえできればその先は API のドキュメントなどを読んで応用できると思います。

プログラムの動かし方

OneDrive へのアクセスは Web アプリケーション、デスクトップアプリケーション、コンソールアプリケーションなどプログラムの形態に依存することはなく様々なプログラムで動かすことができます。 その中で今回はコンソールアプリケーションとして作成しユーザーが操作を行わずに自動実行させるプログラム形態とします。

Microsoft アカウントの種類について

Microsoft アカウントを作成したことがある人であれば見たことがある人もいるかもしれませんが、 Micorsoft アカウントの種類は大きく分けて2つあり「個人用の Microsoft アカウント」と「職場または学校アカウント」があります。 OneDrive は Microsoft アカウントに紐づきますが、上記のアカウントの種類によって OneDrive の種類も若干変わります。

一応 OneDrive API は上記どちらの種類のアカウントでも使用することができるのですが、 今回の「コンソールアプリケーションとして作成しユーザーが操作を行わずに実行する」形態のプログラムでは 「職場または学校アカウント」の Microsoft アカウントでしか使用できませんのでご注意ください。 これは認証方法の違いによるものです。

認証方法について

まずプログラムで OneDrive にアクセスするためには認証を行わなければなりません。 前述のとおり OneDrive へのアクセスはどのようなプログラムでも動かせますが、API に接続するための認証はプログラムの実行形態によって変わってきます。 Web アプリケーション、デスクトップアプリケーションはユーザーの操作によって認証することができますが、 バックグラウンドで動かすようなコンソールアプリケーションなどではユーザーが操作できないので認証方法も上記とは異なります。

詳しい理由は調べ切れていないのでわかりませんが、ぱっと思いつくものであればセキュリティやアカウントの管理形態に起因するものではないかと思われます。 これはもう仕様ですとしか言えませんので、もし「個人用の Microsoft アカウント」に紐づく OneDrive にプログラムでアクセスしたいというのであれば、 バックグラウンドで動くプログラム以外にするかユーザーの操作を含めた認証方法でコンソールアプリケーションを動かす形にするしかないと思います。

今回 OneDrive API にアクセスするためのライブラリは使用しません

OneDrive API へのアクセスは「Microsoft Graph」というライブラリを使用すれば比較的扱いやすいのですが、 今回は API の URL に直接アクセスして実行する方法を使います。 少し面倒ですがライブラリを使用しないので .NET 以外のプログラムでも同じような手順で OneDrive API にアクセスできるメリットがあります。 また、ライブラリのバージョン違いによってコードが変わってしまうというデメリットを防ぐこともできます。

「Microsoft Graph」を使った方法については別の Tips で説明します。

前提条件

  • 「職場または学校アカウント」の Microsoft アカウントを登録済みであること
  • 上記 Microsoft アカウントに紐づく OneDrive が使用できること (OneDrive Business など)
  • Visual Studio 2022 がインストールされていること

アプリケーションが Microsoft アカウントや OneDrive にアクセスできるように Azure で設定を行う

以下の URL から Azure にログインします。

上の検索欄から「アプリの登録」と入力して選択します。

「新規登録」をクリックします。

「名前」の内容は任意です。わかりやすい名前を入力してください。

「サポートされているアカウントの種類」は「個人用 Microsoft アカウントのみ」以外を選択します。 これは前述通り今回の認証では組織に所属するアカウントでしか使用できないためです。 ここでは「任意の組織ディレクトリ内のアカウント (任意の Microsoft Entra ID テナント - マルチテナント) と個人用の Microsoft アカウント (Skype、Xbox など)」を選択しています。

「リダイレクト URI」は今回使用しないので入力しなくてよいです。

入力が終わったら「登録」ボタンをクリックします。

作成したアプリの登録の概要ページが表示されるので「アプリケーション (クライアント) ID」と「ディレクトリ (テナント) ID」をコピーしておきます。この値は後で使います。

左のメニューの管理カテゴリから「証明書とシークレット」を選択し、中央のタブで「クライアント シークレット」が選択されていることを確認します。 「新しいクライアント シークレット」をクリックします。

「説明」の入力は任意です。継続して使用する場合は使用目的がわかりやすい説明を入れてください。

「有効期限」はこのクライアント シークレットが有効な期間です。今回テスト目的なので 90日にしていますが使用目的に合わせて設定してください。

クライアント シークレットを作成すると一覧に追加されます。この中で「値」列の値を使用するのでコピーします。 この値は後でコピーできなくなるので注意してください。

続いて左のメニューの管理カテゴリから「API のアクセス許可」を選択し、中央にある「アクセス許可の追加」をクリックします。

API アクセス許可の要求で「Microsoft Graph」をクリックします。

「アプリケーションの許可」をクリックします。この項目は個人用 Microsoft アカウントでは選択できません。

下にアクセス許可の一覧が表示されるので検索欄などを使用して以下の項目にチェックを入れます。

  • Files.ReadWrite.All

OneDrive のみを使用するのであればこれだけで十分ですが、ほかにもアクセスしたい物があれば許可を追加します。 OneDrive へのアクセスに必要な許可は以下の公式ページに記載されています。

選択が終わったら「アクセス許可の追加」ボタンをクリックします。

追加したものが一覧に追加され「種類」は「アプリケーション」になっています。 ただ「状態」が「XXXX に付与されてません」となっているので「XXXX に管理者の同意を与えます」をクリックします。(XXXX は組織の名前です)

「はい」をクリックします。

付与された状態に変わります。

ちなみに最初からある「委任済みの User.Read」は今回使わないので削除しても構いません。 アプリケーションとして「User.Read」を追加した場合はそちらを使用してください。

ユーザー ID の確認

対象ユーザーが持つ OneDrive にアクセスするので事前にユーザー ID を確認します。

Microsoft Entra ID で検索して開きます。

左のメニューから「ユーザー」を選択します。

所属しているユーザーの一覧が表示されるので対象者をクリックします。

「オブジェクト ID」がユーザー ID となるのでメモしておきます。

コンソールアプリケーションを作成する

Visual Studio を起動してコンソールアプリケーションのプロジェクトを作成します。 Visual Studio 以外で作ってもよいですがここでは Visual Studio で説明します。

場所やプロジェクト名などは任意です。ここではプロジェクト名を OneDriveApiDotNetClientCredentialsHttpClient としています。

今回コード分けなどはせず Program.cs に上からずらずら記述するので動きを確認出来たら必要に応じてコードを分けるなど書き換えてください。

using System.Runtime.Serialization.Json;
using System.Text;
using System.Text.Json;
using System.Web;
using System.Xml;

使用する名前空間を記述します。

// 認証結果を格納するクラスです
public record TokenResponse(string token_type, int expires_in, int ext_expires_in, string access_token);

認証後のトークン情報を受け取るためのクラスです。 Program.cs に記述する場合はコードの最後のほうに記述してください。 このクラスだけほかのファイルに分けてしまってもいいです。

// JSON 文字列を整形するメソッド
static string ConvertToIndentedJson(string json)
{
  byte[] buffer = Encoding.UTF8.GetBytes(json);
  using MemoryStream stream = new();
  using XmlDictionaryWriter writer = JsonReaderWriterFactory.CreateJsonWriter(stream, Encoding.UTF8, true, true);
  using XmlDictionaryReader reader = JsonReaderWriterFactory.CreateJsonReader(buffer, XmlDictionaryReaderQuotas.Max);
  writer.WriteNode(reader, true);
  writer.Flush();
  return Encoding.UTF8.GetString(stream.ToArray());
}

API から受け取った JSON テキストをインデントありで整形するおまじないメソッドです。 インターネットから拾ってきたコードなので中身については詳しく説明しません。

// 各種 ID などの定義
var clientId = "XXXXXXXX";      // クライアント ID
var tenantId = "XXXXXXXX";      // テナント ID
var clientSecret = "XXXXXXXX";  // クライアント シークレット
var userId = "XXXXXXXX";        // ユーザー ID
var accessToken = "";           // アクセストークン

それぞれ Azure 上で取得した ID などをセットしてください。 accessToken については OneDrive にアクセスするための一時トークンなので実行時にセットされます。

// 各種 URL の定義
var urlToken = $"https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token";
var urlOneDriveGetRootFolders = $"https://graph.microsoft.com/v1.0/users/{userId}/drive/root/children";

認証の API URL と OneDvie のルートにあるフォルダ一覧を取得する API URL です。 今回 OneDrive の API へのアクセスを確認するところまでの説明なので OneDrive API の内容については説明しません。 OneDrive の API にアクセスできればアップロードやダウンロードは応用なのでそこまで難しくないと思います。

// 使いまわすので最初に定義しておく
HttpClient httpClient = new();

指定した URL に対してリクエストするための HttpClient を生成しておきます。

// 「Name1=Value1&Name2=Value2...」形式のクエリパラメータ文字列を作成する
var valueDict = new Dictionary<string, string>
{
    { "client_id", clientId },
    { "scope", "https://graph.microsoft.com/.default" },
    { "client_secret", clientSecret },
    { "grant_type", "client_credentials" },
};
var valueQS = string.Join("&", valueDict.Select(x => string.Format("{0}={1}", x.Key, HttpUtility.UrlEncode(x.Value))));
Console.WriteLine($"valueQS={valueQS}");

最初に認証 API に対してデータを含めてリクエストを行うのでそのデータを準備します。 送るデータの形式は「Name1=Value1&Name2=Value2...」形式のクエリパラメータ文字列になるのでいったん Dictionary で作成しておいてからクエリパラメータ文字列に変換しています。

送るパラメータは以下の4つです。

パラメータ名 値例
client_id アプリの登録で取得したクライアント ID をセットします。
scope クライアント シークレットによる認証では https://graph.microsoft.com/.default 固定です。
client_secret アプリの登録で取得したクライアント シークレットの値をセットします。
grant_type クライアント シークレットによる認証なので client_credentials にします

詳しい説明については以下の公式サイトを参照してください。

// 認証処理
using (HttpRequestMessage request = new(HttpMethod.Post, urlToken))
{
  request.Content = new StringContent(valueQS, Encoding.UTF8, "application/x-www-form-urlencoded");

  // データを送信し結果を受け取る
  using var response = httpClient.SendAsync(request).Result;

  Console.WriteLine($"IsSuccessStatusCode={response.IsSuccessStatusCode}");
  Console.WriteLine($"StatusCode={response.StatusCode}");
  Console.WriteLine($"ReasonPhrase={response.ReasonPhrase}");

  if (response.IsSuccessStatusCode == false)
  {
    Console.WriteLine("トークンの取得に失敗しました。");
    return;
  }

  // レスポンスからアクセストークンを受け取る。
  var tokenResultJson = response.Content.ReadAsStringAsync().Result;
  Console.WriteLine($"tokenResultJson={ConvertToIndentedJson(tokenResultJson)}");

  var tokenResult = JsonSerializer.Deserialize<TokenResponse>(tokenResultJson) ?? throw new Exception("JSON をデシリアライズできませんでした。");
  accessToken = tokenResult.access_token;
}

HttpClient を使用して認証 API にリクエストを行います。よくある HttpClient の使い方なのであまり注意すべき点はないと思います。

送信するデータは UTF8 でエンコードし Content-Type は application/x-www-form-urlencoded にします。メソッドは POST です。

レスポンスの IsSuccessStatusCode を確認し正常に処理されたのであれば、結果を JSON 文字列で受け取ります。 受け取る JSON は以下のようなパラメータになっています。

{
  "token_type": "Bearer",
  "expires_in": 3599,
  "ext_expires_in": 3599,
  "access_token": "XXXXXXXXXXXXXXX"
}

この中で使用するのは access_token です。この値を OneDrive API で使用します。 他のパラメータは無視してもいいですが、access_token の有効期限を把握したい場合は expires_in なども使用してください。

JSON 文字列のままでは使いづらいので JsonSerializer.Deserialize でデシリアライズしてから access_token を取得しています。

// OneDrive 処理
using (HttpRequestMessage request = new(HttpMethod.Get, urlOneDriveGetRootFolders))
{
  // アクセストークンをヘッダーに追加する
  request.Headers.Add("Authorization", "Bearer " + accessToken);

  // OneDrive API にリクエスト
  using var response = httpClient.SendAsync(request).Result;

  Console.WriteLine($"IsSuccessStatusCode={response.IsSuccessStatusCode}");
  Console.WriteLine($"StatusCode={response.StatusCode}");
  Console.WriteLine($"ReasonPhrase={response.ReasonPhrase}");

  if (response.IsSuccessStatusCode == false)
  {
    Console.WriteLine("OneDrive へのアクセスに失敗しました。");
    return;
  }

  // レスポンスからアクセストークンを受け取る。
  var apiResultJson = response.Content.ReadAsStringAsync().Result;
  Console.WriteLine($"apiResultJson={ConvertToIndentedJson(apiResultJson)}");
}

最後にアクセストークンを使用して OneDrive API にリクエストを行います。 メソッドは GET を指定していますが、API の種類によって変わるので OneDrive API のドキュメントで確認してください。

ヘッダーにアクセストークンを追加します。キーを Authorization とし、値に Bearer {アクセストークン} の形式でセットします。

後はリクエストを行い結果を JSON 文字列で受け取ります。 今回 OneDrive API にアクセスできることを確認するだけですので JSON 文字列を整形してコンソールに出力しているだけです。 実際には受け取った JSON をデシリアライズして内容に合わせて処理を行います。

今回フォルダー一覧を受け取っていますが JSON は以下のようになっていると思います。

{
  "@odata.context": "https:\/\/graph.microsoft.com\/v1.0\/$metadata#Collection(driveItem)",
  "value": [
    {
      "@microsoft.graph.Decorator": "decorator has been deprecated. Refer to folder.decorator",
      "createdBy": {
        "フォルダ作成者の情報":""
      },
      "createdDateTime": "2017-06-20T04:31:24Z",
      "eTag": "\"{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX},1\"",
      "id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
      "lastModifiedBy": {
        "フォルダ更新者の情報":""
      },
      "lastModifiedDateTime": "2017-06-20T04:31:24Z",
      "name": "フォルダ名",
      "parentReference": {
        "親フォルダの情報":""
      },
      "webUrl": "フォルダにアクセスできる URL",
      "cTag": "\"c:{5926E2DA-B7FF-4936-88A8-7908C378ECE2},0\"",
      "fileSystemInfo": {
        "createdDateTime": "2017-06-20T04:31:24Z",
        "lastModifiedDateTime": "2017-06-20T04:31:24Z"
      },
      "folder": {
        "childCount": 0
      },
      "size": 0,
      "specialFolder": {
        "name": "フォルダ名"
      }
    },
    :
  ]
}

全部の情報は必要ないと思うので、例えばフォルダ名だけ取得したいのであれば value にある配列から name パラメータだけを取得すればよいかと思います。

まとめ

ここまで正常に動作したのであればバッチプログラムなどで OneDrive にアクセスできるはずです。 アップロードやダウンロードのなどの方法について Tips にまとめるかは分かりませんが、基本的には前述のルートにあるフォルダの一覧取得を応用すればうまくいくはずです。 API の種類については以下の公式サイトにまとめられているので使いたい API を探して実装してみてください。

謝辞

今回 OneDrive API を使用するにあたりいくつか不明点があったので質問させていただきました。