mirror of
https://github.com/aspnet/JavaScriptServices.git
synced 2025-12-22 17:47:53 +00:00
Create new top-level DefaultNodeInstance concept that will soon hold the "connection draining" logic
This commit is contained in:
@@ -12,21 +12,21 @@ namespace ConsoleApplication
|
|||||||
public class Program
|
public class Program
|
||||||
{
|
{
|
||||||
public static void Main(string[] args) {
|
public static void Main(string[] args) {
|
||||||
using (var nodeServices = CreateNodeServices(Configuration.DefaultNodeHostingModel)) {
|
using (var nodeServices = CreateNodeServices(NodeServicesOptions.DefaultNodeHostingModel)) {
|
||||||
MeasureLatency(nodeServices).Wait();
|
MeasureLatency(nodeServices).Wait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task MeasureLatency(INodeServices nodeServices) {
|
private static async Task MeasureLatency(INodeServices nodeServices) {
|
||||||
// Ensure the connection is open, so we can measure per-request timings below
|
// Ensure the connection is open, so we can measure per-request timings below
|
||||||
var response = await nodeServices.Invoke<string>("latencyTest", "C#");
|
var response = await nodeServices.InvokeAsync<string>("latencyTest", "C#");
|
||||||
Console.WriteLine(response);
|
Console.WriteLine(response);
|
||||||
|
|
||||||
// Now perform a series of requests, capturing the time taken
|
// Now perform a series of requests, capturing the time taken
|
||||||
const int requestCount = 100;
|
const int requestCount = 100;
|
||||||
var watch = Stopwatch.StartNew();
|
var watch = Stopwatch.StartNew();
|
||||||
for (var i = 0; i < requestCount; i++) {
|
for (var i = 0; i < requestCount; i++) {
|
||||||
await nodeServices.Invoke<string>("latencyTest", "C#");
|
await nodeServices.InvokeAsync<string>("latencyTest", "C#");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display results
|
// Display results
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ namespace NodeServicesExamples.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Invoke Node and pipe the result to the response
|
// Invoke Node and pipe the result to the response
|
||||||
var imageStream = await _nodeServices.Invoke<Stream>(
|
var imageStream = await _nodeServices.InvokeAsync<Stream>(
|
||||||
"./Node/resizeImage",
|
"./Node/resizeImage",
|
||||||
fileInfo.PhysicalPath,
|
fileInfo.PhysicalPath,
|
||||||
mimeType,
|
mimeType,
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ namespace NodeServicesExamples
|
|||||||
if (requestPath.StartsWith("/js/") && requestPath.EndsWith(".js")) {
|
if (requestPath.StartsWith("/js/") && requestPath.EndsWith(".js")) {
|
||||||
var fileInfo = env.WebRootFileProvider.GetFileInfo(requestPath);
|
var fileInfo = env.WebRootFileProvider.GetFileInfo(requestPath);
|
||||||
if (fileInfo.Exists) {
|
if (fileInfo.Exists) {
|
||||||
var transpiled = await nodeServices.Invoke<string>("./Node/transpilation.js", fileInfo.PhysicalPath, requestPath);
|
var transpiled = await nodeServices.InvokeAsync<string>("./Node/transpilation.js", fileInfo.PhysicalPath, requestPath);
|
||||||
await context.Response.WriteAsync(transpiled);
|
await context.Response.WriteAsync(transpiled);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.NodeServices
|
|
||||||
{
|
|
||||||
public static class Configuration
|
|
||||||
{
|
|
||||||
public const NodeHostingModel DefaultNodeHostingModel = NodeHostingModel.Http;
|
|
||||||
|
|
||||||
private static readonly string[] DefaultWatchFileExtensions = {".js", ".jsx", ".ts", ".tsx", ".json", ".html"};
|
|
||||||
private static readonly NodeServicesOptions DefaultOptions = new NodeServicesOptions
|
|
||||||
{
|
|
||||||
HostingModel = DefaultNodeHostingModel,
|
|
||||||
WatchFileExtensions = DefaultWatchFileExtensions
|
|
||||||
};
|
|
||||||
|
|
||||||
public static void AddNodeServices(this IServiceCollection serviceCollection)
|
|
||||||
=> AddNodeServices(serviceCollection, DefaultOptions);
|
|
||||||
|
|
||||||
public static void AddNodeServices(this IServiceCollection serviceCollection, NodeServicesOptions options)
|
|
||||||
{
|
|
||||||
serviceCollection.AddSingleton(typeof(INodeServices), serviceProvider =>
|
|
||||||
{
|
|
||||||
var hostEnv = serviceProvider.GetRequiredService<IHostingEnvironment>();
|
|
||||||
if (string.IsNullOrEmpty(options.ProjectPath))
|
|
||||||
{
|
|
||||||
options.ProjectPath = hostEnv.ContentRootPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CreateNodeServices(options);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static INodeServices CreateNodeServices(NodeServicesOptions options)
|
|
||||||
{
|
|
||||||
var watchFileExtensions = options.WatchFileExtensions ?? DefaultWatchFileExtensions;
|
|
||||||
switch (options.HostingModel)
|
|
||||||
{
|
|
||||||
case NodeHostingModel.Http:
|
|
||||||
return new HttpNodeInstance(options.ProjectPath, /* port */ 0, watchFileExtensions);
|
|
||||||
case NodeHostingModel.Socket:
|
|
||||||
return new SocketNodeInstance(options.ProjectPath, watchFileExtensions);
|
|
||||||
default:
|
|
||||||
throw new ArgumentException("Unknown hosting model: " + options.HostingModel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.NodeServices.HostingModels;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.NodeServices
|
||||||
|
{
|
||||||
|
public static class Configuration
|
||||||
|
{
|
||||||
|
public static void AddNodeServices(this IServiceCollection serviceCollection)
|
||||||
|
=> AddNodeServices(serviceCollection, new NodeServicesOptions());
|
||||||
|
|
||||||
|
public static void AddNodeServices(this IServiceCollection serviceCollection, NodeServicesOptions options)
|
||||||
|
{
|
||||||
|
serviceCollection.AddSingleton(typeof(INodeServices), serviceProvider =>
|
||||||
|
{
|
||||||
|
// Since this instance is being created through DI, we can access the IHostingEnvironment
|
||||||
|
// to populate options.ProjectPath if it wasn't explicitly specified.
|
||||||
|
var hostEnv = serviceProvider.GetRequiredService<IHostingEnvironment>();
|
||||||
|
if (string.IsNullOrEmpty(options.ProjectPath))
|
||||||
|
{
|
||||||
|
options.ProjectPath = hostEnv.ContentRootPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new NodeServicesImpl(options, () => CreateNodeInstance(options));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static INodeServices CreateNodeServices(NodeServicesOptions options)
|
||||||
|
{
|
||||||
|
return new NodeServicesImpl(options, () => CreateNodeInstance(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static INodeInstance CreateNodeInstance(NodeServicesOptions options)
|
||||||
|
{
|
||||||
|
if (options.NodeInstanceFactory != null)
|
||||||
|
{
|
||||||
|
// If you've explicitly supplied an INodeInstance factory, we'll use that. This is useful for
|
||||||
|
// custom INodeInstance implementations.
|
||||||
|
return options.NodeInstanceFactory();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Otherwise we'll construct the type of INodeInstance specified by the HostingModel property,
|
||||||
|
// which itself has a useful default value.
|
||||||
|
switch (options.HostingModel)
|
||||||
|
{
|
||||||
|
case NodeHostingModel.Http:
|
||||||
|
return new HttpNodeInstance(options.ProjectPath, /* port */ 0, options.WatchFileExtensions);
|
||||||
|
case NodeHostingModel.Socket:
|
||||||
|
return new SocketNodeInstance(options.ProjectPath, options.WatchFileExtensions);
|
||||||
|
default:
|
||||||
|
throw new ArgumentException("Unknown hosting model: " + options.HostingModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.AspNetCore.NodeServices.HostingModels;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.NodeServices
|
||||||
|
{
|
||||||
|
public class NodeServicesOptions
|
||||||
|
{
|
||||||
|
public const NodeHostingModel DefaultNodeHostingModel = NodeHostingModel.Http;
|
||||||
|
|
||||||
|
private static readonly string[] DefaultWatchFileExtensions = { ".js", ".jsx", ".ts", ".tsx", ".json", ".html" };
|
||||||
|
|
||||||
|
public NodeServicesOptions()
|
||||||
|
{
|
||||||
|
HostingModel = DefaultNodeHostingModel;
|
||||||
|
WatchFileExtensions = (string[])DefaultWatchFileExtensions.Clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeHostingModel HostingModel { get; set; }
|
||||||
|
public Func<INodeInstance> NodeInstanceFactory { get; set; }
|
||||||
|
public string ProjectPath { get; set; }
|
||||||
|
public string[] WatchFileExtensions { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Serialization;
|
using Newtonsoft.Json.Serialization;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.NodeServices
|
namespace Microsoft.AspNetCore.NodeServices.HostingModels
|
||||||
{
|
{
|
||||||
internal class HttpNodeInstance : OutOfProcessNodeInstance
|
internal class HttpNodeInstance : OutOfProcessNodeInstance
|
||||||
{
|
{
|
||||||
@@ -45,7 +45,7 @@ namespace Microsoft.AspNetCore.NodeServices
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<T> Invoke<T>(NodeInvocationInfo invocationInfo)
|
protected override async Task<T> InvokeExportAsync<T>(NodeInvocationInfo invocationInfo)
|
||||||
{
|
{
|
||||||
await EnsureReady();
|
await EnsureReady();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.NodeServices.HostingModels
|
||||||
|
{
|
||||||
|
public interface INodeInstance : IDisposable
|
||||||
|
{
|
||||||
|
Task<T> InvokeExportAsync<T>(string moduleName, string exportNameOrNull, params object[] args);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.NodeServices
|
namespace Microsoft.AspNetCore.NodeServices.HostingModels
|
||||||
{
|
{
|
||||||
public class NodeInvocationException : Exception
|
public class NodeInvocationException : Exception
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Microsoft.AspNetCore.NodeServices
|
namespace Microsoft.AspNetCore.NodeServices.HostingModels
|
||||||
{
|
{
|
||||||
public class NodeInvocationInfo
|
public class NodeInvocationInfo
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ using System.Diagnostics;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.NodeServices
|
namespace Microsoft.AspNetCore.NodeServices.HostingModels
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class responsible for launching the Node child process, determining when it is ready to accept invocations,
|
/// Class responsible for launching the Node child process, determining when it is ready to accept invocations,
|
||||||
/// and finally killing it when the parent process exits. Also it restarts the child process if it dies.
|
/// and finally killing it when the parent process exits. Also it restarts the child process if it dies.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="Microsoft.AspNetCore.NodeServices.INodeServices" />
|
/// <seealso cref="Microsoft.AspNetCore.NodeServices.INodeInstance" />
|
||||||
public abstract class OutOfProcessNodeInstance : INodeServices
|
public abstract class OutOfProcessNodeInstance : INodeInstance
|
||||||
{
|
{
|
||||||
private readonly object _childProcessLauncherLock;
|
private readonly object _childProcessLauncherLock;
|
||||||
private string _commandLineArguments;
|
private string _commandLineArguments;
|
||||||
@@ -34,15 +34,12 @@ namespace Microsoft.AspNetCore.NodeServices
|
|||||||
set { _commandLineArguments = value; }
|
set { _commandLineArguments = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<T> Invoke<T>(string moduleName, params object[] args)
|
public Task<T> InvokeExportAsync<T>(string moduleName, string exportNameOrNull, params object[] args)
|
||||||
=> InvokeExport<T>(moduleName, null, args);
|
|
||||||
|
|
||||||
public Task<T> InvokeExport<T>(string moduleName, string exportedFunctionName, params object[] args)
|
|
||||||
{
|
{
|
||||||
return Invoke<T>(new NodeInvocationInfo
|
return InvokeExportAsync<T>(new NodeInvocationInfo
|
||||||
{
|
{
|
||||||
ModuleName = moduleName,
|
ModuleName = moduleName,
|
||||||
ExportedFunctionName = exportedFunctionName,
|
ExportedFunctionName = exportNameOrNull,
|
||||||
Args = args
|
Args = args
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -53,7 +50,7 @@ namespace Microsoft.AspNetCore.NodeServices
|
|||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract Task<T> Invoke<T>(NodeInvocationInfo invocationInfo);
|
protected abstract Task<T> InvokeExportAsync<T>(NodeInvocationInfo invocationInfo);
|
||||||
|
|
||||||
protected void ExitNodeProcess()
|
protected void ExitNodeProcess()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ using Microsoft.AspNetCore.NodeServices.HostingModels.VirtualConnections;
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Serialization;
|
using Newtonsoft.Json.Serialization;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.NodeServices
|
namespace Microsoft.AspNetCore.NodeServices.HostingModels
|
||||||
{
|
{
|
||||||
internal class SocketNodeInstance : OutOfProcessNodeInstance
|
internal class SocketNodeInstance : OutOfProcessNodeInstance
|
||||||
{
|
{
|
||||||
@@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.NodeServices
|
|||||||
_watchFileExtensions = watchFileExtensions;
|
_watchFileExtensions = watchFileExtensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<T> Invoke<T>(NodeInvocationInfo invocationInfo)
|
protected override async Task<T> InvokeExportAsync<T>(NodeInvocationInfo invocationInfo)
|
||||||
{
|
{
|
||||||
await EnsureReady();
|
await EnsureReady();
|
||||||
var virtualConnectionClient = await GetOrCreateVirtualConnectionClientAsync();
|
var virtualConnectionClient = await GetOrCreateVirtualConnectionClientAsync();
|
||||||
|
|||||||
@@ -5,8 +5,14 @@ namespace Microsoft.AspNetCore.NodeServices
|
|||||||
{
|
{
|
||||||
public interface INodeServices : IDisposable
|
public interface INodeServices : IDisposable
|
||||||
{
|
{
|
||||||
|
Task<T> InvokeAsync<T>(string moduleName, params object[] args);
|
||||||
|
|
||||||
|
Task<T> InvokeExportAsync<T>(string moduleName, string exportedFunctionName, params object[] args);
|
||||||
|
|
||||||
|
[Obsolete("Use InvokeAsync instead")]
|
||||||
Task<T> Invoke<T>(string moduleName, params object[] args);
|
Task<T> Invoke<T>(string moduleName, params object[] args);
|
||||||
|
|
||||||
|
[Obsolete("Use InvokeExportAsync instead")]
|
||||||
Task<T> InvokeExport<T>(string moduleName, string exportedFunctionName, params object[] args);
|
Task<T> InvokeExport<T>(string moduleName, string exportedFunctionName, params object[] args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
92
src/Microsoft.AspNetCore.NodeServices/NodeServicesImpl.cs
Normal file
92
src/Microsoft.AspNetCore.NodeServices/NodeServicesImpl.cs
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.NodeServices.HostingModels;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.NodeServices
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Default implementation of INodeServices. This is the primary API surface through which developers
|
||||||
|
/// make use of this package. It provides simple "InvokeAsync" methods that dispatch calls to the
|
||||||
|
/// correct Node instance, creating and destroying those instances as needed.
|
||||||
|
///
|
||||||
|
/// If a Node instance dies (or none was yet created), this class takes care of creating a new one.
|
||||||
|
/// If a Node instance signals that it needs to be restarted (e.g., because a file changed), then this
|
||||||
|
/// class will create a new instance and dispatch future calls to it, while keeping the old instance
|
||||||
|
/// alive for a defined period so that any in-flight RPC calls can complete. This latter feature is
|
||||||
|
/// analogous to the "connection draining" feature implemented by HTTP load balancers.
|
||||||
|
///
|
||||||
|
/// TODO: Implement everything in the preceding paragraph.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="Microsoft.AspNetCore.NodeServices.INodeServices" />
|
||||||
|
internal class NodeServicesImpl : INodeServices
|
||||||
|
{
|
||||||
|
private NodeServicesOptions _options;
|
||||||
|
private Func<INodeInstance> _nodeInstanceFactory;
|
||||||
|
private INodeInstance _currentNodeInstance;
|
||||||
|
private object _currentNodeInstanceAccessLock = new object();
|
||||||
|
|
||||||
|
internal NodeServicesImpl(NodeServicesOptions options, Func<INodeInstance> nodeInstanceFactory)
|
||||||
|
{
|
||||||
|
_options = options;
|
||||||
|
_nodeInstanceFactory = nodeInstanceFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<T> InvokeAsync<T>(string moduleName, params object[] args)
|
||||||
|
{
|
||||||
|
return InvokeExportAsync<T>(moduleName, null, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<T> InvokeExportAsync<T>(string moduleName, string exportedFunctionName, params object[] args)
|
||||||
|
{
|
||||||
|
var nodeInstance = GetOrCreateCurrentNodeInstance();
|
||||||
|
return nodeInstance.InvokeExportAsync<T>(moduleName, exportedFunctionName, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
lock (_currentNodeInstanceAccessLock)
|
||||||
|
{
|
||||||
|
if (_currentNodeInstance != null)
|
||||||
|
{
|
||||||
|
_currentNodeInstance.Dispose();
|
||||||
|
_currentNodeInstance = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private INodeInstance GetOrCreateCurrentNodeInstance()
|
||||||
|
{
|
||||||
|
var instance = _currentNodeInstance;
|
||||||
|
if (instance == null)
|
||||||
|
{
|
||||||
|
lock (_currentNodeInstanceAccessLock)
|
||||||
|
{
|
||||||
|
instance = _currentNodeInstance;
|
||||||
|
if (instance == null)
|
||||||
|
{
|
||||||
|
instance = _currentNodeInstance = CreateNewNodeInstance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private INodeInstance CreateNewNodeInstance()
|
||||||
|
{
|
||||||
|
return _nodeInstanceFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obsolete method - will be removed soon
|
||||||
|
public Task<T> Invoke<T>(string moduleName, params object[] args)
|
||||||
|
{
|
||||||
|
return InvokeAsync<T>(moduleName, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obsolete method - will be removed soon
|
||||||
|
public Task<T> InvokeExport<T>(string moduleName, string exportedFunctionName, params object[] args)
|
||||||
|
{
|
||||||
|
return InvokeExportAsync<T>(moduleName, exportedFunctionName, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
namespace Microsoft.AspNetCore.NodeServices
|
|
||||||
{
|
|
||||||
public class NodeServicesOptions
|
|
||||||
{
|
|
||||||
public NodeServicesOptions()
|
|
||||||
{
|
|
||||||
HostingModel = Configuration.DefaultNodeHostingModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NodeHostingModel HostingModel { get; set; }
|
|
||||||
public string ProjectPath { get; set; }
|
|
||||||
public string[] WatchFileExtensions { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -37,7 +37,6 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
|||||||
{
|
{
|
||||||
_nodeServices = _fallbackNodeServices = Configuration.CreateNodeServices(new NodeServicesOptions
|
_nodeServices = _fallbackNodeServices = Configuration.CreateNodeServices(new NodeServicesOptions
|
||||||
{
|
{
|
||||||
HostingModel = Configuration.DefaultNodeHostingModel,
|
|
||||||
ProjectPath = _applicationBasePath
|
ProjectPath = _applicationBasePath
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
|||||||
string requestPathAndQuery,
|
string requestPathAndQuery,
|
||||||
object customDataParameter)
|
object customDataParameter)
|
||||||
{
|
{
|
||||||
return nodeServices.InvokeExport<RenderToStringResult>(
|
return nodeServices.InvokeExportAsync<RenderToStringResult>(
|
||||||
NodeScript.Value.FileName,
|
NodeScript.Value.FileName,
|
||||||
"renderToString",
|
"renderToString",
|
||||||
applicationBasePath,
|
applicationBasePath,
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ namespace Microsoft.AspNetCore.Builder
|
|||||||
var hostEnv = (IHostingEnvironment)appBuilder.ApplicationServices.GetService(typeof(IHostingEnvironment));
|
var hostEnv = (IHostingEnvironment)appBuilder.ApplicationServices.GetService(typeof(IHostingEnvironment));
|
||||||
var nodeServices = Configuration.CreateNodeServices(new NodeServicesOptions
|
var nodeServices = Configuration.CreateNodeServices(new NodeServicesOptions
|
||||||
{
|
{
|
||||||
HostingModel = Configuration.DefaultNodeHostingModel,
|
|
||||||
ProjectPath = hostEnv.ContentRootPath,
|
ProjectPath = hostEnv.ContentRootPath,
|
||||||
WatchFileExtensions = new string[] { } // Don't watch anything
|
WatchFileExtensions = new string[] { } // Don't watch anything
|
||||||
});
|
});
|
||||||
@@ -61,7 +60,7 @@ namespace Microsoft.AspNetCore.Builder
|
|||||||
suppliedOptions = options
|
suppliedOptions = options
|
||||||
};
|
};
|
||||||
var devServerInfo =
|
var devServerInfo =
|
||||||
nodeServices.InvokeExport<WebpackDevServerInfo>(nodeScript.FileName, "createWebpackDevServer",
|
nodeServices.InvokeExportAsync<WebpackDevServerInfo>(nodeScript.FileName, "createWebpackDevServer",
|
||||||
JsonConvert.SerializeObject(devServerOptions)).Result;
|
JsonConvert.SerializeObject(devServerOptions)).Result;
|
||||||
|
|
||||||
// Proxy the corresponding requests through ASP.NET and into the Node listener
|
// Proxy the corresponding requests through ASP.NET and into the Node listener
|
||||||
|
|||||||
Reference in New Issue
Block a user