管理AEM主機

部署AEM Headless應用程式時,需要注意AEM URL的建構方式,以確保使用正確的AEM主機/網域。 要注意的主要URL/請求型別為:

通常AEM Headless應用程式會針對GraphQL API和影像要求與單一AEM服務互動。 AEM服務會根據AEM Headless應用程式部署而變更:

AEM Headless部署型別
AEM環境
AEM服務
生產
生產
發佈
製作預覽
生產
預覽
開發
開發
發佈

若要處理部署型別排列,每個應用程式部署都是使用指定要連線的AEM服務的設定所建置。 接著會使用已設定的AEM服務主機/網域來建構AEM GraphQL API URL和影像URL。 若要判斷管理組建相依設定的正確方法,請參考AEM Headless應用程式的架構(例如React、iOS、Android™等)檔案,因為方法會因架構而異。

使用者端型別
單頁應用程式(SPA)
網頁元件/JS
行動裝置
伺服器對伺服器
AEM主機設定

下列範例是為AEM GraphQL API影像要求建構URL的可能方法,適用於幾個熱門的Headless架構和平台。

AEM GraphQL API要求

從Headless應用程式到AEM的GraphQL API的HTTPGET要求必須設定為與正確的AEM服務互動,如上方🔗的表格所述。

使用AEM Headless SDK (適用於瀏覽器式JavaScript、伺服器式JavaScript和Java™)時,AEM主機可以使用AEM服務初始化AEM Headless使用者端物件以與其連線。

開發自訂AEM Headless使用者端時,請確定AEM服務的主機可以根據組建引數引數化。

範例

以下範例說明AEM GraphQL API要求如何讓AEM主機值可供各種Headless應用程式架構設定。

React範例

此範例大致以AEM Headless React應用程式為基礎,說明如何設定AEM GraphQL API要求以根據環境變數連線至不同的AEM Services。

React應用程式應使用適用於JavaScript🔗的AEM Headless使用者端,與AEM的GraphQL API互動。 適用於JavaScript的AEM Headless使用者端所提供的AEM Headless使用者端必須透過其連線的AEM Service主機進行初始化。

React環境檔案

React使用儲存在專案根目錄中的自訂環境檔案.env檔案來定義組建特定值。 例如,.env.development檔案包含在開發期間使用的值,而.env.production包含用於生產組建的值。

  • .env.development
code language-none
# Environment variable used to specify the AEM service the React app will connect to when running under this profile
REACT_APP_AEM_HOST=https://publish-p123-e456.adobeaemcloud.com
...

可以將.env和語意描述元(例如.env.stage.env.production)後置化,以指定.env檔案供其他用途使用。 執行npm命令前設定REACT_APP_ENV,可在執行或建置React應用程式時使用不同的.env檔案。

例如,React應用程式的package.json可能包含下列scripts設定:

  • package.json
code language-none
...
"scripts": {
  "build:development": "REACT_APP_ENV=development npm run build",
  "build:stage": "REACT_APP_ENV=stage npm run build",
  "build:production": "REACT_APP_ENV=production npm run build"
},
...

AEM headless使用者端

適用於JavaScript🔗的AEM Headless使用者端包含AEM Headless使用者端,會向AEM的GraphQL API發出HTTP請求。 AEM Headless使用者端必須使用使用中.env檔案的值與其互動的AEM主機進行初始化。

  • src/api/headlessClient.js
code language-none
const { AEMHeadless } = require('@adobe/aem-headless-client-js');
...
// Get the environment variables for configuring the headless client,
// specifically the `REACT_APP_AEM_HOST` which contains the AEM service host.
const {
    REACT_APP_AEM_HOST,         // https://publish-p123-e456.adobeaemcloud.com
    REACT_APP_GRAPHQL_ENDPOINT,
} = process.env;
...

// Initialize the AEM Headless client with the AEM Service host, which dictates the AEM service provides the GraphQL data.
export const aemHeadlessClient = new AEMHeadless({
    serviceURL: REACT_APP_AEM_HOST,
    endpoint: REACT_APP_GRAPHQL_ENDPOINT
});

