mirror of
https://github.com/aspnet/JavaScriptServices.git
synced 2025-12-22 17:47:53 +00:00
Remove System.Runtime.Loader dependency to enable net461 support. Use IApplicationLifetime instead of AssemblyLoadContext.Default.Unloading.
This commit is contained in:
@@ -49,6 +49,7 @@ namespace Microsoft.AspNetCore.NodeServices.Sockets
|
||||
options.ProjectPath,
|
||||
options.WatchFileExtensions,
|
||||
MakeNewCommandLineOptions(socketAddress),
|
||||
options.ApplicationStoppingToken,
|
||||
options.NodeInstanceOutputLogger,
|
||||
options.EnvironmentVariables,
|
||||
options.InvocationTimeoutMilliseconds,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Microsoft.AspNetCore.NodeServices.HostingModels;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
@@ -42,6 +43,12 @@ namespace Microsoft.AspNetCore.NodeServices
|
||||
EnvironmentVariables["NODE_ENV"] = hostEnv.IsDevelopment() ? "development" : "production"; // De-facto standard values for Node
|
||||
}
|
||||
|
||||
var applicationLifetime = serviceProvider.GetService<IApplicationLifetime>();
|
||||
if (applicationLifetime != null)
|
||||
{
|
||||
ApplicationStoppingToken = applicationLifetime.ApplicationStopping;
|
||||
}
|
||||
|
||||
// If the DI system gives us a logger, use it. Otherwise, set up a default one.
|
||||
var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
|
||||
NodeInstanceOutputLogger = loggerFactory != null
|
||||
@@ -93,5 +100,10 @@ namespace Microsoft.AspNetCore.NodeServices
|
||||
/// Specifies the maximum duration, in milliseconds, that your .NET code should wait for Node.js RPC calls to return.
|
||||
/// </summary>
|
||||
public int InvocationTimeoutMilliseconds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A token that indicates when the host application is stopping.
|
||||
/// </summary>
|
||||
public CancellationToken ApplicationStoppingToken { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
|
||||
options.ProjectPath,
|
||||
options.WatchFileExtensions,
|
||||
MakeCommandLineOptions(port),
|
||||
options.ApplicationStoppingToken,
|
||||
options.NodeInstanceOutputLogger,
|
||||
options.EnvironmentVariables,
|
||||
options.InvocationTimeoutMilliseconds,
|
||||
|
||||
@@ -45,6 +45,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
|
||||
/// <param name="projectPath">The root path of the current project. This is used when resolving Node.js module paths relative to the project root.</param>
|
||||
/// <param name="watchFileExtensions">The filename extensions that should be watched within the project root. The Node instance will automatically shut itself down if any matching file changes.</param>
|
||||
/// <param name="commandLineArguments">Additional command-line arguments to be passed to the Node.js instance.</param>
|
||||
/// <param name="applicationStoppingToken">A token that indicates when the host application is stopping.</param>
|
||||
/// <param name="nodeOutputLogger">The <see cref="ILogger"/> to which the Node.js instance's stdout/stderr (and other log information) should be written.</param>
|
||||
/// <param name="environmentVars">Environment variables to be set on the Node.js process.</param>
|
||||
/// <param name="invocationTimeoutMilliseconds">The maximum duration, in milliseconds, to wait for RPC calls to complete.</param>
|
||||
@@ -55,6 +56,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
|
||||
string projectPath,
|
||||
string[] watchFileExtensions,
|
||||
string commandLineArguments,
|
||||
CancellationToken applicationStoppingToken,
|
||||
ILogger nodeOutputLogger,
|
||||
IDictionary<string, string> environmentVars,
|
||||
int invocationTimeoutMilliseconds,
|
||||
@@ -67,7 +69,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
|
||||
}
|
||||
|
||||
OutputLogger = nodeOutputLogger;
|
||||
_entryPointScript = new StringAsTempFile(entryPointScript);
|
||||
_entryPointScript = new StringAsTempFile(entryPointScript, applicationStoppingToken);
|
||||
_invocationTimeoutMilliseconds = invocationTimeoutMilliseconds;
|
||||
_launchWithDebugging = launchWithDebugging;
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="$(AspNetCoreVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(AspNetCoreVersion)" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="$(JsonNetVersion)" />
|
||||
<PackageReference Include="System.Runtime.Loader" Version="$(CoreFxVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish" Condition=" '$(IsCrossTargetingBuild)' != 'true' ">
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace Microsoft.AspNetCore.NodeServices
|
||||
{
|
||||
@@ -11,22 +12,21 @@ namespace Microsoft.AspNetCore.NodeServices
|
||||
private bool _disposedValue;
|
||||
private bool _hasDeletedTempFile;
|
||||
private object _fileDeletionLock = new object();
|
||||
private IDisposable _applicationLifetimeRegistration;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance of <see cref="StringAsTempFile"/>.
|
||||
/// </summary>
|
||||
/// <param name="content">The contents of the temporary file to be created.</param>
|
||||
public StringAsTempFile(string content)
|
||||
/// <param name="applicationStoppingToken">A token that indicates when the host application is stopping.</param>
|
||||
public StringAsTempFile(string content, CancellationToken applicationStoppingToken)
|
||||
{
|
||||
FileName = Path.GetTempFileName();
|
||||
File.WriteAllText(FileName, content);
|
||||
|
||||
// Because .NET finalizers don't reliably run when the process is terminating, also
|
||||
// add event handlers for other shutdown scenarios.
|
||||
// Note that this still doesn't capture SIGKILL (at least on macOS) - there doesn't
|
||||
// appear to be a way of doing that. So in that case, the temporary file will be
|
||||
// left behind.
|
||||
System.Runtime.Loader.AssemblyLoadContext.Default.Unloading += HandleAssemblyUnloading;
|
||||
_applicationLifetimeRegistration = applicationStoppingToken.Register(EnsureTempFileDeleted);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -50,7 +50,7 @@ namespace Microsoft.AspNetCore.NodeServices
|
||||
if (disposing)
|
||||
{
|
||||
// Dispose managed state
|
||||
System.Runtime.Loader.AssemblyLoadContext.Default.Unloading -= HandleAssemblyUnloading;
|
||||
_applicationLifetimeRegistration.Dispose();
|
||||
}
|
||||
|
||||
EnsureTempFileDeleted();
|
||||
@@ -71,11 +71,6 @@ namespace Microsoft.AspNetCore.NodeServices
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleAssemblyUnloading(System.Runtime.Loader.AssemblyLoadContext context)
|
||||
{
|
||||
EnsureTempFileDeleted();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implements the finalization part of the IDisposable pattern by calling Dispose(false).
|
||||
/// </summary>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
@@ -24,6 +25,7 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
||||
private static INodeServices _fallbackNodeServices; // Used only if no INodeServices was registered with DI
|
||||
|
||||
private readonly string _applicationBasePath;
|
||||
private readonly CancellationToken _applicationStoppingToken;
|
||||
private readonly INodeServices _nodeServices;
|
||||
|
||||
/// <summary>
|
||||
@@ -36,6 +38,9 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
||||
_nodeServices = (INodeServices) serviceProvider.GetService(typeof(INodeServices)) ?? _fallbackNodeServices;
|
||||
_applicationBasePath = hostEnv.ContentRootPath;
|
||||
|
||||
var applicationLifetime = (IApplicationLifetime) serviceProvider.GetService(typeof(IApplicationLifetime));
|
||||
_applicationStoppingToken = applicationLifetime.ApplicationStopping;
|
||||
|
||||
// Consider removing the following. Having it means you can get away with not putting app.AddNodeServices()
|
||||
// in your startup file, but then again it might be confusing that you don't need to.
|
||||
if (_nodeServices == null)
|
||||
@@ -101,6 +106,7 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
||||
var result = await Prerenderer.RenderToString(
|
||||
_applicationBasePath,
|
||||
_nodeServices,
|
||||
_applicationStoppingToken,
|
||||
new JavaScriptModuleExport(ModuleName)
|
||||
{
|
||||
ExportName = ExportName
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.NodeServices;
|
||||
|
||||
@@ -9,22 +10,16 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
||||
/// </summary>
|
||||
public static class Prerenderer
|
||||
{
|
||||
private static readonly Lazy<StringAsTempFile> NodeScript;
|
||||
private static readonly object CreateNodeScriptLock = new object();
|
||||
|
||||
static Prerenderer()
|
||||
{
|
||||
NodeScript = new Lazy<StringAsTempFile>(() =>
|
||||
{
|
||||
var script = EmbeddedResourceReader.Read(typeof(Prerenderer), "/Content/Node/prerenderer.js");
|
||||
return new StringAsTempFile(script); // Will be cleaned up on process exit
|
||||
});
|
||||
}
|
||||
private static StringAsTempFile NodeScript;
|
||||
|
||||
/// <summary>
|
||||
/// Performs server-side prerendering by invoking code in Node.js.
|
||||
/// </summary>
|
||||
/// <param name="applicationBasePath">The root path to your application. This is used when resolving project-relative paths.</param>
|
||||
/// <param name="nodeServices">The instance of <see cref="INodeServices"/> that will be used to invoke JavaScript code.</param>
|
||||
/// <param name="applicationStoppingToken">A token that indicates when the host application is stopping.</param>
|
||||
/// <param name="bootModule">The path to the JavaScript file containing the prerendering logic.</param>
|
||||
/// <param name="requestAbsoluteUrl">The URL of the currently-executing HTTP request. This is supplied to the prerendering code.</param>
|
||||
/// <param name="requestPathAndQuery">The path and query part of the URL of the currently-executing HTTP request. This is supplied to the prerendering code.</param>
|
||||
@@ -35,6 +30,7 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
||||
public static Task<RenderToStringResult> RenderToString(
|
||||
string applicationBasePath,
|
||||
INodeServices nodeServices,
|
||||
CancellationToken applicationStoppingToken,
|
||||
JavaScriptModuleExport bootModule,
|
||||
string requestAbsoluteUrl,
|
||||
string requestPathAndQuery,
|
||||
@@ -43,7 +39,7 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
||||
string requestPathBase)
|
||||
{
|
||||
return nodeServices.InvokeExportAsync<RenderToStringResult>(
|
||||
NodeScript.Value.FileName,
|
||||
GetNodeScriptFilename(applicationStoppingToken),
|
||||
"renderToString",
|
||||
applicationBasePath,
|
||||
bootModule,
|
||||
@@ -53,5 +49,19 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
||||
timeoutMilliseconds,
|
||||
requestPathBase);
|
||||
}
|
||||
|
||||
private static string GetNodeScriptFilename(CancellationToken applicationStoppingToken)
|
||||
{
|
||||
lock(CreateNodeScriptLock)
|
||||
{
|
||||
if (NodeScript == null)
|
||||
{
|
||||
var script = EmbeddedResourceReader.Read(typeof(Prerenderer), "/Content/Node/prerenderer.js");
|
||||
NodeScript = new StringAsTempFile(script, applicationStoppingToken); // Will be cleaned up on process exit
|
||||
}
|
||||
}
|
||||
|
||||
return NodeScript.FileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,7 +68,7 @@ namespace Microsoft.AspNetCore.Builder
|
||||
// Get a filename matching the middleware Node script
|
||||
var script = EmbeddedResourceReader.Read(typeof(WebpackDevMiddleware),
|
||||
"/Content/Node/webpack-dev-middleware.js");
|
||||
var nodeScript = new StringAsTempFile(script); // Will be cleaned up on process exit
|
||||
var nodeScript = new StringAsTempFile(script, nodeServicesOptions.ApplicationStoppingToken); // Will be cleaned up on process exit
|
||||
|
||||
// Ideally, this would be relative to the application's PathBase (so it could work in virtual directories)
|
||||
// but it's not clear that such information exists during application startup, as opposed to within the context
|
||||
|
||||
Reference in New Issue
Block a user