With Sitecore Headless, every request is going through the layout service. For example, http://mysite.localhost/forms/subscribe-to-email-updates?email=cielo.chua@email.com, HttpContext request url is something like /sitecore/api/layout/render/default?item=forms/subscribe-to-email-updates&sc_apikey=C1B5BE75-9889-4BDB-946F-FEC88AACAF66&sc_site=mysite&sc_lang=en&tracking=false which did not persist the query string
So if you have a form page that needs to pre-fill a field based on the query string, you would like to forward the query string parameters to the Layout Service.
Create a custom Parameterized Rest Layout Service and overwrite the fetchLayoutData and getFetchParams
import {
AxiosDataFetcher,
AxiosResponse,
LayoutServiceData,
RestLayoutService,
RestLayoutServiceConfig,
} from '@sitecore-jss/sitecore-jss-nextjs';
import { IncomingMessage, ServerResponse } from 'http';
import { parse, ParsedUrlQuery } from 'querystring';
interface FetchParams {
[param: string]: string | number | boolean;
sc_apikey: string;
sc_site: string;
sc_lang: string;
tracking: boolean;
}
export class ParameterizedRestLayoutService extends RestLayoutService {
constructor(private config: RestLayoutServiceConfig) {
super(config);
}
fetchLayoutData = async (
itemPath: string,
language: string | undefined,
req: IncomingMessage,
res: ServerResponse
): Promise<LayoutServiceData> => {
const reqParams: ParsedUrlQuery = parse(req?.url?.split('?')[1] || '');
if (!req?.url || !reqParams) return super.fetchLayoutData(itemPath, language, req, res);
const querystringParams = this.getFetchParams(language, reqParams);
const combinedParams = Object.assign({ item: itemPath }, querystringParams);
const fetchParams: Record<string, string> = {};
Object.keys(combinedParams).forEach((key) => {
if (key == 'path') return;
fetchParams[key] = combinedParams[key].toString();
});
try {
const fetchUrl = this.resolveLayoutServiceUrl('render');
const url = this.buildUrl(fetchUrl, fetchParams);
const response = await this.dataFetcher<LayoutServiceData>(url);
return response.data;
} catch (error: any) {
throw error;
}
};
private buildUrl = (urlBase: string, params: Record<string, string>) => {
const url = new URL(urlBase);
url.search = new URLSearchParams(params).toString();
return url.toString();
};
private dataFetcher<ResponseType>(
url: string,
data?: unknown
): Promise<AxiosResponse<ResponseType>> {
return new AxiosDataFetcher().fetch<ResponseType>(url, data);
}
protected getFetchParams = (language?: string, params?: ParsedUrlQuery): FetchParams => {
return {
sc_apikey: this.config.apiKey,
sc_site: this.config.siteName,
sc_lang: language || '',
tracking: this.config.tracking ?? true,
...params,
};
};
}
Go to the layout-service-factory.ts then use the newly created ParameterizedRestLayoutService
import { LayoutService } from '@sitecore-jss/sitecore-jss-nextjs';
import config from 'temp/config';
import { ParameterizedRestLayoutService } from './parameterized-rest-layout-service';
export class LayoutServiceFactory {
create(): LayoutService {
return new ParameterizedRestLayoutService({
apiHost: config.sitecoreApiHost as string,
apiKey: config.sitecoreApiKey,
siteName: config.jssAppName,
configurationName: 'default',
});
}
}
export const layoutServiceFactory = new LayoutServiceFactory();
Then on the Forms Value Provider you can now retrieve the query sting using HttpContext.
public class QueryStringValueProvider : IFieldValueProvider
{
public FieldValueProviderContext ValueProviderContext { get; set; }
public object GetValue(string parameters)
{
HttpContext httpContext = System.Web.HttpContext.Current;
string q = HttpUtility.ParseQueryString(httpContext.Request.Url.Query).Get(parameters);
if (string.IsNullOrWhiteSpace(q))
{
return string.Empty;
}
return q;
}
}
has context menu
Leave a comment