You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在同一域名下同时托管.NET7 React SPA与.NET4.7 MVC遗留项目?

项目迁移与SPA配置问题解决

背景

我们正在进行渐进式项目迁移:从基于.NET 4.7 Framework的ASP.NET MVC遗留系统,迁移至.NET 7 Core后端+React(搭配Vite)前端架构。项目规模较大,已完成90%前端迁移、60%后端迁移。当前React并非真正SPA,而是由遗留项目每个View渲染独立React包。目标是将React改造为.NET 7托管的真正SPA,部分页面(如开发连接页、管理页)仍留在遗留项目中,要求:

  • React页面路径:XXXXX/pro/pageA
  • 遗留页面路径:XXXXX/pageB(XXXXX为同一地址,如localhost)

当前问题

  1. .NET 7项目无法启动Vite服务
  2. 单独启动Vite时提示端口80已被占用

相关配置信息

Vite配置(vite.config.js)

export default defineConfig({
  plugins: [react(), tsconfigPaths()],
  build: {
    outDir: 'build',
  },
  base: '/pro',
  server: {
    strictPort: true,
    port: 80,
    host: true
  }
});

.NET 7 Program.cs片段

builder.Services.AddSpaStaticFiles(filesOptions => filesOptions.RootPath = "ClientApp/build"); 
app.UseStaticFiles();
app.UseSpaStaticFiles();

app.UseRouting();

app.Run();

.NET 7 launchSettings.json

{
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iis": {
      "applicationUrl": "http://localhost/pro"
    },

    "profiles": {
       "WebPro": {
          "commandName": "Project",
          "launchBrowser": true,
          "environmentVariables": {
          "ASPNETCORE_ENVIRONMENT": "Development",
          "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
       }
    },
}

.NET 7 .csproj

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

    <PropertyGroup>
        <TargetFramework>net7.0</TargetFramework>
        <RootNamespace>WebPro</RootNamespace>
        <AssemblyName>WebPro</AssemblyName>
        <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
        <TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
        <IsPackable>true</IsPackable>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
        <SpaRoot>ClientApp\</SpaRoot>
        <DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
        <SpaProxyServerUrl>http://localhost:80/pro</SpaProxyServerUrl>
        <SpaProxyLaunchCommand>npm run dev</SpaProxyLaunchCommand>

        <!-- Set this to true if you enable server-side prerendering -->
        <BuildServerSideRenderer>false</BuildServerSideRenderer>
        <UserSecretsId>efad71c6-743c-4b87-9de8-f26d77146f6d</UserSecretsId>
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="FluentValidation.AspNetCore" Version="10.3.4" />
        <PackageReference Include="Microsoft.AspNetCore.OData" Version="8.0.10" />
        <PackageReference Include="Microsoft.AspNetCore.SpaProxy" Version="7.0.5" />
        <PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="6.0.0" />
        <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="6.0.0" />
        <PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="6.0.0" />
        <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.10.8" />
        <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.0">
            <PrivateAssets>all</PrivateAssets>
            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
        </PackageReference>
        <PackageReference Include="NewRelic.LogEnrichers.Serilog" Version="1.0.1" />
        <PackageReference Include="Serilog" Version="2.10.0" />
        <PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />
        <PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
        <PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
        <PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
        <PackageReference Include="System.ServiceModel.Duplex" Version="4.10.*" />
        <PackageReference Include="System.ServiceModel.Federation" Version="4.10.*" />
        <PackageReference Include="System.ServiceModel.Http" Version="4.10.*" />
        <PackageReference Include="System.ServiceModel.NetTcp" Version="4.10.*" />
        <PackageReference Include="System.ServiceModel.Security" Version="4.10.*" />
    </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>

    <PropertyGroup>
        <RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
    </PropertyGroup>

    
    <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)dist\**" />
        <ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
          <RelativePath>wwwroot\%(RecursiveDir)%(FileName)%(Extension)</RelativePath>
          <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
          <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
        </ResolvedFileToPublish>
      </ItemGroup>
    </Target>

</Project>

解决方案

1. 解决端口占用问题

端口80被占用大概率是遗留.NET 4.7项目或其他服务正在使用:

  • 先关闭遗留项目,再尝试启动Vite;
  • 或修改Vite为非80端口(如默认的5173),调整Vite配置:
    server: {
      strictPort: true,
      port: 5173,
      host: true
    }
    
    同时更新.csproj中的SpaProxyServerUrl为http://localhost:5173/pro

2. 修复.NET 7启动Vite服务的问题

  • 完善launchSettings.json的WebPro配置,添加独立端口避免冲突:
    "profiles": {
       "WebPro": {
          "commandName": "Project",
          "launchBrowser": true,
          "applicationUrl": "http://localhost:5000",
          "environmentVariables": {
          "ASPNETCORE_ENVIRONMENT": "Development",
          "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
       }
    }
    
  • 补充Program.cs的SPA路由配置,确保/pro请求转发到Vite开发服务器:
    app.UseRouting();
    
    // 放置其他中间件(如授权、API路由等)
    
    app.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
    
    app.UseSpa(spa =>
    {
        spa.Options.SourcePath = "ClientApp";
    
        if (builder.Environment.IsDevelopment())
        {
            spa.UseProxyToSpaDevelopmentServer("http://localhost:5173/pro");
        }
    });
    
    app.Run();
    
  • 修正.csproj的发布目标,匹配Vite的build输出目录:
    <DistFiles Include="$(SpaRoot)build\**" />
    

3. 实现路径分流(.NET 7与遗留项目共存)

要让/pro路径走.NET 7+SPA、其余路径走遗留项目,可通过IIS配置:

  • 配置两个站点:遗留项目绑定localhost处理非/pro请求,.NET 7项目绑定localhost/pro处理/pro开头请求;
  • 或使用IIS URL重写规则,将/pro/*请求转发到.NET 7项目,其余请求转发到遗留项目。

内容的提问来源于stack exchange,提问作者Nico_tvn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 00:17:02