Beim Bereitstellen einer AEM Headless-Anwendung muss beachtet werden, wie AEM-URLs aufgebaut sind, um sicherzustellen, dass der richtige AEM-Host/die richtige Domain verwendet wird. Die folgenden primären URL-/Anfragetypen sind zu beachten:
In der Regel interagiert eine AEM Headless-App mit einem einzigen AEM-Service für GraphQL-API- und Bildanforderungen. Der AEM-Dienst ändert sich je nach Bereitstellung der AEM Headless-App:
Typ der AEM Headless-Bereitstellung | AEM-Umgebung | AEM-Service |
---|---|---|
Produktion | Produktion | Veröffentlichen |
Authoring-Vorschau | Produktion | Vorschau |
Entwicklung | Entwicklung | Veröffentlichen |
Um Permutationen des Bereitstellungstyps zu handhaben, wird jede App-Bereitstellung mit einer Konfiguration erstellt, die angibt, mit welchem AEM-Service eine Verbindung hergestellt werden soll. Der Host/die Domain des konfigurierten AEM-Services wird dann verwendet, um die AEM GraphQL-API-URLs und Bild-URLs zu erstellen. Um den richtigen Ansatz für die Verwaltung von Build-abhängigen Konfigurationen zu bestimmen, verweisen Sie auf die Dokumentation zum Framework der AEM Headless-App (z. B. React, iOS, Android™ usw.), da der Ansatz je nach Framework unterschiedlich ist.
Client-Typ | Single Page App (SPA) | Web-Komponente/JS | Mobilgerät | Server-zu-Server |
---|---|---|---|---|
AEM-Host-Konfiguration | ✔ | ✔ | ✔ | ✔ |
Im Folgenden finden Sie Beispiele für mögliche Ansätze zum Erstellen von URLs für AEM GraphQL-API- und Bildanfragen für mehrere beliebte Headless-Frameworks und Plattformen.
Die HTTP-GET-Anfragen von der Headless-App an AEM GraphQL-APIs müssen für die Interaktion mit dem richtigen AEM-Service konfiguriert werden, wie in der obigen Tabelle beschrieben.
Bei Verwendung von AEM Headless-SDKs (für Browser-basiertes JavaScript, serverbasiertes JavaScript und Java™ verfügbar) kann ein AEM-Host das AEM Headless-Client-Objekt mit dem AEM-Service initialisieren, mit dem eine Verbindung hergestellt werden soll.
Stellen Sie bei der Entwicklung eines benutzerdefinierten AEM Headless-Clients sicher, dass der Host des AEM-Services basierend auf Build-Parametern parametrisierbar ist.
Im Folgenden finden Sie Beispiele dafür, wie AEM GraphQL-API-Anfragen den Wert des AEM-Hosts für verschiedene Headless-App-Frameworks konfigurierbar machen können.
Dieses Beispiel, grob basierend auf der AEM Headless React-App, veranschaulicht, wie AEM GraphQL-API-Anforderungen so konfiguriert werden können, dass sie sich auf der Grundlage von Umgebungsvariablen mit verschiedenen AEM-Services verbinden.
React-Apps sollten den AEM Headless-Client für JavaScript verwenden, um mit den GraphQL-APIs von AEM zu interagieren. Der AEM Headless-Client, der vom AEM Headless-Client für JavaScript bereitgestellt wird, muss mit dem Host des AEM-Services initialisiert werden, mit dem eine Verbindung hergestellt wird.
React benutzt benutzerdefinierte Umgebungsdateien, oder .env
-Dateien, die in dem Stammverzeichnis des Projekts gespeichert sind, um Build-spezifische Werte zu definieren. Die Datei .env.development
enthält beispielsweise Werte für die Entwicklungsphase, während .env.production
Werte für Produktions-Builds enthält.
.env.development
# 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
-Dateien für andere Verwendungszwecke können durch Anhängen von .env
und eines semantischen Deskriptors wie .env.stage
oder .env.production
angegeben werden. Verschiedene .env
-Dateien können beim Ausführen oder Bauen der React-App verwendet werden, indem man REACT_APP_ENV
vor das Ausführen eines npm
-Befehls setzt.
Zum Beispiel kann die package.json
einer React-App die folgende scripts
-Konfiguration enthalten:
package.json
...
"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"
},
...
Der AEM Headless-Client für JavaScript enthält einen AEM Headless-Client, der HTTP-Anfragen an AEM GraphQL-APIs sendet. Der AEM Headless-Client muss mit dem AEM-Host initialisiert werden, mit dem er interagiert, wobei der Wert aus der aktiven .env
-Datei verwendet wird.
src/api/headlessClient.js
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
});
Benutzerdefinierte useEffect-Hooks in React rufen den AEM Headless-Client auf, der mit dem AEM-Host im Namen der React-Komponente initialisiert wurde, die die Ansicht rendert.
src/api/persistedQueries.js
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};
}
Der benutzerdefinierte useEffect-Hook useAdventureByPath
wird importiert und verwendet, um die Daten über den AEM Headless-Client abzurufen und den Inhalt für die Endbenutzenden zu rendern.
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')
...
Dieses Beispiel basiert auf der beispielhaften iOS™-App für AEM Headless basiert und veranschaulicht, wie AEM GraphQL-API-Anfragen so konfiguriert werden können, dass sie auf der Grundlage von Build-spezifischen Konfigurationsvariablen eine Verbindung zu verschiedenen AEM-Hosts herstellen.
Für iOS™-Apps ist ein benutzerdefinierter AEM Headless-Client erforderlich, um mit AEM GraphQL-APIs zu interagieren. Der AEM Headless-Client muss so geschrieben sein, dass der Host des AEM-Services konfigurierbar ist.
Die XCode-Konfigurationsdatei enthält die Standardkonfigurationsdetails.
Config.xcconfig
// 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
...
Die beispielhafte iOS-App für AEM Headless verwendet einen benutzerdefinierten AEM Headless-Client, der mit den Konfigurationswerten für AEM_SCHEME
und AEM_HOST
initialisiert wurde.
...
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);
Der benutzerdefinierte AEM-Headless-Client (api/Aem.swift
) enthält eine Methode makeRequest(..)
, die AEM GraphQL-API-Anfragen mit dem konfigurierten AEM-scheme
und host
-Prefix versehen.
api/Aem.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;
}
Es können neue Build-Konfigurationsdateien erstellt werden, um eine Verbindung zu verschiedenen AEM-Services herzustellen. Die Build-spezifischen Werte für AEM_SCHEME
und AEM_HOST
werden basierend auf dem ausgewählten Build in XCode verwendet, was dazu führt, dass der benutzerdefinierte AEM Headless-Client eine Verbindung mit dem richtigen AEM-Service herstellt.
Dieses Beispiel, das auf der beispielhaften Android™-App für AEM Headless basiert, veranschaulicht, wie AEM GraphQL-API-Anforderungen so konfiguriert werden können, dass sie auf der Grundlage von Build-spezifischen (oder „Flavors“) Konfigurationsvariablen eine Verbindung zu verschiedenen AEM-Services herstellen.
Android™-Apps (wenn sie in Java™ geschrieben sind) sollten den AEM Headless-Client für Java™ verwenden, um mit AEM GraphQL-APIs zu interagieren. Der AEM Headless-Client, der vom AEM Headless-Client für Java™ bereitgestellt wird, muss mit dem Host des AEM-Services initialisiert werden, mit dem er eine Verbindung herstellt.
Android™-Apps definieren Produktvarianten („productFlavors“), die zum Erstellen von Artefakten für verschiedene Verwendungen verwendet werden.
Dieses Beispiel zeigt, wie zwei Android™-Produktvarianten definiert werden können, die unterschiedliche Werte für Hosts von AEM-Services (AEM_HOST
) für die Entwicklung (dev
) bzw. die Produktion (prod
) verwenden.
In der Datei build.gradle
der App wird eine neue flavorDimension
mit dem Namen env
erstellt.
In der Dimension env
sind zwei productFlavors
definiert: dev
und prod
. Jeder productFlavor
verwendet ein buildConfigField
, um Build-spezifische Variablen festzulegen, die definieren, mit welchem AEM-Service eine Verbindung hergestellt werden soll.
app/build.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"'
...
}
}
...
}
Initialisieren Sie den AEMHeadlessClient
-Builder, der vom AEM Headless-Client für Java™ bereitgestellt wird, mit dem Wert AEM_HOST
aus dem Feld buildConfigField
.
app/src/main/java/com/adobe/wknd/androidapp/loader/AdventuresLoader.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
...
}
...
}
Wenn Sie die Android™-App für verschiedene Verwendungszwecke erstellen, geben Sie die env
-Variante an, woraufhin der entsprechende Wert für den AEM-Host verwendet wird.
Die Bildanfragen der Headless-App an AEM müssen so konfiguriert sein, dass sie mit dem richtigen AEM-Service interagieren, wie in der obigen Tabelle beschrieben.
Adobe empfiehlt die Verwendung optimierter Bilder, die über das _dynamicUrl
-Feld in AEM GraphQL-APIs verfügbar gemacht wurden. Das _dynamicUrl
-Feld gibt eine URL ohne Host zurück, die mit dem zum Abfragen von AEM GraphQL-APIs verwendeten AEM-Servicehost als Präfix versehen werden kann. Für das Feld _dynamicUrl
sieht die GraphQL-Antwort wie folgt aus:
{
...
"_dynamicUrl": "/adobe/dynamicmedia/deliver/dm-aid--dd42d814-88ec-4c4d-b5ef-e3dc4bc0cb42/example.jpg?preferwebp=true",
...
}
Im Folgenden finden Sie Beispiele dafür, wie in Bild-URLs der Wert des AEM-Hosts vorangestellt werden kann, der für verschiedene Headless-App-Frameworks konfigurierbar ist. Die Beispiele gehen von der Verwendung von GraphQL-Abfragen aus, die Bildreferenzen unter Verwendung des Feldes _dynamicUrl
zurückgeben.
Zum Beispiel:
Diese GraphQL-Abfrage gibt den _dynamicUrl
einer Bildreferenz zurück. Wie in der GraphQL-Antwort zu sehen, die einen Host ausschließt.
query ($path: String!) {
adventureByPath(_path: $path, _assetTransform: { format: JPG, preferWebp: true }) {
item {
title,
primaryImage {
... on ImageRef {
_dynamicUrl
}
}
}
}
}
Diese GraphQL-Antwort gibt den _dynamicUrl
der Bildreferenz zurück, der einen Host ausschließt.
{
"data": {
"adventureByPath": {
"item": {
"adventurePrimaryImage": {
"_dynamicUrl": "/adobe/dynamicmedia/deliver/dm-aid--de43411-88ec-4c4d-b5ef-e3dc4bc0cb42/adobestock-175749320.jpg?preferwebp=true",
}
}
}
}
}
Dieses Beispiel basiert auf der beispielhaften AEM Headless-React-App und zeigt, wie Bild-URLs so konfiguriert werden können, dass sie basierend auf Umgebungsvariablen eine Verbindung zu den richtigen AEM-Services herstellen.
Dieses Beispiel zeigt, wie dem Feld _dynamicUrl
der Bildreferenz eine konfigurierbare React-Umgebungsvariable REACT_APP_AEM_HOST
vorangestellt wird.
React benutzt benutzerdefinierte Umgebungsdateien, oder .env
-Dateien, die in dem Stammverzeichnis des Projekts gespeichert sind, um Build-spezifische Werte zu definieren. Die Datei .env.development
enthält beispielsweise Werte für die Entwicklungsphase, während .env.production
Werte für Produktions-Builds enthält.
.env.development
# 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
-Dateien für andere Verwendungszwecke können durch Anhängen von .env
und eines semantischen Deskriptors wie .env.stage
oder .env.production
angegeben werden. Verschiedene .env
-Dateien können beim Ausführen oder Bauen der React-App verwendet werden, indem man REACT_APP_ENV
vor das Ausführen eines npm
-Befehls setzt.
Zum Beispiel kann die package.json
einer React-App die folgende scripts
-Konfiguration enthalten:
package.json
...
"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"
},
...
Die React-Komponente importiert die Umgebungsvariable REACT_APP_AEM_HOST
und stellt dem _dynamicUrl
-Wert des Bilds ein Präfix voran, um eine vollständig auflösbare Bild-URL zu erhalten.
Dieselbe Umgebungsvariable REACT_APP_AEM_HOST
wird verwendet, um den AEM Headless-Client zu initialisieren, der vom benutzerdefinierten useEffect-Hook useAdventureByPath(..)
verwendet wird, um die GraphQL-Daten von AEM abzurufen. Stellen Sie mithilfe derselben Variablen zur Erstellung der GraphQL-API-Anfrage als Bild-URL sicher, dass die React-App für beide Anwendungsfälle mit demselben AEM-Service interagiert.
...
// 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"/> */}
)
Dieses Beispiel basiert auf der beispielhaften iOS™-App für AEM Headless und veranschaulicht, wie AEM-Bild-URLs so konfiguriert werden können, dass sie auf der Grundlage von Build-spezifischen Konfigurationsvariablen eine Verbindung zu verschiedenen AEM-Hosts herstellen.
Die XCode-Konfigurationsdatei enthält die Standardkonfigurationsdetails.
Config.xcconfig
// 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
...
In Aem.swift
, der benutzerdefinierten Client-Implementierung von AEM Headless, nimmt eine benutzerdefinierte Funktion imageUrl(..)
den Bildpfad, der im Feld _dynamicUrl
in der GraphLQ-Antwort angegeben ist, und stellt ihm den AEM-Host voran. Diese Funktion wird dann in den iOS-Ansichten aufgerufen, wenn ein Bild gerendert wird.
WKNDAdventures/AEM/Aem.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)")!
}
...
}
In der iOS-Ansicht wird dem _dynamicUrl
-Wert des Bildes ein Präfix vorangestellt, um eine vollständig auflösbare Bild-URL zu erhalten.
WKNDAdventures/Views/AdventureListItemView.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()
}
}
}
...
Es können neue Build-Konfigurationsdateien erstellt werden, um eine Verbindung zu verschiedenen AEM-Services herzustellen. Die Build-spezifischen Werte für AEM_SCHEME
und AEM_HOST
werden basierend auf dem ausgewählten Build in XCode verwendet, was dazu führt, dass der benutzerdefinierte AEM Headless-Client mit dem richtigen AEM-Service interagiert.
Dieses Beispiel basiert auf der beispielhaften Android™-App für AEM Headless und veranschaulicht, wie AEM-Bild-URLs so konfiguriert werden können, dass sie auf der Grundlage von Build-spezifischen (oder „Flavors“) Konfigurationsvariablen eine Verbindung zu verschiedenen AEM-Services herstellen.
Android™-Apps definieren „productFlavors“, die für die Erstellung von Artefakten für verschiedene Zwecke verwendet werden.
Dieses Beispiel zeigt, wie zwei Android™-Produktvarianten definiert werden können, die unterschiedliche Werte für Hosts von AEM-Services (AEM_HOST
) für die Entwicklung (dev
) bzw. die Produktion (prod
) verwenden.
In der Datei build.gradle
der App wird eine neue flavorDimension
mit dem Namen env
erstellt.
In der Dimension env
sind zwei productFlavors
definiert: dev
und prod
. Jeder productFlavor
verwendet ein buildConfigField
, um Build-spezifische Variablen festzulegen, die definieren, mit welchem AEM-Service eine Verbindung hergestellt werden soll.
app/build.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™ verwendet einen ImageGetter
, um Bilddaten aus AEM abzurufen und lokal zwischenzuspeichern. In prepareDrawableFor(..)
wird der Host des AEM-Services verwendet, der in der aktiven Build-Konfiguration definiert ist, um dem Bildpfad ein Präfix voranzustellen, das eine auflösbare URL für AEM erzeugt.
app/src/main/java/com/adobe/wknd/androidapp/loader/RemoteImagesCache.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);
}
}
Die Android™-Ansicht erhält die Bilddaten über den RemoteImagesCache
unter Verwendung des _dynamicUrl
-Wertes aus der GraphQL-Antwort.
app/src/main/java/com/adobe/wknd/androidapp/AdventureDetailFragment.java
...
public class AdventureDetailFragment extends Fragment implements LoaderManager.LoaderCallbacks<Adventure> {
...
private ImageView adventureDetailImage;
...
private void updateContent() {
...
adventureDetailImage.setImageDrawable(RemoteImagesCache.getInstance().getDrawable(adventure.getPrimaryImageDynamicUrl()));
...
}
...
}
Wenn Sie die Android™-App für verschiedene Verwendungszwecke erstellen, geben Sie die Option env
an, woraufhin der entsprechende Wert für den AEM-Host verwendet wird.