dotnet/aspnetcore

New SPA Integration: Modify SpaProxyLaunchManager to unblock the Vite frontend build tool

johanndev opened this issue · 3 comments

Is your feature request related to a problem? Please describe.

I'm trying to integrate the Vite frontend build tool with the new SPA integration introduced with .NET 6 Preview 4.

Unfortunately, during startup of the app via dotnet run, the ProbeSpaDevelopmentServerUrl method is unable to establish a connection with the Vite dev server and always reports HTTP 404 NotFound.

The reason is that by default, the .NET HttpClient only accepts specific content types in the response (I'm guessing plain text/JSON/XML), but the Vite dev server sends its responses with the content type set to text/html (reasoning from the creator of Vite here).
EDIT: After some more investigation, this turns out to be false - HttpClient will happily accept responses regardless of the provided content type. The problem occurs because Vite is unable to correctly perform its spa-fallback logic without an explicit ACCEPT Header.

After running the frontend dev server in debug mode via vite --debug, we get a descriptive error message:

Vite logs for an request sent without an ACCEPT Header:

  vite:spa-fallback Not rewriting GET / because the client did not send an HTTP accept header. +46s

Vite logs for an request sent with the ACCEPT Header set to */*:

  vite:spa-fallback Rewriting GET / to /index.html +0ms

Describe the solution you'd like

After the HttpClient used for probing the SPA development server is instantiated, add a default accept header for text/html:

httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/html"));

or even a wildcard entry:

httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));

Minimal steps to reproduce the behavior between .NET HttpClient and Vite:

  1. Create a new dotnet console project: dotnet new console -o httpClientTest
  2. Replace the contents of Program.cs with:
using System.Net.Http;
using System.Net.Http.Headers;

var httpClient = new HttpClient();
// Intentionally commented out:
// httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/html"));

var response = await httpClient.GetAsync("http://localhost:3000");

System.Console.WriteLine($"Response status code: {response.StatusCode}");
  1. Create a new Vite project and start the development server:
npm init @vitejs/app my-vue-app --template vue-ts
cd my-vue-app
npm install
npm run dev
  1. Start the httpClientTest application via dotnet run, expected output:
> dotnet run
Response status code: NotFound
  1. Add a default accept header by commenting in line 5 in Program.cs and re-run the app:
> dotnet run
Response status code: OK

@johanndev thanks for contacting us.

We would be ok if you want to contribute a PR for this.

@javiercn Ok, I'll handle it :)

@javiercn Unfortunately, my first PR #33153 was not complete - I missed the second instantiation of the HttpClient in the IsSpaProxyRunning method :/

I created #33164 which moves the instantiation of the HttpClient into a private method to ensure that it is always created with the same options. I also moved the creation of the HttpClient closer to the point of usage.