React useEffect(…) 勾點

自訂React useEffect掛接會呼叫AEM Headless使用者端,並代表呈現檢視的React元件以AEM主機初始化。

  • src/api/persistedQueries.js
code language-javascript
import { aemHeadlessClient , mapErrors} from "./headlessClient";
...
// The exported useEffect hook
export const getAdventureByPath = async function(adventurePath) {
    const queryVariables = {'adventurePath': adventurePath};
    return executePersistedQuery('wknd-shared/adventures-by-path', queryVariables);
}
...
// Private function that invokes the aemHeadlessClient
const executePersistedQuery = async function(persistedQueryPath, queryVariables) {
    let data;
    let errors;

    try {
        // Run the persisted query using using the aemHeadlessClient that's initialized with the AEM host
        const response = await aemHeadlessClient.runPersistedQuery(persistedQueryPath, queryVariables);
        // The GraphQL data is stored on the response's data field
        data = response.data;
        errors = response.errors ? mapErrors(response.errors) : undefined;
    } catch (e) {
        console.error(e.toJSON());
        errors = e;
    }

    return {data, errors};
}

React元件

自訂useEffect鉤點useAdventureByPath已匯入,並用於使用AEM Headless使用者端取得資料,最終將內容呈現給一般使用者。

  • 'src/components/AdventureDetail.js'
code language-javascript
import { useAdventureByPath } from './api/persistedQueries';
...
// Query AEM GraphQL APIs via the useEffect hook that invokes the AEM Headless client initialized with the AEM host
let { data, error } = useAdventureByPath('/content/dam/wknd-shared/en/adventures/bali-surf-camp/adobestock-175749320.jpg')

...
iOS™範例

此範例以範例AEM Headless iOS™應用程式為基礎,說明如何設定AEM GraphQL API要求以根據組建特定設定變數連線至不同的AEM主機。

iOS™應用程式需要自訂AEM Headless使用者端,才能與AEM的GraphQL API互動。 必須撰寫AEM Headless使用者端,以設定AEM服務主機。

建置設定

XCode組態檔案包含預設組態詳細資訊。

  • Config.xcconfig
code language-swift
// The http/https protocol scheme used to access the AEM_HOST
AEM_SCHEME = https

// Target hostname for AEM service, do not include the scheme: http:// or https://
AEM_HOST = publish-p123-e789.adobeaemcloud.com
...

初始化自訂AEM Headless使用者端

範例AEM Headless iOS應用程式使用以AEM_SCHEMEAEM_HOST的設定值初始化的自訂AEM Headless使用者端。

code language-swift
...
let aemScheme: String = try Configuration.value(for: "AEM_SCHEME")  // https
let aemHost: String = try Configuration.value(for: "AEM_HOST")      // publish-p123-e456.adobeaemcloud.com

let aemHeadlessClient = Aem(scheme: aemScheme, host: aemHost);

自訂AEM Headless使用者端(api/Aem.swift)包含方法makeRequest(..),其會以設定的AEM schemehost為AEM GraphQL API要求加上前置詞。

  • api/Aem.swift
code language-swift
/// #makeRequest(..)
/// Generic method for constructing and executing AEM GraphQL persisted queries
private func makeRequest(persistedQueryName: String, params: [String: String] = [:]) -> URLRequest {
    // Encode optional parameters as required by AEM
    let persistedQueryParams = params.map { (param) -> String in
        encode(string: ";\(param.key)=\(param.value)")
    }.joined(separator: "")

    // Construct the AEM GraphQL persisted query URL, including optional query params
    let url: String = "\(self.scheme)://\(self.host)/graphql/execute.json/" + persistedQueryName + persistedQueryParams;

    var request = URLRequest(url: URL(string: url)!);

    return request;
}

可以建立新的組建組態檔以連線到不同的AEM服務。 AEM_SCHEMEAEM_HOST的組建特定值是根據XCode中選取的組建使用,導致自訂AEM Headless使用者端連線到正確的AEM服務。

