テンプレート処理中にエラーが発生しました。
Java method "com.liferay.portal.json.JSONFactoryImpl.createJSONObject(String)" threw an exception when invoked on com.liferay.portal.json.JSONFactoryImpl object "com.liferay.portal.json.JSONFactoryImpl@51577ebb"; see cause exception in the Java stack trace.

----
FTL stack trace ("~" means nesting-related):
	- Failed at: navigationJSONObject = jsonFactoryUti...  [in template "17855804202317#32484267#LEARN-ARTICLE-NAV" at line 4, column 9]
----
1<#assign 
2	groupFriendlyURL = themeDisplay.getScopeGroup().getFriendlyURL() 
3	groupPathFriendlyURLPublic = themeDisplay.getPathFriendlyURLPublic() + groupFriendlyURL 
4	navigationJSONObject = jsonFactoryUtil.createJSONObject(navigation.getData()) 
5	navigationMenuItems = 
6
7			"Analytics Cloud": { 
8				"image": "/documents/d${groupFriendlyURL}/analytics_c-svg", 
9				"title": "Analytics Cloud", 
10				"url": "analytics-cloud" 
11			}, 
12			"Commerce": { 
13				"image": "/documents/d${groupFriendlyURL}/commerce_product-svg", 
14				"title": "Commerce", 
15				"url": "commerce" 
16			}, 
17			"DXP": { 
18				"image": "/documents/d${groupFriendlyURL}/dxp_p-svg", 
19				"title": "DXP / Portal", 
20				"url": "dxp" 
21			}, 
22			"Liferay Cloud": { 
23				"image": "/documents/d${groupFriendlyURL}/dxp_c-svg", 
24				"title": "Liferay Cloud", 
25				"url": "liferay-cloud" 
26			}, 
27			"Reference": { 
28				"image": "/documents/d${groupFriendlyURL}/reference-svg", 
29				"title": "Reference", 
30				"url": "reference" 
31
32
33 
34	breadcrumbJSONArray = navigationJSONObject.getJSONArray("breadcrumb") 
35	childrenJSONArray = navigationJSONObject.getJSONArray("children") 
36	parentJSONObject = navigationJSONObject.getJSONObject("parent") 
37	productJSONObject = breadcrumbJSONArray.getJSONObject(breadcrumbJSONArray.length()-1)!navigationJSONObject.getJSONObject("self") 
38	siblingsJSONArray = navigationJSONObject.getJSONArray("siblings") 
39/> 
40 
41<div class="learn-article-nav"> 
42	<#if productJSONObject?has_content && productJSONObject.getString("title")?has_content && navigationMenuItems[productJSONObject.getString("title")]?has_content && navigationMenuItems[productJSONObject.getString("title")].title?has_content> 
43		<div 
44			class="dropdown learn-article-nav-root learn-dropdown" 
45
46			<div class="learn-article-nav-item"> 
47				<div class="d-flex"> 
48					<div class="learn-article-nav-image"> 
49						<img 
50							class="lexicon-icon lexicon-icon-caret-bottom product-icon" 
51							role="presentation" 
52							src='${navigationMenuItems[productJSONObject.getString("title")].image}' 
53							viewBox="0 0 512 512" 
54						/> 
55					</div> 
56 
57					<span class="learn-article-nav-text">${navigationMenuItems[productJSONObject.getString("title")].title}</span> 
58				</div> 
59 
60				<div id="dropdown-icon"> 
61					<svg 
62						class="lexicon-icon lexicon-icon-caret-bottom" 
63						role="presentation" 
64						viewBox="0 0 512 512" 
65
66						<use xlink:href="/o/admin-theme/images/clay/icons.svg#caret-bottom"></use> 
67					</svg> 
68				</div> 
69			</div> 
70 
71			<ul class="dropdown-menu learn-dropdown-menu"> 
72				<#list navigationMenuItems as key, value> 
73					<li> 
74						<a 
75							class="dropdown-item learn-article-nav-item" 
76							href="/w/${navigationMenuItems[key].url}/index" 
77							tabindex="4" 
78
79							<span class="d-flex"> 
80								<span class="learn-article-nav-image"> 
81									<img 
82										class="lexicon-icon lexicon-icon-caret-bottom product-icon mt-0 mr-2" 
83										role="presentation" 
84										src="${value.image}"height: 25px; margin-left: 5px; max-width: none; width: 25px; 
85										viewBox="0 0 512 512" 
86									/> 
87								</span> 
88								<span class="learn-article-nav-text">${value.title}</span> 
89							</span> 
90 
91							<#if navigationMenuItems[productJSONObject.getString("title")].url == value.url> 
92								<span> 
93									<@clay["icon"] symbol="check" /> 
94								</span> 
95							</#if> 
96						</a> 
97					</li> 
98				</#list> 
99			</ul> 
100		</div> 
101	</#if> 
102 
103	<div class="learn-article-nav-content"> 
104		<#if parentJSONObject?has_content && parentJSONObject.getString("url")?has_content> 
105			<div class="learn-article-nav-item learn-article-nav-parent liferay-nav-item p-2"> 
106				<div class="mr-2"> 
107					<a 
108						href='${parentJSONObject.getString("url")}' 
109
110						<svg 
111							class="lexicon-icon lexicon-icon-angle-left" 
112							role="presentation" 
113							viewBox="0 0 512 512" 
114
115							<use xlink:href="/o/admin-theme/images/clay/icons.svg#angle-left"></use> 
116						</svg> 
117					</a> 
118				</div> 
119 
120				<span>${parentJSONObject.getString("title")}</span> 
121			</div> 
122		</#if> 
123 
124		<#if childrenJSONArray.length() gt 0> 
125			<ul class="m-0 p-2"> 
126				<#list 0..childrenJSONArray.length()-1 as i> 
127					<li class="learn-article-nav-item"> 
128						<a 
129							class='liferay-nav-item ${(navigationJSONObject.getJSONObject("self").url == childrenJSONArray.getJSONObject(i).url)?then("selected", "")}' 
130							href="${childrenJSONArray.getJSONObject(i).url}" 
131
132							<span>${childrenJSONArray.getJSONObject(i).getString("title")}</span> 
133						</a> 
134					</li> 
135				</#list> 
136			</ul> 
137		<#elseif siblingsJSONArray.length() gt 0> 
138			<ul class="m-0 p-2"> 
139				<#list 0..siblingsJSONArray.length()-1 as i> 
140					<li class="learn-article-nav-item"> 
141						<a 
142							class='liferay-nav-item ${(navigationJSONObject.getJSONObject("self").url == siblingsJSONArray.getJSONObject(i).url)?then("selected", "")}' 
143							href="${siblingsJSONArray.getJSONObject(i).url}" 
144
145							<span>${siblingsJSONArray.getJSONObject(i).getString("title")}</span> 
146						</a> 
147					</li> 
148				</#list> 
149			</ul> 
150		</#if> 
151	</div> 
152</div> 

RESTサービスの使用

Liferay DXPには、ほとんどのアプリケーションでRESTサービスが含まれています。 これらのサービスは完全に OpenAPI に準拠しています。 ここでは、それらを使用する方法を紹介します。 必要なステップはわずか3つです。

  1. 使用するサービスを特定します。
  2. 必要なデータを含むサイトを特定します。
  3. データにアクセスできる資格情報を使用してサービス呼び出しを行います。

この例では、Liferay DXPの新規インストールでDockerイメージを使用しています。

使用するサービスを特定する

RESTサービスを呼び出すには、実行中のLiferay DXPが必要です。

新しいLiferay インスタンスを起動し、以下を実行します。

docker run -it -m 8g -p 8080:8080 。

http://localhost:8080でLiferayへのサインインします。 メールアドレス test@liferay.com とパスワード test を使用してください。 プロンプトが表示されたら、パスワードを learn に変更します。

様々なAPIエンドポイントを見るには、ブラウザを使ってLiferayのAPI Explorerの[server]:[port]/o/apiにアクセスします。例えば、あなたの Docker インスタンスでは、ここで見つけることができます:

http://localhost:8080/o/api

APIはいくつかのカテゴリに分かれている。この例ではBlogsウィジェットからブログ記事を取得するためにBlogPostingサービスを使用していますが、公開されているどのサービスでもこの手順を使用することができます。

  1. Headless Delivery カテゴリを選択します。このカテゴリにはBlogPostingサービスが含まれています。フィルタを使用してサービスを検索できます。

  2. Show Schemas ボタンをクリックすると、画面の右側にこのカテゴリーに含まれるすべてのスキーマのリストが表示されます。

  3. BlogPostingをPUTしたいときには、そのスキーマが必要になります。

スキーマ・ブラウザを使えば、欲しいサービスを見つけて呼び出すのに便利です。

データを含むサイトを特定する

次に、デフォルトのサイトIDを見つけなければなりません:

  1. サイトメニュー(サイトメニュー)を開き、 設定サイト設定 に進む。

  2. Platform セクションで Site Configuration をクリックします。Liferay DXP バージョン 7.3 以前の場合は、 General タブをクリックします。

  3. Site ID] でサイト ID を確認します。

    サイト設定とサイト構成オプションでサイト ID を特定する

