如何使ASP.NET/React应用从子路径提供SPA?

时间:2019-07-12 00:40:32

标签: asp.net reactjs asp.net-core

我有一个股票aspnetcorereactjs应用程序,该应用程序是通过启动模板(dotnet new react)生成的。我希望从根URL的子路径中提供SPA应用程序;例如而不是示例应用https://localhost:5001/counter,我正在寻找可以从https://localhost:5001/myapp/counter投放的示例应用。

我将Startup.cs更改为:

app.UseSpa(spa =>
            {
                spa.Options.SourcePath = "ClientApp";

                if (env.IsDevelopment())
                {
                    spa.UseReactDevelopmentServer(npmScript: "start");
                }
            });

对此:

app.Map(new Microsoft.AspNetCore.Http.PathString("/myapp"), appMember =>
            {
                appMember.UseSpa(spa =>
                {
                    spa.Options.SourcePath = "ClientApp";

                    if (env.IsDevelopment())
                    {
                        spa.UseReactDevelopmentServer(npmScript: "start");
                    }
                });
            });

这类作品。如果我浏览到https://localhost:5001/myapp/,它似乎正在加载index.html,但是静态文件正在尝试从根路径而非子路径进行加载。

需要进行哪些更改,以使React应用程序将子路径用作根目录?我希望它既可以在交互式VS开发环境中工作,又可以在IIS上部署时工作。好像已经接近了,但是我想念一些东西。

此处提供解决方案的示例演示:https://github.com/petertirrell/mvc-spa-demo/tree/master/mvc-spa-demo

谢谢!

4 个答案:

答案 0 :(得分:2)

通过将其添加到package.json顶部,开始将应用移动到子路径:

"homepage": "/myapp/", 

在ClientApp文件夹中运行npm start时,应用正在提供http://localhost:3000/myapp

然后像这样更改Startup.cs

先删除

app.UseSpaStaticFiles()

然后添加

        const string spaPath = "/myapp";
        if (env.IsDevelopment())
        {
            app.MapWhen(ctx => ctx.Request.Path.StartsWithSegments(spaPath)
                               || ctx.Request.Path.StartsWithSegments("/sockjs-node"),
                client =>
            {
                client.UseSpa(spa =>
                {
                    spa.Options.SourcePath = "ClientApp";
                    spa.UseReactDevelopmentServer(npmScript: "start");
                });
            });
        }
        else
        {
            app.Map(new PathString(spaPath), client =>
            {
                // `https://github.com/dotnet/aspnetcore/issues/3147`
                client.UseSpaStaticFiles(new StaticFileOptions()
                {
                    OnPrepareResponse = ctx =>
                    {
                        if (ctx.Context.Request.Path.StartsWithSegments($"{spaPath}/static"))
                        {
                            // Cache all static resources for 1 year (versioned file names)
                            var headers = ctx.Context.Response.GetTypedHeaders();
                            headers.CacheControl = new CacheControlHeaderValue
                            {
                                Public = true,
                                MaxAge = TimeSpan.FromDays(365)
                            };
                        }
                        else
                        {
                            // Do not cache explicit `/index.html` or any other files.  See also: `DefaultPageStaticFileOptions` below for implicit "/index.html"
                            var headers = ctx.Context.Response.GetTypedHeaders();
                            headers.CacheControl = new CacheControlHeaderValue
                            {
                                Public = true,
                                MaxAge = TimeSpan.FromDays(0)
                            };
                        }
                    }
                });

                client.UseSpa(spa =>
                {
                    spa.Options.SourcePath = "ClientApp";
                    spa.Options.DefaultPageStaticFileOptions = new StaticFileOptions()
                    {
                        OnPrepareResponse = ctx => {
                            // Do not cache implicit `/index.html`.  See also: `UseSpaStaticFiles` above
                            var headers = ctx.Context.Response.GetTypedHeaders();
                            headers.CacheControl = new CacheControlHeaderValue
                            {
                                Public = true,
                                MaxAge = TimeSpan.FromDays(0)
                            };
                        }
                    };
                });
            });
        }

别忘了在第一次测试更改之前清除浏览器历史记录,例如Azure。

答案 1 :(得分:1)

您可以通过以下方式实现:

services.AddSpaStaticFiles(configuration =>
{
    configuration.RootPath = "ClientApp/build";
});

在您的ConfigureServices中:

string spaPath = "/myapp";
if (env.IsDevelopment())
{
    app.MapWhen(y => y.Request.Path.StartsWithSegments(spaPath), client =>
    {
        client.UseSpa(spa => 
        {
            spa.UseReactDevelopmentServer(npmScript: "start");
        });
    });
}
else
{
    app.Map(new PathString(spaPath), client =>
    {
        client.UseSpaStaticFiles();
        client.UseSpa(spa => {});
    });
}

请注意,在开发中我们使用.MapWhen,因为.Map会导致您的静态文件在/myapp/myapp/[file]而不是/myapp/[file]上可用。

答案 2 :(得分:0)

您可以通过在静态文件选项中指定RequestPath来实现。下面的代码可能对您有用

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseStaticFiles(new StaticFileOptions
            {
                FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "Client")),
                RequestPath = "/client"
            });
         }

答案 3 :(得分:0)

我结合了每个答案中的一些以使其正常工作。您需要将 "homepage": "/myapp" 添加到 package.json 并将配置更改为 Startup.cs。我使用了 Shoe's answer 中提供的更简单的配置,没有所有额外的缓存和套接字指令,因为我不需要这些。

因为我的应用程序还使用了 React Router 作为 /myapp 下的 SPA 路由,所以我还需要将 basename 添加到根 BrowserRouter

<BrowserRouter basename="/myapp" >...</BrowserRouter>