Android™範例

此範例以範例AEM Headless Android™應用程式為基礎,說明如何設定AEM GraphQL API要求以根據組建特定(或風格)設定變數連線至不同的AEM Services。

Android™應用程式(以Java™撰寫時)應使用AEM Headless Client for Java™與AEM的GraphQL API互動。 適用於Java™的AEM Headless使用者端所提供的AEM Headless使用者端必須透過其連線的AEM Service主機進行初始化。

建置組態檔

Android™應用程式會定義「productFlavors」,以針對不同用途建置成品。
此範例顯示如何定義兩種Android™產品風格,提供開發(dev)和生產(prod)使用的不同AEM服務主機(AEM_HOST)值。

在應用程式的build.gradle檔案中,已建立名為env的新flavorDimension

env維度中,定義了兩個productFlavorsdevprod。 每個productFlavor都使用buildConfigField來設定組建特定的變數,這些變數定義要連線的AEM服務。

  • app/build.gradle
code language-gradle
android {
    ...
    flavorDimensions 'env'
    productFlavors {
        dev {
            dimension 'env'
            applicationIdSuffix '.dev'
            buildConfigField "String", "AEM_HOST", '"http://10.0.2.2:4503"'
            ...
        }
        prod {
            dimension 'env'
            buildConfigField "String", "AEM_HOST", '"https://publish-p123-e789.adobeaemcloud.com"'
            ...
        }
    }
    ...
}

Android™載入器

使用buildConfigField欄位中的AEM_HOST值初始化AEM Headless Client for Java™提供的AEMHeadlessClient產生器。

  • app/src/main/java/com/adobe/wknd/androidapp/loader/AdventuresLoader.java
code language-java
public class AdventuresLoader extends AsyncTaskLoader<AdventureList> {
    ...

    @Override
    public AdventureList loadInBackground() {
        ...
        // Initialize the AEM Headless client using the AEM Host exposed via BuildConfig.AEM_HOST
        AEMHeadlessClientBuilder builder = AEMHeadlessClient.builder().endpoint(BuildConfig.AEM_HOST);
        AEMHeadlessClient client = builder.build();
        // With the AEM headless client initialized, make GraphQL persisted query calls to AEM
        ...
    }
    ...
}

為不同用途建置Android™應用程式時,請指定env風格,並使用對應的AEM主機值。

AEM影像URL

從Headless應用程式到AEM的影像要求必須設定為與正確的AEM服務互動,如上表🔗中的所述。

Adobe建議使用AEMGraphQL API中透過_dynamicUrl欄位提供的最佳化影像_dynamicUrl欄位傳回無主機URL,其前置詞可為用於查詢AEM GraphQL API的AEM服務主機。 在GraphQL回應中的_dynamicUrl欄位看起來如下所示:

{
    ...
    "_dynamicUrl": "/adobe/dynamicmedia/deliver/dm-aid--dd42d814-88ec-4c4d-b5ef-e3dc4bc0cb42/example.jpg?preferwebp=true",
    ...
}

範例

以下範例說明影像URL為多種Headless應用程式架構所設定的AEM主機值加上前置詞的方式。 這些範例假設使用GraphQL查詢,這些查詢使用_dynamicUrl欄位傳回影像參考。

例如:

GraphQL持續查詢

此GraphQL查詢傳回影像參考的_dynamicUrl。 如排除主機的GraphQL回應中所示。

query ($path: String!) {
  adventureByPath(_path: $path, _assetTransform: { format: JPG, preferWebp: true }) {
    item {
      title,
      primaryImage {
        ... on ImageRef {
          _dynamicUrl
        }
      }
    }
  }
}

GraphQL回應

此GraphQL回應會傳回影像參考的_dynamicUrl (排除主機)。

{
  "data": {
    "adventureByPath": {
      "item": {
        "adventurePrimaryImage": {
          "_dynamicUrl": "/adobe/dynamicmedia/deliver/dm-aid--de43411-88ec-4c4d-b5ef-e3dc4bc0cb42/adobestock-175749320.jpg?preferwebp=true",
        }
      }
    }
  }
}
React範例