データにアクセスできる証明書を使用してサービスを呼び出す

これで呼び出しに必要なものはすべて揃いました。すべてのウェブサービスは、リクエストするデータにアクセスできるクレデンシャルを使ってアクセスしなければなりません。最も単純な方法は、URLでクレデンシャルデータを渡すBasic Authを使うことです。これは安全ではないので、この方法を使うのは開発中のみにしてください。本番環境では、OAuth2を使ってユーザーを認証してください。

以下の例では、 cURL を使用しています。

基本認証を使ってサービスを呼び出す (開発中のみ)

Basic Authを使ってサービスを呼び出すには、URLに認証情報を記述します:

curl "http://localhost:8080/o/headless-delivery/v1.0/sites/20122/blog-postings/" -u 'test@liferay.com:learn'

OAuth2を使用してサービスを呼び出す

本番環境では、OAuth2アプリケーションを作成し、OAuth2プロセスを使用して認証トークンを取得します。 トークンを取得したら、それをHTTPヘッダーに指定します。

curl -H "Authorization: Bearer d5571ff781dc555415c478872f0755c773fa159" http://localhost:8080/o/headless-delivery/v1.0/sites/20122/blog-postings

データの取得と投稿

上記のクエリを実行してすべてのブログ投稿を取得すると、何もないことがわかります。

