Vivere
Vivere

Reputation: 2270

Use ASP.Net Core React.js Template without SPA Proxy

In VS2019 Preview you have multiple templates. I'm interested in the following two: enter image description here

I played a bit with both and I observed a difference. In the ASP.NET Core with React.js template, a SPA Proxy Server is started, whereas in the ASP.NET Core with React.js and Redux the simply works without that (the second template is in TS, not JS).

What I mean by simply works without that. I've looked into both .csproj files and I observed a difference (I'll only attach the noteworthy differences):

First template:

...
    <SpaProxyServerUrl>https://localhost:5002</SpaProxyServerUrl>
    <SpaProxyLaunchCommand>npm start</SpaProxyLaunchCommand>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.SpaProxy" Version="6.0.0-preview.6.21355.2" />
  </ItemGroup>
...
 <ItemGroup>
      <DistFiles Include="$(SpaRoot)build\**" />
      <ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
        <RelativePath>wwwroot\%(RecursiveDir)%(FileName)%(Extension)</RelativePath>
...

Second template:

...
# SpaProxy stuff missing
  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="6.0.0-preview.5.21301.17" />
  </ItemGroup>
...
 <ItemGroup>
      <DistFiles Include="$(SpaRoot)build\**; $(SpaRoot)build-ssr\**" />
      <ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
        <RelativePath>%(DistFiles.Identity)</RelativePath>

Note that in the second template Microsoft.AspNetCore.SpaServices.Extensions is used instead of Microsoft.AspNetCore.SpaProxy.

There are also some differences in the Startup.cs files. The second template has some additional middleware used

    public void ConfigureServices(IServiceCollection services)
    {
      services.AddControllersWithViews();

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

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
      ...
      // NEW
      app.UseSpaStaticFiles();
      ...
      // NEW
      app.UseSpa(spa =>
      {
        spa.Options.SourcePath = "ClientApp";

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

I also observed that in the JS ClientApp there is a setupProxy.js file whereas in the TS ClientApp there is none. Also the npm start command is set to "start": "rimraf ./build && react-scripts start" and in the TS file it's only react-scripts start.

I tried to make the necessary adjustments to run the JS app like the TS one, but I think I'm missing something since I've ran into an issue:

System.Net.Http.HttpRequestException: Failed to proxy the request to http://localhost:61105/, because the request to the proxy target failed. Check that the proxy target server is running and accepting requests to http://localhost:61105/.

While this error does make sense, I don't get where and how the TS template starts such a proxy server, if it does at all.

My question is: Can I get the behaviour of the TS template in the JS template? If yes, how? If not, why?

Upvotes: 8

Views: 21025

Answers (3)

Todd Smith
Todd Smith

Reputation: 17272

  1. Create "ASP.NET Core Web API" project
  2. In launchSettings set https.applicationUrl to "https://localhost:3000;http://localhost:3001"
  3. Change WeatherForecastController from [Route("[controller]")] to [Route("api/[controller]")]
  4. Add a ClientApp folder to the solution
  5. Inside ClientApp run >npm create vite@latest and choose your template (react/vue/etc.)
  6. npm install "vite-plugin-mkcert"
  7. Update vite.config.ts with the following. This will run the frontend on https://localhost:5000 and the backend on https://localhost:5000/api which proxies to https://localhost:3000/api
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import mkcert from "vite-plugin-mkcert";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react(), mkcert()],
  server: {
    port: 5000,
    https: true,
    strictPort: true,
    proxy: {
      "/api": {
        target: "https://localhost:3000",
        changeOrigin: true,
        secure: false,
        rewrite: (path) => path.replace(/^\/api/, "/api"),
      },
    },
  },
  1. Now you can restart your backend without having to start/stop the frontend or mess with the Microsoft SpaProxy nonsense.

Upvotes: 9

Jono Rogers
Jono Rogers

Reputation: 171

While this error does make sense, I don't get where and how the TS template starts such a proxy server, if it does at all.

It doesn't. I just spent ages chasing my tail figuring this magic out.. and it's create-react-app: create-react-app docs

If the proxy option is not flexible enough for you, you can get direct access to the Express app instance and hook up your own proxy middleware.

You can use this feature in conjunction with the proxy property in package.json, but it is recommended you consolidate all of your logic into src/setupProxy.js.

Next, create src/setupProxy.js and place the following contents in it:

This github issue about what they've changed and why also provides a bit more context but to be honest, it's a complete hot mess at the moment. I wish I'd just made a spa on .NET 5 rather than trawl through all this mess

Upvotes: 1

BlazingFrog
BlazingFrog

Reputation: 2395

I don't get where and how the TS template starts such a proxy server, if it does at all.

That's what spa.UseReactDevelopmentServer(npmScript: "start") does for you. Look at your package.json and you'll see that the start script starts the proxy server on port 5002.

Upvotes: 2

Related Questions