此範例以範例AEM Headless React應用程式為基礎,說明如何將影像URL設定為根據環境變數連線到正確的AEM Services。

此範例顯示如何使用可設定的REACT_APP_AEM_HOST React環境變數為影像參考_dynamicUrl欄位加上前置詞。

React環境檔案

React使用儲存在專案根目錄中的自訂環境檔案.env檔案來定義組建特定值。 例如,.env.development檔案包含在開發期間使用的值,而.env.production包含用於生產組建的值。

  • .env.development
code language-none
# Environment variable used to specify the AEM service the React app will connect to when running under this profile
REACT_APP_AEM_HOST=https://publish-p123-e456.adobeaemcloud.com
...

可以將.env和語意描述元(例如.env.stage.env.production)後置化,以指定.env檔案供其他用途使用。 執行npm命令前設定REACT_APP_ENV,可在執行或建置React應用程式時使用不同的.env檔案。

例如,React應用程式的package.json可能包含下列scripts設定:

  • package.json
code language-none
...
"scripts": {
  "build:development": "REACT_APP_ENV=development npm run build",
  "build:stage": "REACT_APP_ENV=stage npm run build",
  "build:production": "REACT_APP_ENV=production npm run build"
},
...

React元件

React元件會匯入REACT_APP_AEM_HOST環境變數,並在影像_dynamicUrl值加上前置詞,以提供完全可解析的影像URL。

這個相同的REACT_APP_AEM_HOST環境變數可用來初始化useAdventureByPath(..)個自訂useEffect勾點所使用的AEM Headless使用者端,該勾點用來從AEM擷取GraphQL資料。 使用相同的變數來建構GraphQL API要求作為影像URL,請確定React應用程式會在兩個使用案例中與相同的AEM服務互動。

  • 'src/components/AdventureDetail.js'
code language-javascript
...
// Import the AEM origin from the app's environment configuration
const AEM_HOST = env.process.REACT_APP_AEM_HOST; // https://publish-p123-e456.adobeaemcloud.com

let { data, error } = useAdventureByPath('/content/dam/wknd-shared/en/adventures/bali-surf-camp/bali-surf-camp')

return (
    // Prefix the image src URL with the AEM host
    <img src={AEM_HOST + data.adventureByPath.item.primaryImage._dynamicUrl }>
    {/* Resulting in: <img src="https://publish-p123-e456.adobeaemcloud.com/adobe/dynamicmedia/deliver/dm-aid--de43411-88ec-4c4d-b5ef-e3dc4bc0cb42/adobestock-175749320.jpg"/>  */}
)
iOS™範例

此範例以範例AEM Headless iOS™應用程式為基礎,說明如何設定AEM影像URL以根據組建特定設定變數連線至不同的AEM主機。

建置設定

XCode組態檔案包含預設組態詳細資訊。

  • Config.xcconfig
code language-swift
// The http/https protocol scheme used to access the AEM_HOST
AEM_SCHEME = https

// Target hostname for AEM service, do not include the scheme: http:// or https://
AEM_HOST = publish-p123-e789.adobeaemcloud.com
...

影像URL產生器

Aem.swift自訂AEM Headless使用者端實作中,自訂函式imageUrl(..)會採用GraphQL回應中_dynamicUrl欄位所提供的影像路徑,並在其前面加上AEM的主機。 然後每當影像轉譯時,就會在iOS檢視中叫用此函式。

  • WKNDAdventures/AEM/Aem.swift
code language-swift
class Aem: ObservableObject {
    let scheme: String
    let host: String
    ...
    init(scheme: String, host: String) {
        self.scheme = scheme
        self.host = host
    }
    ...
    /// Prefixes AEM image dynamicUrl with the AEM scheme/host
    func imageUrl(dynamicUrl: String) -> URL {
        return URL(string: "\(self.scheme)://\(self.host)\(dynamicUrl)")!
    }
    ...
}

iOS檢視

