0

Background

I wanted to integrate an existing React app to ASP.NET Core web app. The goal was to be able to launch the ASP.NET Core web project and render the React app as I would have rendered it by running npm start from ClientApp folder.

Setup

To check it out, I created a fresh ASP.NET Core app using VS2022 New Project template "ASP.NET Core with React.js and Redux". The project boilerplate only supports up to .NET 5.0 so I immediately upgraded the project to .NET 7.0, and it worked as it supposed to: displaying a menu with Counter and Fetch Data, and React and Redux code all worked fine and hitting breakpoints when I launched the ASP.NET Core web app in debug mode, served by IIS Express. Running command npm start from inside the folder ClientApp (created automatically by the VS) also rendered the same frontend.

It seemed to me that running ASP.NET Core project to achieve the same outcome by running npm start is mainly done through this piece of code in Startup.cs:

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

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

Replace ClientApp content

Now I replaced the entire ClientApp folder content with my own React app (credit this post for the guidance). To make sure the React code works, I ran the usual commands from inside ClientApp:

npm install, npm run build, npm start

All ran without issues. When npm start ran, it rendered to https://localhost:3000/ fine, as shown below:

React landing page when running command npm start

So, standing by itself, everything works.

Error

Now that npm start from the ClientApp folder works, supposedly, without changing anything (since we already have this in place in Startup):

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

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

I expected to see the same React app rendering in browser, as seen earlier when I ran npm start from inside the ClientApp folder.

But instead, I got this mysterious error:

An unhandled exception occurred while processing the request.

IOException: The response ended prematurely.

System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, bool async, CancellationToken cancellationToken)

HttpRequestException: An error occurred while sending the request.
System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, bool async, CancellationToken cancellationToken)

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

The underlying exception message was 'An error occurred while sending the request.'.Check the InnerException for more details.

Microsoft.AspNetCore.SpaServices.Extensions.Proxy.SpaProxy.PerformProxyRequest(HttpContext context, HttpClient httpClient, Task baseUriTask, CancellationToken applicationStoppingToken, bool proxy404s)

Why a dynamic port number?

As you can see from the error, it looks like the app tried to send request to http://localhost:[dynamic port] every time I ran the ASP.NET Core web project. Why is this and how to configure the app to not to do that? I don't see anywhere that I could set that up.

Here is my launchSettings.json:

{
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:28790",
      "sslPort": 44360
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "MyAspCoreWtihReactApp": {
      "commandName": "IIS",
      "launchBrowser": true,
      "applicationUrl": "https://localhost:3000;http://localhost:5000",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

And this is the project meta file:

<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
    <TargetFramework>net7.0</TargetFramework>
    <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
    <TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
    <IsPackable>false</IsPackable>
    <SpaRoot>ClientApp\</SpaRoot>
    <DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
    <StartupObject>MyApp.Core.React.Program</StartupObject>
    <PackageProjectUrl>https://localhost:44360</PackageProjectUrl>
</PropertyGroup>

<ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="5.0.17" />
</ItemGroup>

<ItemGroup>
    <!-- Don't publish the SPA source files, but do show them in the project files list -->
    <Content Remove="$(SpaRoot)**" />
    <None Remove="$(SpaRoot)**" />
    <None Include="$(SpaRoot)**" Exclude="$(SpaRoot)node_modules\**" />
</ItemGroup>

<Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SpaRoot)node_modules') ">
    <!-- Ensure Node.js is installed -->
    <Exec Command="node --version" ContinueOnError="true">
      <Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
    </Exec>
    <Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
    <Message Importance="high" Text="Restoring dependencies using 'npm'. This may take several minutes..." />
    <Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
</Target>

<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
    <!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
    <Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
    <Exec WorkingDirectory="$(SpaRoot)" Command="npm run build" />

    <!-- Include the newly-built files in the publish output -->
    <ItemGroup>
      <DistFiles Include="$(SpaRoot)build\**; $(SpaRoot)build-ssr\**" />
      <ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
        <RelativePath>%(DistFiles.Identity)</RelativePath>
        <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
        <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
      </ResolvedFileToPublish>
    </ItemGroup>
</Target>

</Project>

Summary

  • React app itself works fine when running npm start from inside the ClientApp folder
  • When running the ASP.NET Core web project, the SPA extension supposedly would run npm start but somehow it kept sending http request to a dynamic http port and errored out.

How to solve this dynamic port assigning seemed to be the key?

I also tried to do this line in Startup

spa.UseProxyToSpaDevelopmentServer("https://localhost:44360") 

and it didn't work either.

Thanks in advance for your help!

5
  • When you replaced the content of the ClientApp folder, did you also remove the js files that were generated by the template? These files are required for the spa proxy to function correctly. IIRC the names are 'aspnetcore-https.js' 'aspnetcore-react.js 'setupProxy.js' Commented Apr 6, 2023 at 22:31
  • Thanks for your input. I don't recall seeing any files like that under the ClientApp folder. I just created another Template (ASP.NET Core with React.js and Redux) project and confirmed that these are the only files under ClientApp root folder: .env, .tsconfig.json, eslintrc.json, and package.json . Commented Apr 8, 2023 at 16:17
  • Yes, I read about that yesterday. As it turns out, there are two different setups for the react templates. If you use the one with redux, you get the setup that uses C# to configure the proxy. If you use the one WITHOUT redux, then you get the setup that uses the js files I mentioned before Commented Apr 9, 2023 at 23:01
  • Did you compare the package json files between the two projects? You might just be missing one of the required packages to communicate between c# and js. For example, you might need to have 'http-proxy-middleware' installed Commented Apr 9, 2023 at 23:06
  • Yes, but there is no "http-proxy-middleware" in the original packages.json file. Commented Apr 11, 2023 at 20:35

1 Answer 1

1

Finally solved my problem by making these two changes to my Asp.Net Core Web project:

  1. Added this line "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy" to the launchSettings.json file, below the line " "ASPNETCORE_ENVIRONMENT": "Development" (without this line, I would continue to get error like "HttpRequestException: Failed to proxy the request to http://localhost:[dynamic port number]/, because the request to the proxy target failed. Check that the proxy target server is running and accepting requests to http://localhost:[dynamic port number]/.)
  2. Added this line to project properties page: <SpaProxyServerUrl>https://localhost:3000</SpaProxyServerUrl>. Without this, I got a Http 500 error: "This page isn’t working right now, localhost can't currently handle this request. HTTP ERROR 500"

After these changes, when I launched the project using "IIS Express" profile, I saw this more pleasant message: (from https://localhost:44360, which is the Project Url): "Launching the SPA proxy... This page will automatically redirect to https://localhost:3000 when the SPA proxy is ready."

At the same time, the Windows PowerShell started the command "npm start" and finally rendered the react app on port: 3000, as I would have expected to see if I ran "npm start" manually from the ClientApp root folder.

It was from this MS article I got the tips for fixing my problem, very grateful to the author at Microsoft.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.