将我的应用程序迁移到 ASP.NET Core 3.0 后,之前有效的索引 URL returns 404

After migrating my app to ASP.NET Core 3.0, the previously valid Index URL returns 404

一个应用程序从 ASP.NET Core 2.0(我认为)开始,然后迁移到 2.1,然后迁移到 2.2,现在我正在尝试将它迁移到 3.0 但失败了...

我阅读并尝试应用 official migration docs 中的说明,据此我应该(除其他外)将 services.AddMvc() 替换为 services.AddRazorPages() 并将 app.UseMvc() 替换为app.UseEndpoints(endpoints => {endpoints.MapRazorPages();}) 如果我使用的是 Razor Pages。据我所知,我一直在使用 Razor Pages 而不是成熟的 MVC,所以这就是我所做的。

现在以前工作的 URL return HTTP 404 而不是任何内容...

例如,//Index 路由会执行此操作,即使在项目目录中有一个 Pages/Index.cshtml 文件和一个 Pages/Index.cshtml.cs 文件。 (尽管很奇怪:也许只有索引 url 失败了 - 我只是尝试将浏览器指向 /Error 并且它起作用了!)

Pages/Index.cshtml.cs 内容(与工作版本相同):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Mon.Battle;

namespace mon.Pages
{
    public class IndexModel : PageModel
    {
        public IndexModel(IBattleManager battleManager)
        {
            // I hope I don't have to lock this dict here, I'm only reading
            configurationSerialized = battleManager.configurationSerialized;
        }

        public ConfigurationSerializedFormat configurationSerialized;

        public void OnGet()
        {

        }
    }
}

Pages/Index.cshtml也有一些内容,但是太长太乱了post这里就整体...不过肯定是return什么的 return在迁移到 3.0 之前正在做一些事情。

Index.cshtml 顶部的页面指令足够短,但是:

@* TODO: The site becomes ugly :( Should I start using Bootstrap, instead of trying to handcraft CSS? *@
@* Hey... I actually start to like how the site looks :) *@

@page

@using System.Text.Encodings.Web
@using Microsoft.Extensions.Configuration
@inject JavaScriptEncoder jsencoder
@inject IConfiguration conf
@using static System.Text.Json.JsonSerializer
@model IndexModel
@{
    Layout = null;
}

不幸的是,这些必须与迁移前的版本稍作更改:即自从 3.0 删除 Newtonsoft.JSON,我不得不将其替换为 System.Text.Json

我当前的 Startup.cs(我想我准确地应用了上述文档中的说明):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.EntityFrameworkCore;
using mon.Data;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Mon.Chat;
using Mon.MatchMaker;
using Mon.Battle;
using Mon.Player;

namespace mon
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            services.Configure<IdentityOptions>(options =>
            {
                options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
            });

            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(
                    Configuration.GetConnectionString("DefaultConnection")));
            services.AddDefaultIdentity<ApplicationUser>().AddRoles<IdentityRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>();

            services.AddRazorPages();

            services.AddSignalR();

            services.AddSingleton<IBattleManager, BattleManager>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseDatabaseErrorPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseRouting();

            app.UseAuthentication();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapHub<ChatHub>("/chathub");
                endpoints.MapHub<MatchMakerHub>("/mmrhub");
                endpoints.MapHub<BattleHub>("/battlehub");
                endpoints.MapRazorPages();
            });
        }
    }
}

上一个 Startup.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using mon.Data;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Mon.Chat;
using Mon.MatchMaker;
using Mon.Battle;
using Newtonsoft.Json.Serialization;
using Mon.Player;

namespace mon
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            services.Configure<IdentityOptions>(options =>
            {
                options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
            });

            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(
                    Configuration.GetConnectionString("DefaultConnection")));
            services.AddDefaultIdentity<ApplicationUser>().AddRoles<IdentityRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>();

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

            services
                .AddSignalR()
                .AddJsonProtocol(options =>
                {
                    options.PayloadSerializerSettings.ContractResolver = new DefaultContractResolver();
                });

            services.AddSingleton<IBattleManager, BattleManager>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseDatabaseErrorPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseCookiePolicy();

            app.UseAuthentication();

            app.UseSignalR(routes =>
            {
                routes.MapHub<ChatHub>("/chathub");
                routes.MapHub<MatchMakerHub>("/mmrhub");
                routes.MapHub<BattleHub>("/battlehub");
            });

            app.UseMvc();
        }
    }
}

我做错了什么?我错过了什么?为什么 /Index return HTTP 404?

如果您需要更多信息,我会提供。

有一次我的 .csproj 文件出现问题。确保您的文件没有像这样列出:

<ItemGroup>
   <Content Remove="Views\Extractor\Insert.cshtml" />
   <Content Remove="Views\_ViewImports.cshtml" />
</ItemGroup>

当我们复制粘贴文件/更改构建操作等时可能会发生这种情况