{
  "actions" : {
    "subscribe" : {
      "method" : "PUT",
      "href" : "http://localhost:8080/o/headless-delivery/v1.0/sites/{siteId}/blog-postings/subscribe"
    },
    "unsubscribe" : {
      "method" : "PUT",
      "href" : "http://localhost:8080/o/headless-delivery/v1.0/sites/{siteId}/blog-postings/unsubscribe"
    },
    "create" : {
      "method" : "POST",
      "href" : "http://localhost:8080/o/headless-delivery/v1.0/sites/{siteId}/blog-postings"
    }
  },
  "items" : [ ],
  "lastPage" : 1,
  "page" : 1,
  "pageSize" : 20,
  "totalCount" : 0
}

まず、ブログエントリーを投稿します。

ブログエントリーの投稿

スキーマブラウザーを使用して、ブログエントリを投稿する方法を学ぶことができます。

サービスのスキーマは、Liferay DXPインスタンスで公開されます。

  1. スキーマブラウザを含むブラウザタブに戻ります。 右側にある BlogPosting エントリーをクリックして、そのスキーマを表示します(上記を参照)。 これは BlogPostingデータ構造全体を示していますが、必須フィールドは2つだけです。

    • articleBody
    • headline
  2. ブログエントリを投稿する単純なJSONドキュメントを作成します。

    {
        "headline": "Test Blog Entry from REST Services",
        "articleBody": "This article was posted via REST services provided by Liferay DXP."
    }
    
  3. リクエストを行います。

    curl --header "Content-Type: application/json" --request POST --data '{ "headline": "Test Blog Entry from REST Services", "articleBody": "This article was posted via REST services provided by Liferay DXP." }' http://localhost:8080/o/headless-delivery/v1.0/sites/20122/blog-postings -u test@liferay.com:learn
    

Liferay DXPは、ブログエントリーの完全なJSON表現を返します。

{
  "actions" : {
    "get" : {
      "method" : "GET",
      "href" : "http://localhost:8080/o/headless-delivery/v1.0/blog-postings/{blogPostingId}"
    },
    "replace" : {
      "method" : "PUT",
      "href" : "http://localhost:8080/o/headless-delivery/v1.0/blog-postings/{blogPostingId}"
    },
    "update" : {
      "method" : "PATCH",
      "href" : "http://localhost:8080/o/headless-delivery/v1.0/blog-postings/{blogPostingId}"
    },
    "delete" : {
      "method" : "DELETE",
      "href" : "http://localhost:8080/o/headless-delivery/v1.0/blog-postings/{blogPostingId}"
    }
  },
  "alternativeHeadline" : "",
  "articleBody" : "This article was posted via REST services provided by Liferay DXP.",
  "creator" : {
    "additionalName" : "",
    "contentType" : "UserAccount",
    "familyName" : "Test",
    "givenName" : "Test",
    "id" : 20125,
    "name" : "Test Test",
    "profileURL" : "/web/test"
  },
  "customFields" : [ ],
  "dateCreated" : "2020-03-06T18:02:26Z",
  "dateModified" : "2020-03-06T18:02:27Z",
  "datePublished" : "2020-03-06T18:02:00Z",
  "description" : "",
  "encodingFormat" : "text/html",
  "friendlyUrlPath" : "test-blog-entry-from-rest-services",
  "headline" : "Test Blog Entry from REST Services",
  "id" : 35215,
  "keywords" : [ ],
  "numberOfComments" : 0,
  "relatedContents" : [ ],
  "siteId" : 20122,
  "taxonomyCategories" : [ ]
}

