====== Java consuming SharePoint REST API ====== SharePoint2016オンプレ環境のREST APIと連携するJava Clientの実装例を紹介します。 {{keywords>consuming REST client}} ====== SharePoint RESTサービスの概要 ====== %%SharePoint%% 2016ではREST(REpresentational State Transfer)サービスを導入しています。\\ これにより開発者は、標準のREST Web要求をサポートするテクノロジを使用して%%SharePoint%%データとリモートで対話できるようになました。\\ つまり、クライアントアプリケーションからREST Webテクノロジと標準のOData(Open Data Protocol)構文を使用して、CRUD操作が実行できるようになりました。 ====== SharePoint RESTサービスエンドポイントを決定する ====== %%SharePoint%% リソース用のRESTエンドポイントを作成するには、次の手順に従います。 1. REST サービスの参照から始めます。 http:///_api 2. 適切なエントリポイントを指定します。 次にその例を示します。 http:///_api/web 3. エントリポイントから、アクセスする特定のリソースに移動します。\\ これには、クライアントオブジェクトモデルのメソッドに対応するエンドポイントのパラメータの指定が含まれます。\\ 次にその例を示します。 http:///_api/web/lists/getbytitle('listname') ====== client.svcサービスの直接参照の代わりに_apiを使用する ====== %%SharePoint%% REST サービスのアーキテクチャ((アーキテクチャはこちらを参考してください。[[https://docs.microsoft.com/ja-jp/sharepoint/dev/sp-add-ins/get-to-know-the-sharepoint-rest-service|SharePoint REST サービスのアーキテクチャ]]))を見ると分かりますが、client.svcがREST サービスへの参照するURIを認識し受け入れます。\\ たとえば、%%http:///_vti_bin/client.svc/web/lists%% でアクセスできます。\\ しかし、これに加え、後に説明するパラメーターが入ってくるとURLが長くなり、URL制限の256文字に引っかかります。\\ これを回避するために、_apiを使用してclient.svc web サービスを明示的に参照する必要性を取り除きます。\\ _apiを使用することで、URLを短縮し、残り部分を構築するために使用できる文字数を増やすことができます。 ====== REST エンドポイント URIでパラメータを指定する ====== %%SharePoint%%ではOData仕様が拡張されており、かっこ()を使用してメソッドのパラメーターとインデックスの値を指定することができます。\\ 複数のパラメーターを指定するには、次のように、名前と値をペアにしたパラメーターを指定し、各パラメーターをコンマで区切ります。 http:///_api/web/getAvailableWebTemplates(lcid=1033, includeCrossLanguage=true) ====== context infoへのHTTP要求例 ====== form digest value((REST APIを利用してサイトにオブジェクトを生成するためには、
を指定する必要があります))はcontext情報から取得可能です。\\ context情報のHTTP要求例を次に示します。 ^区分^value^ |endpoint|%%http:///_api/contextinfo%%| |HTTP method|POST| |HTTP header|content-type: application/json;odata=verbose| | |accept: application/json;odata=verbose| ====== フォルダー作成のHTTP要求例 ====== e-キャビネットにフォルダーを作成するHTTP要求例を次に示します。 ^区分^value^ |endpoint|%%http:///_api/web/folders/add('/document library relative url/folder name')%%| |HTTP method|POST| |HTTP header|content-type: application/json;odata=verbose| | |accept: application/json;odata=verbose| | |%%X-RequestDigest: %%| ここでdocument library relative url((例) = %%http://sps-op.example.com/sites/qua-app%%, document library = Test Data の場合,\\ document library relative url = sites/qua-app/Test%20Data\\ ※ ここで、%20は半角スペースを表します。(半角スペースをエスケープした形) ))はのhost対しての相対パスを指定します。 ====== ドキュメント・ライブラリの指定フォルダーへファイル追加のHTTP要求例 ====== あるドキュメント・ライブラリの指定フォルダーにファイルを追加するHTTP要求例を次に示します。 ^区分^value^ |endpoint|%%http:///_api/web/getfolderbyserverrelativeurl('/document library relative url/folder name')/files/add(overwrite=true, url='file name')%%| |HTTP method|POST| |HTTP header|content-type: multipart/form-data| | |accept: application/json;odata=verbose| | |%%X-RequestDigest: %%| ====== REST Clientの実装概要 ====== REST サービスへのHTTP要求/応答を実装するために、Spring-web及びApacheのhttpclientを利用します。\\ また、ResponseのJSONを解析するために、json-libを利用します。\\ その他、エンドポイントURLのエスケープにgoogleのguavaライブラリを、ロギングにslf4jを使っています。\\ ===== 必要ライブラリ ===== 以下にREST Client実装で必要なライブラリを示します。\\ 必要ライブラリをbuild-pathに追加してください。 ^ライブラリ^説明^ |httpclient-4.5.6.jar|Apache httpclient| |httpcore-4.4.10.jar|httpclientの依存ライブラリ| |commons-codec-1.10.jar|httpclientの依存ライブラリ| |logback-classic-1.2.3.jar|loggingライブラリ| |logback-core-1.2.3.jar|logback-classicの依存ライブラリ| |slf4j-api-1.7.25.jar|logback-classicの依存ライブラリ| |spring-core-4.3.18.RELEASE.jar|spring-webの依存ライブラリ| |spring-web-4.3.18.RELEASE.jar|spring-web| |spring-aop-4.3.18.RELEASE.jar|spring-webの依存ライブラリ| |spring-beans-4.3.18.RELEASE.jar|spring-webの依存ライブラリ| |spring-context-4.3.18.RELEASE.jar|spring-webの依存ライブラリ| |spring-expression-4.3.18.RELEASE.jar|spring-contextの依存ライブラリ| |guava-25.1-android.jar|google guava| |json-20210307.jar|JSON解析用ライブラリ| ===== SharePointService.java code snippet ===== %%SharePointService%%コードの一部を示します。 /** * return Form Digest value * @return Form Digest value */ public String getFormDigestValue() { String _output = ""; try { //endpoint String _endpoint = MessageFormat.format("{0}/_api/contextinfo", properties.getProperty("siteUrl")); //request entity RequestEntity _requestEntity = RequestEntity .post(new URI(_endpoint)) .header("content-type", "application/json;odata=verbose") .header("accept", "application/json;odata=verbose") .body(null); RestTemplate _restTemplate = new RestTemplate(); _restTemplate.setRequestFactory(buildHttpComponentsClientHttpRequestFactory(properties.getProperty("username"), properties.getProperty("password"), properties.getProperty("host"), properties.getProperty("domain"))); ResponseEntity _responseEntity = _restTemplate.exchange(_requestEntity, String.class); if (_responseEntity.getStatusCode() != HttpStatus.OK) { throw new RuntimeException("Failed : HTTP error code : " + _responseEntity.getStatusCode()); } logger.debug("response body=" + _responseEntity.getBody()); //parse json _output = RestResponseUtil.parseJsonDigestValue(_responseEntity.getBody()); } catch (Exception e) { logger.debug(e.getMessage(), e); }//try~catch return _output; } /** * Create a folder in the relative document library.
* * @param digestValue Form Digest value * @param folderName Folder name * @return relative folder path */ public String createEcabinetFolder(String digestValue, String folderName) { String _output = ""; try { //endpoint String _endpoint = MessageFormat.format("{0}/_api/web/folders/add(''/{1}/{2}'')", properties.getProperty("siteUrl"), properties.getProperty("libraryName"), folderName); //escape url String _requestUrl = UrlEscapers.urlFragmentEscaper().escape(_endpoint); logger.debug("endpoint=" + _requestUrl); //request entity RequestEntity _requestEntity = RequestEntity .post(new URI(_requestUrl)) .header("content-type", "application/json;odata=verbose") .header("accept", "application/json;odata=verbose") .header("X-RequestDigest", digestValue) .body(null); RestTemplate _restTemplate = new RestTemplate(); _restTemplate.setRequestFactory(buildHttpComponentsClientHttpRequestFactory(properties.getProperty("username"), properties.getProperty("password"), properties.getProperty("host"), properties.getProperty("domain"))); ResponseEntity _responseEntity = _restTemplate.exchange(_requestEntity, String.class); if (_responseEntity.getStatusCode() != HttpStatus.OK) { throw new RuntimeException("Failed : HTTP error code : " + _responseEntity.getStatusCode()); } logger.debug("response body=" + _responseEntity.getBody()); //parse json _output = RestResponseUtil.parseJsonServletRelativeUrl(_responseEntity.getBody()); } catch (Exception e) { logger.debug(e.getMessage(), e); }//try~catch return _output; } /** * Add a file to the folder in the relative document library.
* * @param digestValue * @param sourceUrl * @param folderName * @return relative file path */ public String addFileToEcabinet(String digestValue, String sourceUrl, String folderName) { String _output = ""; try { File _f = new File(sourceUrl); //endpoint String _endpoint = MessageFormat.format("{0}/_api/web/getfolderbyserverrelativeurl(''/{1}/{2}'')/files/add(overwrite=true, url=''{3}'')", properties.getProperty("siteUrl"), properties.getProperty("libraryName"), folderName, _f.getName()); //escape url String _requestUrl = UrlEscapers.urlFragmentEscaper().escape(_endpoint); logger.debug("endpoint=" + _requestUrl); //request entity RequestEntity _requestEntity = RequestEntity .post(new URI(_requestUrl)) .contentType(MediaType.MULTIPART_FORM_DATA) .header("accept", "application/json;odata=verbose") .header("X-RequestDigest", digestValue) .contentLength(_f.length()) .body(new FileSystemResource(_f)); RestTemplate _restTemplate = new RestTemplate(); _restTemplate.setRequestFactory(buildHttpComponentsClientHttpRequestFactory(properties.getProperty("username"), properties.getProperty("password"), properties.getProperty("host"), properties.getProperty("domain"))); ResponseEntity _responseEntity = _restTemplate.exchange(_requestEntity, String.class); if (_responseEntity.getStatusCode() != HttpStatus.OK) { throw new RuntimeException("Failed : HTTP error code : " + _responseEntity.getStatusCode()); } logger.debug("response body=" + _responseEntity.getBody()); //parse json _output = RestResponseUtil.parseJsonServletRelativeUrl(_responseEntity.getBody()); } catch (Exception e) { logger.debug(e.getMessage(), e); }//try~catch return _output; } /** * build HttpComponentsClientHttpRequestFactory
* * @param userName SharePoint userName * @param password SharePoint password * @param host Host * @param domain Domain * @return HttpComponentsClientHttpRequestFactory */ private HttpComponentsClientHttpRequestFactory buildHttpComponentsClientHttpRequestFactory(String userName, String password, String host, String domain) throws Exception { //create connection manager PoolingHttpClientConnectionManager _cm = new PoolingHttpClientConnectionManager(); _cm.setMaxTotal(128); _cm.setDefaultMaxPerRoute(24); //request configuration RequestConfig.Builder _requestBuilder = RequestConfig.custom().setConnectTimeout(5000).setSocketTimeout(10000); //build registry and register authentication to its registry Registry _authSchemeRegistry = RegistryBuilder.create() .register(AuthSchemes.NTLM, new NTLMSchemeFactory()) .register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory()).build(); //create NTCredential CredentialsProvider _credentialProvider = new BasicCredentialsProvider(); _credentialProvider.setCredentials(AuthScope.ANY, new NTCredentials(userName, password, host, domain)); //create HttpClient HttpClientBuilder _builder = HttpClientBuilder.create() .setConnectionManager(_cm) .setDefaultRequestConfig(_requestBuilder.build()) .setDefaultAuthSchemeRegistry(_authSchemeRegistry) .setDefaultCredentialsProvider(_credentialProvider); HttpComponentsClientHttpRequestFactory _factory = new HttpComponentsClientHttpRequestFactory(_builder.build()); return _factory; }
====== reference ====== - [[https://jira.apache.org/jira/browse/HTTPCLIENT-1881|NTLM authentication against ntlm.herokuapp.com]] - [[https://blog.ch.atosconsulting.com/interoperability-between-java-and-sharepoint-2013-on-premises/|Interoperability between Java and SharePoint 2013 on Premises]] - [[https://docs.microsoft.com/ja-jp/sharepoint/dev/sp-add-ins/get-to-know-the-sharepoint-rest-service| SharePoint REST サービスの概要]] - [[https://msdn.microsoft.com/en-us/library/office/dn450841.aspx|Files and folders REST API reference]] ~~DISCUSSION~~