iOS檢視並加上影像_dynamicUrl值的前置詞,以提供完全可解析的影像URL。

  • WKNDAdventures/Views/AdventureListItemView.swift
code language-swift
import SDWebImageSwiftUI
...
struct AdventureListItemView: View {
    @EnvironmentObject private var aem: Aem

    var adventure: Adventure

    var body: some View {
        HStack {
            // Path the image dynamicUrl to `aem.imageUrl(..)` to prepend the AEM service host
            AdventureRowImage(imageUrl: aem.imageUrl(dynamicUrl: adventure.image()))
            Text(adventure.title)
            Spacer()
        }
    }
}
...

可以建立新的組建組態檔以連線到不同的AEM服務。 AEM_SCHEMEAEM_HOST的組建特定值是根據XCode中選取的組建使用,導致自訂AEM Headless使用者端與正確的AEM服務互動。

Android™範例

此範例是以範例AEM Headless Android™應用程式為基礎,說明如何設定AEM影像URL,以根據組建特定(或風格)設定變數連線至不同的AEM Services。

建置組態檔

Android™應用程式會定義「productFlavors」,以針對不同用途建置成品。
此範例顯示如何定義兩種Android™產品風格,提供開發(dev)和生產(prod)使用的不同AEM服務主機(AEM_HOST)值。

在應用程式的build.gradle檔案中,已建立名為env的新flavorDimension

env維度中,定義了兩個productFlavorsdevprod。 每個productFlavor都使用buildConfigField來設定組建特定的變數,這些變數定義要連線的AEM服務。

  • app/build.gradle
code language-gradle
android {
    ...
    flavorDimensions 'env'
    productFlavors {
        dev {
            dimension 'env'
            applicationIdSuffix '.dev'
            buildConfigField "String", "AEM_HOST", '"http://10.0.2.2:4503"'
            ...
        }
        prod {
            dimension 'env'
            buildConfigField "String", "AEM_HOST", '"https://publish-p123-e789.adobeaemcloud.com"'
            ...
        }
    }
    ...
}

正在載入AEM影像

Android™使用ImageGetter從AEM擷取及本機快取影像資料。 在prepareDrawableFor(..)中,AEM服務主機(定義於使用中的組建組態)是用來為影像路徑加上前置詞,以建立AEM的可解析URL。

  • app/src/main/java/com/adobe/wknd/androidapp/loader/RemoteImagesCache.java
code language-java
...
public class RemoteImagesCache implements Html.ImageGetter {
    ...
    private final Map<String, Drawable> drawablesByPath = new HashMap<>();
    ...
    public void prepareDrawableFor(String path) {
        ...

        // Prefix the image path with the build config AEM_HOST variable
        String urlStr = BuildConfig.AEM_HOST + path;

        URL url = new URL(urlStr);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        // Get the image data from AEM
        Drawable drawable = Drawable.createFromStream(is, new File(path).getName());
        ...
        // Save the image data into the cache using the path as the key
        drawablesByPath.put(path, drawable);
        ...
    }

    @Override
    public Drawable getDrawable(String dynamicUrl) {
        // Get the image data from the cache using the dynamicUrl as the key
        return drawablesByPath.get(dynamicUrl);
    }
}

Android™ view

Android™檢視會使用GraphQL回應中的_dynamicUrl值,透過RemoteImagesCache取得影像資料。

  • app/src/main/java/com/adobe/wknd/androidapp/AdventureDetailFragment.java
code language-java
...
public class AdventureDetailFragment extends Fragment implements LoaderManager.LoaderCallbacks<Adventure> {
    ...
    private ImageView adventureDetailImage;
    ...

    private void updateContent() {
        ...
        adventureDetailImage.setImageDrawable(RemoteImagesCache.getInstance().getDrawable(adventure.getPrimaryImageDynamicUrl()));
        ...
    }
...
}

為不同用途建置Android™應用程式時,請指定env風格,並使用對應的AEM主機值。

recommendation-more-help
e25b6834-e87f-4ff3-ba56-4cd16cdfdec4