mirror of
https://github.com/aspnet/JavaScriptServices.git
synced 2025-12-23 01:58:29 +00:00
Prerendering imposes its own (overridable) timeout with descriptive error
This commit is contained in:
@@ -18,6 +18,7 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
|||||||
private const string PrerenderExportAttributeName = "asp-prerender-export";
|
private const string PrerenderExportAttributeName = "asp-prerender-export";
|
||||||
private const string PrerenderWebpackConfigAttributeName = "asp-prerender-webpack-config";
|
private const string PrerenderWebpackConfigAttributeName = "asp-prerender-webpack-config";
|
||||||
private const string PrerenderDataAttributeName = "asp-prerender-data";
|
private const string PrerenderDataAttributeName = "asp-prerender-data";
|
||||||
|
private const string PrerenderTimeoutAttributeName = "asp-prerender-timeout";
|
||||||
private static INodeServices _fallbackNodeServices; // Used only if no INodeServices was registered with DI
|
private static INodeServices _fallbackNodeServices; // Used only if no INodeServices was registered with DI
|
||||||
|
|
||||||
private readonly string _applicationBasePath;
|
private readonly string _applicationBasePath;
|
||||||
@@ -50,6 +51,9 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
|||||||
[HtmlAttributeName(PrerenderDataAttributeName)]
|
[HtmlAttributeName(PrerenderDataAttributeName)]
|
||||||
public object CustomDataParameter { get; set; }
|
public object CustomDataParameter { get; set; }
|
||||||
|
|
||||||
|
[HtmlAttributeName(PrerenderTimeoutAttributeName)]
|
||||||
|
public int TimeoutMillisecondsParameter { get; set; }
|
||||||
|
|
||||||
[HtmlAttributeNotBound]
|
[HtmlAttributeNotBound]
|
||||||
[ViewContext]
|
[ViewContext]
|
||||||
public ViewContext ViewContext { get; set; }
|
public ViewContext ViewContext { get; set; }
|
||||||
@@ -79,7 +83,8 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
|||||||
},
|
},
|
||||||
unencodedAbsoluteUrl,
|
unencodedAbsoluteUrl,
|
||||||
unencodedPathAndQuery,
|
unencodedPathAndQuery,
|
||||||
CustomDataParameter);
|
CustomDataParameter,
|
||||||
|
TimeoutMillisecondsParameter);
|
||||||
output.Content.SetHtmlContent(result.Html);
|
output.Content.SetHtmlContent(result.Html);
|
||||||
|
|
||||||
// Also attach any specified globals to the 'window' object. This is useful for transferring
|
// Also attach any specified globals to the 'window' object. This is useful for transferring
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
|||||||
JavaScriptModuleExport bootModule,
|
JavaScriptModuleExport bootModule,
|
||||||
string requestAbsoluteUrl,
|
string requestAbsoluteUrl,
|
||||||
string requestPathAndQuery,
|
string requestPathAndQuery,
|
||||||
object customDataParameter)
|
object customDataParameter,
|
||||||
|
int timeoutMilliseconds)
|
||||||
{
|
{
|
||||||
return nodeServices.InvokeExportAsync<RenderToStringResult>(
|
return nodeServices.InvokeExportAsync<RenderToStringResult>(
|
||||||
NodeScript.Value.FileName,
|
NodeScript.Value.FileName,
|
||||||
@@ -32,7 +33,8 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
|||||||
bootModule,
|
bootModule,
|
||||||
requestAbsoluteUrl,
|
requestAbsoluteUrl,
|
||||||
requestPathAndQuery,
|
requestPathAndQuery,
|
||||||
customDataParameter);
|
customDataParameter,
|
||||||
|
timeoutMilliseconds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,8 @@ import * as domain from 'domain';
|
|||||||
import { run as domainTaskRun } from 'domain-task/main';
|
import { run as domainTaskRun } from 'domain-task/main';
|
||||||
import { baseUrl } from 'domain-task/fetch';
|
import { baseUrl } from 'domain-task/fetch';
|
||||||
|
|
||||||
|
const defaultTimeoutMilliseconds = 30 * 1000;
|
||||||
|
|
||||||
export interface RenderToStringCallback {
|
export interface RenderToStringCallback {
|
||||||
(error: any, result: RenderToStringResult): void;
|
(error: any, result: RenderToStringResult): void;
|
||||||
}
|
}
|
||||||
@@ -33,7 +35,7 @@ export interface BootModuleInfo {
|
|||||||
webpackConfig?: string;
|
webpackConfig?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function renderToString(callback: RenderToStringCallback, applicationBasePath: string, bootModule: BootModuleInfo, absoluteRequestUrl: string, requestPathAndQuery: string, customDataParameter: any) {
|
export function renderToString(callback: RenderToStringCallback, applicationBasePath: string, bootModule: BootModuleInfo, absoluteRequestUrl: string, requestPathAndQuery: string, customDataParameter: any, overrideTimeoutMilliseconds: number) {
|
||||||
findBootFunc(applicationBasePath, bootModule, (findBootFuncError, bootFunc) => {
|
findBootFunc(applicationBasePath, bootModule, (findBootFuncError, bootFunc) => {
|
||||||
if (findBootFuncError) {
|
if (findBootFuncError) {
|
||||||
callback(findBootFuncError, null);
|
callback(findBootFuncError, null);
|
||||||
@@ -66,8 +68,22 @@ export function renderToString(callback: RenderToStringCallback, applicationBase
|
|||||||
// Make the base URL available to the 'domain-tasks/fetch' helper within this execution context
|
// Make the base URL available to the 'domain-tasks/fetch' helper within this execution context
|
||||||
baseUrl(absoluteRequestUrl);
|
baseUrl(absoluteRequestUrl);
|
||||||
|
|
||||||
|
// Begin rendering, and apply a timeout
|
||||||
|
const bootFuncPromise = bootFunc(params);
|
||||||
|
if (!bootFuncPromise || typeof bootFuncPromise.then !== 'function') {
|
||||||
|
callback(`Prerendering failed because the boot function in ${bootModule.moduleName} did not return a promise.`, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const timeoutMilliseconds = overrideTimeoutMilliseconds || defaultTimeoutMilliseconds; // e.g., pass -1 to override as 'never time out'
|
||||||
|
const bootFuncPromiseWithTimeout = timeoutMilliseconds > 0
|
||||||
|
? wrapWithTimeout(bootFuncPromise, timeoutMilliseconds,
|
||||||
|
`Prerendering timed out after ${timeoutMilliseconds}ms because the boot function in '${bootModule.moduleName}' `
|
||||||
|
+ 'returned a promise that did not resolve or reject. Make sure that your boot function always resolves or '
|
||||||
|
+ 'rejects its promise. You can change the timeout value using the \'asp-prerender-timeout\' tag helper.')
|
||||||
|
: bootFuncPromise;
|
||||||
|
|
||||||
// Actually perform the rendering
|
// Actually perform the rendering
|
||||||
bootFunc(params).then(successResult => {
|
bootFuncPromiseWithTimeout.then(successResult => {
|
||||||
callback(null, { html: successResult.html, globals: successResult.globals });
|
callback(null, { html: successResult.html, globals: successResult.globals });
|
||||||
}, error => {
|
}, error => {
|
||||||
callback(error, null);
|
callback(error, null);
|
||||||
@@ -84,6 +100,25 @@ export function renderToString(callback: RenderToStringCallback, applicationBase
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function wrapWithTimeout<T>(promise: Promise<T>, timeoutMilliseconds: number, timeoutRejectionValue: any): Promise<T> {
|
||||||
|
return new Promise<T>((resolve, reject) => {
|
||||||
|
const timeoutTimer = setTimeout(() => {
|
||||||
|
reject(timeoutRejectionValue);
|
||||||
|
}, timeoutMilliseconds);
|
||||||
|
|
||||||
|
promise.then(
|
||||||
|
resolvedValue => {
|
||||||
|
clearTimeout(timeoutTimer);
|
||||||
|
resolve(resolvedValue);
|
||||||
|
},
|
||||||
|
rejectedValue => {
|
||||||
|
clearTimeout(timeoutTimer);
|
||||||
|
reject(rejectedValue);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function findBootModule<T>(applicationBasePath: string, bootModule: BootModuleInfo, callback: (error: any, foundModule: T) => void) {
|
function findBootModule<T>(applicationBasePath: string, bootModule: BootModuleInfo, callback: (error: any, foundModule: T) => void) {
|
||||||
const bootModuleNameFullPath = path.resolve(applicationBasePath, bootModule.moduleName);
|
const bootModuleNameFullPath = path.resolve(applicationBasePath, bootModule.moduleName);
|
||||||
if (bootModule.webpackConfig) {
|
if (bootModule.webpackConfig) {
|
||||||
|
|||||||
Reference in New Issue
Block a user