2014-01-25

ApexからSharepointのREST APIを触ってみる。

毎度恒例Apexから触ってみるシリーズ。 前回はSharepointのREST APIの解説をしたので、今回は実際にApexからコールしてみます。

流れとしてはOAuth2.0なので…

  1. 認可画面のURLを生成し、クライアントにアクセスさせ、認可処理をさせる。
  2. 認可処理後のコールバックURL(VF)でcodeパラメータを取得。
  3. 2で取得したcodeパラメータからAccess TokenとRefresh Tokenを取得する。
  4. Access TokenをAuthorizationヘッダに設定してRESTコールする。
って感じ。

1. 認可画面

認可画面は以下になります。

https://{Sharepoint URL}/_layouts/15/OAuthAuthorize.aspx?IsDlg=1&client_id=&response_type=code&redirect_uri=&state={state}

IsDlg=1はダイアログの場合のパラメータで、このパラメータを設定すると、ダイアログ用の画面(サイドバーとかトップメニューが無い画面)に遷移します。あとは普通のOAuth2.0のパラメータ。

SharepointURLの部分はサブサイトを使っている場合は、https://hoge.sharepoint.com/sites/fuga って感じにサブサイトの階層まで含めます。

で、Apexだとこんな感じ↓であんまりサンプルの意味がないくらいシンプルっす。

public String createGetAuthorizationCodeUrl(String siteUri, String targetSalesforceId) {
    return 'https://' + SHAREPOINT_REALM + siteUri + GET_CODE_URI + 
      '?IsDlg=1&client_id=' + CLIENT_ID + '&response_type=code&redirect_uri=' + 
      REDIRECT_URI + '&state=' + this.state;
}

2. コールバックURLでcodeの取得

対象のURL(SalesforceだとVF)にパラメータcodeが付与された形で、リダイレクトされます。

ApexではApexPages.currentPage().getParameters()、Visualforceでは{!$CurrentPage.parameters.code}で取得する。

state値のチェックも忘れずに。

3. 取得したcodeからAccess TokenとRefresh Tokenを取得

以下のようにして、トークンを取得。
public void getAccessTokenByCode(String code) {
    HttpRequest req = new HttpRequest();
    req.setEndpoint('https://accounts.accesscontrol.windows.net/' + O365_REALM + '/tokens/OAuth/2');
    req.setMethod('POST');
    req.setHeader('Content-Type', 'application/x-www-form-urlencoded');

    req.setBody(Utility.getParam(new Map<String, String>{
        'grant_type' => 'authorization_code',
        'client_id' => CLIENT_ID + '@' + O365_REALM,
        'client_secret' => CLIENT_SECRET,
        'code' => code,
        'redirect_uri' => REDIRECT_URI,
        'resource' => '00000003-0000-0ff1-ce00-000000000000/' + SHAREPOINT_REALM + '@' + O365_REALM
    }));

    Http http = new Http();
    HTTPResponse res = http.send(req);
    if (res.getStatus() == 'Unauthorized') {
        throw new HttpStatusException(res.getStatus() + ':' + res.getBody());
    }
    String jsonBody = res.getBody();

    Map<String, Object> tokenMap = (Map<String, Object>)JSON.deserializeUntyped(jsonBody);

    this.currentUser.SharepointAccessToken__c = String.valueOf(tokenMap.get('access_token'));
    this.currentUser.SharepointRefreshToken__c = String.valueOf(tokenMap.get('refresh_token'));
    update this.currentUser;
}

このコードではユーザオブジェクトにトークン情報を保持させて、Access TokenがexpireしたときにRefresh Tokenで再取得させるように、HttpStatusExceptionというカスタムなExceptionクラスをスローしています。

ちなみに、いつもどおり手抜きサンプルなのでステータスがUnauthorizedっていうのだけじゃなくて、他にも条件を加えないと正確にexpireを検知できません。

4. 取得したAccessTokenでREST APIをコール

Authorizationヘッダに"Bearer "+AccessTokenを設定して各REST APIをコールする。

以下は特定のフォルダーのファイル一覧を取得するREST APIをコールするコード。

private Map<String, Object> getFolderItems(String folderName) {
    HttpRequest req = new HttpRequest();
    req.setEndpoint('https://' + SHAREPOINT_REALM + siteUri + API_ROOT_URI + '/getfolderbyserverrelativeurl(\'' + folderName + '\')/files');
    req.setMethod('GET');
    req.setHeader('Authorization', 'Bearer ' + this.currentUser.SharepointAccessToken__c);
    req.setHeader('Accept', 'application/json; odata=verbose');
    req.setHeader('Content-Type', 'application/x-www-form-urlencoded');

    Http http = new Http();
    HTTPResponse res = http.send(req);

    if (res.getStatus() == 'Unauthorized') {
    	throw new HttpStatusException(res.getStatus() + ':' + res.getBody());
    }

    return (Map<String, Object>)JSON.deserializeUntyped(res.getBody());
}

普通のOAuth2.0なのに、対象のエンドポイントとかアプリ登録方法に関する情報が少ないし散在してたので、ここまでたどり着くのに時間かかりました。

もうちょっとDeveloperに優しいReferenceにしてくれ…っていう感想。

このエントリーをはてなブックマークに追加