asp.netasp.net-corewebpacksingle-page-applicationmulti-page-application

Webpack serve (or another npm script) in ASP.NET Core non-spa project


I am working on an ASP.NET Core non-spa project in frontend part, which is mixed with backend. What I need, that just start webpack-dev-server (or other npm script, which will watching), when I am building and starting the project. I will serve only js, css, assets, but no html, because there is Razor with .cshtml

Now I am using:

app.UseSpa(spa =>
            {
                spa.Options.SourcePath = "ClientApp";                 
                spa.UseReactDevelopmentServer(npmScript: "start");
            });

for solving my problem, and it works somehow, but I don't understand why I need use this.

Why is there is no useDevelopmentServer or useNodeCommand or something like that?

I just need to use all possibilities of modern development in the non-spa project, before our clients will decide to move to SPA.

Of course I can start manually my npm (or yarn or whatever else) from terminal, but I need automation of process, because it's hard to explain our backenders how to do that, and because it's convenient.


Solution

  • We can implement it by using middleware, and we should let this npm script is run only once. Here is my sample middleware.

    NpmMiddleware.cs

    public class NpmMiddleware
    {
        private static bool _npmScriptStarted = false;
        private readonly RequestDelegate _next;
        private readonly string _sourcePath;
        private readonly string _npmScript;
    
        public NpmMiddleware(RequestDelegate next, string sourcePath, string npmScript)
        {
            _next = next;
            _sourcePath = sourcePath;
            _npmScript = npmScript;
        }
    
        public async Task InvokeAsync(HttpContext context)
        {
            if (!_npmScriptStarted)
            {
                var isDevelopment = string.Equals(Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"), "Development", StringComparison.OrdinalIgnoreCase);
                
                if (isDevelopment)
                {
                    var npmScriptProcess = new Process
                    {
                        StartInfo = new ProcessStartInfo
                        {
                            FileName = "npm",
                            Arguments = $"run {_npmScript}",
                            RedirectStandardError = true,
                            RedirectStandardInput = true,
                            RedirectStandardOutput = true,
                            UseShellExecute = false,
                            WorkingDirectory = _sourcePath
                        }
                    };
    
                    npmScriptProcess.Start();
                    _npmScriptStarted = true;
                }
            }
    
            // Call the next delegate/middleware in the pipeline
            await _next(context);
        }
    }
    public static class NpmMiddlewareExtensions
    {
        public static IApplicationBuilder UseNpmScript(this IApplicationBuilder builder, string sourcePath, string npmScript)
        {
            return builder.UseMiddleware<NpmMiddleware>(sourcePath, npmScript);
        }
    }
    

    And use it in Program.cs(or startup.cs file if you have) file.

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        // ... other middlewares ...
    
        app.UseNpmScript("ClientApp", "start");
    
        // ... remaining configuration ...
    }