すべてのブログエントリーを取得する

ここで、最初のクエリを繰り返して、投稿したブログエントリが表示されることを確認できます。

curl "http://localhost:8080/o/headless-delivery/v1.0/sites/20122/blog-postings/" -u 'test@liferay.com:learn'

ブログエントリのリストが返されます。 追加したエントリは、リスト内の唯一のエントリです。

{
  "actions" : {
    "subscribe" : {
      "method" : "PUT",
      "href" : "http://localhost:8080/o/headless-delivery/v1.0/sites/{siteId}/blog-postings/subscribe"
    },
    "unsubscribe" : {
      "method" : "PUT",
      "href" : "http://localhost:8080/o/headless-delivery/v1.0/sites/{siteId}/blog-postings/unsubscribe"
    },
    "create" : {
      "method" : "POST",
      "href" : "http://localhost:8080/o/headless-delivery/v1.0/sites/{siteId}/blog-postings"
    }
  },
  "items" : [ {
    "actions" : {
      "get" : {
        "method" : "GET",
        "href" : "http://localhost:8080/o/headless-delivery/v1.0/blog-postings/{blogPostingId}"
      },
      "replace" : {
        "method" : "PUT",
        "href" : "http://localhost:8080/o/headless-delivery/v1.0/blog-postings/{blogPostingId}"
      },
      "update" : {
        "method" : "PATCH",
        "href" : "http://localhost:8080/o/headless-delivery/v1.0/blog-postings/{blogPostingId}"
      },
      "delete" : {
        "method" : "DELETE",
        "href" : "http://localhost:8080/o/headless-delivery/v1.0/blog-postings/{blogPostingId}"
      }
    },
    "alternativeHeadline" : "",
    "articleBody" : "This article was posted via REST services provided by Liferay DXP.",
    "creator" : {
      "additionalName" : "",
      "contentType" : "UserAccount",
      "familyName" : "Test",
      "givenName" : "Test",
      "id" : 20125,
      "name" : "Test Test",
      "profileURL" : "/web/test"
    },
    "customFields" : [ ],
    "dateCreated" : "2020-03-06T18:02:26Z",
    "dateModified" : "2020-03-06T18:02:27Z",
    "datePublished" : "2020-03-06T18:02:00Z",
    "description" : "",
    "encodingFormat" : "text/html",
    "friendlyUrlPath" : "test-blog-entry-from-rest-services",
    "headline" : "Test Blog Entry from REST Services",
    "id" : 35215,
    "keywords" : [ ],
    "numberOfComments" : 0,
    "relatedContents" : [ ],
    "siteId" : 20122,
    "taxonomyCategories" : [ ]
  } ],
  "lastPage" : 1,
  "page" : 1,
  "pageSize" : 20,
  "totalCount" : 1
}

単一のブログエントリーを取得する

リクエストを行うたびに、Liferay DXPは他の考えられるエンドポイントを返します。 そのうちの1つは、IDによって単一のブログエントリを取得することです。 エントリーのIDがわかっている場合は、それを取得できます。

curl "http://localhost:8080/o/headless-delivery/v1.0/blog-postings/35215" -u test@liferay.com:learn

同じブログエントリーを返します。

ブログエントリーを削除する

IDがわかっている場合は、ブログエントリーを削除することもできます。

curl -X DELETE "http://localhost:8080/o/headless-delivery/v1.0/blog-postings/35215" -u test@liferay.com:learn

この場合、何も返されませんが、上記のようにリクエストすることで、エントリーが削除されたことを確認できます。

curl "http://localhost:8080/o/headless-delivery/v1.0/blog-postings/35215" -u test@liferay.com:learn

次に、Liferay DXPは、応答として次のJSONドキュメントを返します。

{
  "status" : "NOT_FOUND",
  "title" : "No BlogsEntry exists with the primary key 35215"
}

  Liferay DXPのRESTサービスを呼び出す方法を学びました。 上記の例では基本認証を使用していることに注意してください。本番環境では、OAuth2を使用して安全な方法でサービスを呼び出します。