从壹开始学习NetCore 44 ║ 最全的 netcore 3.0 升级实战方案

缘起

1、哈喽大家中秋节 (后) 好呀!感觉已经好久没有写文章了,但是也没有偷懒哟,我的视频教程《 系列一、NetCore 视频教程(Blog.Core) 》也已经录制八期了,还在每周末同步更新中,欢迎大家多多指教。

2、除此之外呢,我也在平时的时间帮朋友开发了一个小项目,就是使用 .net mvc+vue+ele+mongo 框架写的项目,之前一直想着用mvc结合着vue写,这次也终于上手了,不过是一个小的demo,因为是朋友的项目,所以就不开源了。

言归正传,:point_right:  从2018年8月就开始听说 netcore 要准备3.0了,:point_right: 到了近期 v3.0.0-preview9 的发布,官方也最终定稿不会再更新了, :point_right:  接着马上 在下周 9月23日至25日 .NET Conf 社区大会上,会正式推出 netcore3.0 版本, 最后 :point_right: 微软会将 .netcore 和 .net 进一步融合,推出完美跨平台 net 5.0 版本,这里暂时先不说 ,单单从这一年里 netcore 3.0 的快速发展、迭代以及接受用户的反馈进一步修改中,我们就能感觉的到,微软是如何的有希望并且有信心在未来的发展中,将微软系产品进一步融入到广大开发者的心中,我们也要有信心微软能做到这一点。

前言

在netcore 3.0 马上要到来之际,我也要尝尝鲜,我肯定不是第一个吃螃蟹的人,博客园这两个月也是一直轰轰烈烈的进行 3.0 的更新和迭代,不过过程是怎样的吧,至少结果目前还是可以的,也可以作为一个成功案例给大家提供一些建议和思路。

感觉尝试就是成功的一半,所以我在中秋节这两天,也把 Blog.Core  项目给提升到了 3.0 版本,大家现在看的我的在线地址( http://apk.neters.club/index.html ) 就是netcore 3.0 的,总体看起来,可能没有什么差别,而且运行中也没有发现任何问题,(具体的请看我的后台项目: http://vueadmin.neters.club/ ),不过这次官方更新的东西还是稍微挺多的,所以我这里就统一做下记录,方便大家吧,希望每一个在使用 netcore 的小伙伴都能从这里得到一些帮助,虽然官网也有一些记录,但是我看了看,英文的可能有些小伙伴不好理解,尽管有中文翻译版,可是看着不是很通顺,而且也不是很全,大家可以看看: 地址

当然不仅仅包括下边的这几点,我还在慢慢更新,如果你使用到了我 blog.core 项目中没有用到的技术,并且自己在更新 3.0 的时候出现了问题,可以和我聊聊,我在下边补充下,争取达到一个最全的解决方案合集。

好啦,废话到此结束,马上开始今天的迁移报告内容!:rainbow::rainbow::rainbow:

零、NetCore3.0 有哪些新特性

netcore 1.0 到 2.0 主要的是网络和云服务的升级,那 net core 从2.0 到 3.0 更新的是哪些呢?

这里我就简单的列举了下这一年来netcore 3.0 更新的比较热门的特性,当然还有其他的,因为本篇文章主要是讲解升级实战,所以对以下特性就不过多的铺开讲解。

性能、性能、性能,重要的地方说三遍

在机器学习,AI等很好的支持

对Winform、WPF的支持

gRPC的添加

支持 API 授权在单页面应用 (Spa) 中提供身份验证、实现 Open ID Connect 的IdentityServer结合。

Worker Service 模板,为开发做服务或监控微服务相关Bus

Microsoft.Data.SqlClient:独立存在于.NET Framework和.NET Core中

ReadyToRun

HttpClient支持HTTP/2

Json.NET 不在内置在框架内,使用System.Text.Json

HostBuilder 替换掉WebHostBuilder

Blazor 是一个用于使用 .NET 生成交互式客户端 Web UI 的框架,用c#开发前端

.NET Framework不支持.NET Standard 2.1

IL linker

发布成单个程序 dotnet publish -r win10-x64 /p:PublishSingleFile=true

那下面我就针对我的 Blog.Core 项目,坐下迁移的说明,一共八个方面,不是很多,大家可以一一对比下。

一、项目启动部分

操作前必备:备份文件,这个很重要,我们要玩儿新花样,肯定要做好备份文件,可别因为升级失败,而不好回退。

当然我的操作是直接操作的 Blog.Core 项目,因为项目在 git 上,如果不成功,就直接回退,这种资源管理工具还是很有必要的。

1、安装SDK

首先可以查看自己的本地 SDK 是什么版本的,比如我的目前只有 2.1和 2.2 :

所以,如果我们要升级 3.0 的话,就肯定要安装指定的 SDK 了,下载地址: https://dotnet.microsoft.com/download/visual-studio-sdks

选择指定版本的 SDK ,然后进行安装,最后我们就可以看到我们的本地已经安装好了:

这里我们可以看到我们的 3.0 的 SDK 已经安装好了,最后再做个验证,就是在我们的 VS 2019 中,查看是否有 3.0 的框架:

竟然没有??!!别慌,这里有两个方法:

1、 工具 -> 选项 -> 项目与解决方案 -> 右侧,勾选预览版(这个方案是2019 最老版本的 和 2017 版本的)

2、在工具 -> 选项 -> 环境里(我建议使用这个):

然后我们把 vs 重新启动一下,发现已经有了:

安装好了 SDK,我们就已经是成功了一半了,下边我们就正式开始升级打怪之路。

但是这里还有一个问题,就是打开的项目属性里,虽然有了 3.0 的框架,但是新建的项目,依然没有 3.0 的部分,那这个是为什么呢?

这里网上的方案是: 不要用preview8或者9,这两个版本出不来core3.0的选项,preview7没有问题。如果非要用最新版,可以用dotnet new创建项目,或者等下星期的 net core 3.0正式版出来,这样就不用来来回回勾选了

2、更新框架以及所有依赖

刚刚我们已经成功的安装好了 3.0 的 SDK ,那接下来就是正式开始升级项目,首先呢,就是需要更新我们的目标框架,这里有两种方法:

第一种是直接修改我们的项目文件 .csproj ,修改节点 <TargetFramework>netcoreapp3.0</TargetFramework>,并移除关于 Aspnetcore 2.2 相关的包;

第二种就是直接右键项目,属性,应用程序,修改目标框架到 netcore 3.0 就行,就是上文截图中显示的那个,我个人采用的是这种方法。

记得要把项目从底层开始更新,比如从 Model 层和 Common 层开始更新,然后最后更新 API 层,就是从下向上,(这里有个小问题,就是出现修改了,CTRL S 保存后,又重新回到2.2了,可以重启下项目,重启下vs就行了)。

代码修改对比图:

(netcore 3.0 修改sdk框架)

接下来,就是把项目中用到的所有nuget包都更新到最新的版本,因为有些是为了迎接 netcore 3.0,做了相应的修改,比如下午说到的 swagger ,一定要更新到 5.0+版本。

到了这里,我们的项目已经把框架和依赖升级完成了,是不是很简单,重新编译,运行,这里肯定会有错误,别着急,接下来我们就进一步修改 Code 中出现的bug。

3、宿主机变化(Program.cs)

netcore 3.0,对 host 做了调整,底层没有发生太多的变化,这里不细说,主要说要修改的地方,更新的内容,我会在视频里,详细给大家讲解。

在 Program.cs 文件中,修改HostBuilder生成方法, 注意在main 方法里引用也要做相应的修改

代码修改对比图:

(Program.cs 修改 host 宿主机)

CODE:

 public static IHostBuilder CreateHostBuilder(string[] args) =>
   Host.CreateDefaultBuilder(args)
     .ConfigureWebHostDefaults(webBuilder =>
     {
         webBuilder
           .UseStartup<Startup>()
           .UseUrls("http://localhost:8081")
           //这里是配置log的
           .ConfigureLogging((hostingContext, builder) =>
           {
               builder.ClearProviders();
               builder.SetMinimumLevel(LogLevel.Trace);
               builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
               builder.AddConsole();
               builder.AddDebug();
           });
     });

4、Host 环境变量(Startup.cs)

从上边我们也可以看得出来,官方更新了 host ,所以自然而然的,也更新了部分的命名空间,这样就出现了一个问题:

当 Microsoft.Extensions.Hosting 在 2.1 中被引入时,某些类型 IHostingEnvironment 和  IApplicationLifetime 是从 Microsoft.AspNetCore.Hosting 复制的。某些 3.0 更改会导致应用同时包含 Microsoft.Extensions.Hosting 和 Microsoft.AspNetCore.Hosting 两个命名空间。当同时引用两个命名空间时,对这些重复类型的任何使用都会导致"不明确的引用"编译器错误。

所以官方就对某些命名空间和类做了修改:

Obsolete types (warning):

Microsoft.Extensions.Hosting.IHostingEnvironment
Microsoft.AspNetCore.Hosting.IHostingEnvironment
Microsoft.Extensions.Hosting.IApplicationLifetime
Microsoft.AspNetCore.Hosting.IApplicationLifetime
Microsoft.Extensions.Hosting.EnvironmentName
Microsoft.AspNetCore.Hosting.EnvironmentName

New types:

Microsoft.Extensions.Hosting.IHostEnvironment
Microsoft.AspNetCore.Hosting.IWebHostEnvironment : IHostEnvironment
Microsoft.Extensions.Hosting.IHostApplicationLifetime
Microsoft.Extensions.Hosting.Environments

这个不用记忆,到时候使用的时候,会有提示的,那我们的项目中,有哪个地方需要修改呢,就是配置中间件的时候有一个环境变量配置需要修改下:

1、将 IHostingEnvironment env 改成 IWebHostEnvironment env,这个时候会报错,因为命名空间变了;
2、所以需要引用新的命名空间: using Microsoft.Extensions.Hosting;

到了这里,我们就完全修改好了宿主机的部分,现在项目还不能正常的使用,还需要继续修改 mvc 部分,别着急,慢慢往下看。

二、MVC 部分

刚刚我们修改了宿主机 host ,启动项目的时候,还是会有错误,主要提示我们的中间件 .UseMvc() 已经不能被使用了,3.0后,对mvc做了较大的修改,主要从两个方面,一个是服务注册,一个是中间件的拆分:

1、MVC 服务注册(Startup.cs)

在 netcore 3.0 中,官方对 mvc 服务做了细分,主要有以下几个部分:

  services.AddMvc();// 我们平时2.2使用的,最全面的mvc服务注册
  services.AddMvcCore();// 稍微精简的mvc注册
  services.AddControllers();// 适用于api的mvc部分服务注册
  services.AddControllersWithViews();//含有api和view的部分服务注册
  services.AddRazorPages();//razor服务注册

我们看出来,如果我们的项目是webapi的,那只需要注册 .AddControllers() 这个就够了,.AddMvc() 里边服务太多,会造成浪费,而大材小用。

代码修改对比图:

2、MVC 中间件的拆分(Startup.cs)

除了上边的 mvc 服务注册以外,我们还需要对 UseMvc() 中间件做修改。

官方已经正式去掉了Mvc()这个短路中间件,取代他的是 .UseEndpoints() 方法,我们可以做以下修改:

代码修改对比图:

CODE:

 app.UseRouting();//路由中间件
 // 短路中间件,配置Controller路由
 app.UseEndpoints(endpoints =>
 {
     endpoints.MapControllerRoute(
         name: "default",
         pattern: "{controller=Home}/{action=Index}/{id?}");
 });

到了这里,我们已经完成了 netcore 2.2 到 net core 3.0 的最简单的升级,如果你想尝试下,可以自己手动建立一个空的 2.2 项目,实现到 3.0 的迁移,我们运行项目,可以看到已经成功的启动起来,还是很成功的。这里要注意下中间件的顺序,一般的顺序是这样的:

  app.UseStaticFiles();

  app.UseRouting();

  app.UseCors();

  app.UseAuthentication();
  app.UseAuthorization();

  app.UseEndpoints(endpoints =>
  {
      endpoints.MapHub<ChatHub>("/chat");
      endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
  });

那是不是到了这里已经完成了呢,答案当然是否定的,我们的项目不可能这么简单,肯定还会有其他的依赖,还有各种各样的中间件,那我们在升级的过程中,还会有哪些地方需要做处理呢,就比如下边的这些。

三、Swagger  部分

在 netcore 3.0 中,要求我们使用的是 swagger 5.0 ,而且变化的内容也挺多的,但是原理和思路都是一样的,大家一看就知道了,所以我就直接贴代码了。

1、代码修改对比图(ConfigureServices)

这次的修改,主要是服务的注册部分,中间件没有变化,所以我们直接在 startup.cs 中的 configureService 中,做下调整:

这里要注意下,需要引用两个 Nuget 包: Swashbuckle.AspNetCore 和  Swashbuckle.AspNetCore.Filters

2、修改后的完整代码

services.AddSwaggerGen(c =>
{
    typeof(ApiVersions).GetEnumNames().ToList().ForEach(version =>
    {
        // swagger文档配置
        c.SwaggerDoc(version, new OpenApiInfo
        {
            Version = version,
            Title = $"{ApiName} 接口文档",
            Description = $"{ApiName} HTTP API " + version,
            Contact = new OpenApiContact { Name = ApiName, Email = "Blog.Core@xxx.com", Url = new Uri("https://www.jianshu.com/u/94102b59cc2a") },
            License = new OpenApiLicense { Name = ApiName, Url = new Uri("https://www.jianshu.com/u/94102b59cc2a") }
        });
        // 接口排序
        c.OrderActionsBy(o => o.RelativePath);
    });

    // 配置 xml 文档
    var xmlPath = Path.Combine(basePath, "Blog.Core.xml");
    c.IncludeXmlComments(xmlPath, true);
    var xmlModelPath = Path.Combine(basePath, "Blog.Core.Model.xml");
    c.IncludeXmlComments(xmlModelPath);

    c.OperationFilter<AddResponseHeadersFilter>();
    c.OperationFilter<AppendAuthorizeToSummaryOperationFilter>();
    // 很重要!这里配置安全校验,和之前的版本不一样
    c.OperationFilter<SecurityRequirementsOperationFilter>();

    // 开启 oauth2 安全描述
    c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
    {
        Description = "JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}(注意两者之间是一个空格)\"",
        Name = "Authorization",
        In = ParameterLocation.Header,
        Type = SecuritySchemeType.ApiKey
    });
});

代码中,变化比较大的地方,我已经用红色标注,某些又做了注解,不过大部分的内容还是和之前是一样的,相信大家都能看得懂。

四、Autofac 部分

关于依赖注入框架 Autofac 的变化,整体来说不是很大,主要是在依赖容器的使用上,在 2.2 的时候,我们是直接修改的的 ConfigureServices ,然后将容器实例给 return 出去,但是 3.0 之后,ConfigureServices 不能是返回类型了,只能是 void 方法,那我们就不用 return 出去了,官方给我们提供了一个服务提供上工厂,我们从这个工厂里拿,而不是将服务配置 return 出去。

1、代码修改对比图

1、首先我们需要在 Program.cs 中的 CreateHostBuilder 中,添加Autofac的服务工厂:

2、然后在 startup.cs 文件中, 新建一个 ConfigureContainer(ContainerBuilder builder) 的方法 ,里边的内容就是我们之前写的 Autofac 的代码, 把之前在 configureService 中的代码都删掉。

只不过我们这里是注入了 builder 的实例对象,不用new了,然后也不用 build 容器了,交给了 hotst 帮助我们一起 build。

如果有任何看不懂的,请查看我的 Blog.Core 项目中的代码。

2、修改后的完整代码

Startup.cs 中,新增 ConfigureContainer 方法,删除 ConfigureService中,所有有关 Autofac 的内容:

     public void ConfigureContainer(ContainerBuilder builder)
        {
            var basePath = Microsoft.DotNet.PlatformAbstractions.ApplicationEnvironment.ApplicationBasePath;

            //注册要通过反射创建的组件
            //builder.RegisterType<AdvertisementServices>().As<IAdvertisementServices>();
            builder.RegisterType<BlogCacheAOP>();//可以直接替换其他拦截器
            builder.RegisterType<BlogRedisCacheAOP>();//可以直接替换其他拦截器
            builder.RegisterType<BlogLogAOP>();//这样可以注入第二个

            // ※※★※※ 如果你是第一次下载项目,请先F6编译,然后再F5执行,※※★※※

            #region 带有接口层的服务注入

            #region Service.dll 注入,有对应接口
            //获取项目绝对路径,请注意,这个是实现类的dll文件,不是接口 IService.dll ,注入容器当然是Activatore
            try
            {
                var servicesDllFile = Path.Combine(basePath, "Blog.Core.Services.dll");
                var assemblysServices = Assembly.LoadFrom(servicesDllFile);//直接采用加载文件的方法  ※※★※※ 如果你是第一次下载项目,请先F6编译,然后再F5执行,※※★※※

                //builder.RegisterAssemblyTypes(assemblysServices).AsImplementedInterfaces();//指定已扫描程序集中的类型注册为提供所有其实现的接口。


                // AOP 开关,如果想要打开指定的功能,只需要在 appsettigns.json 对应对应 true 就行。
                var cacheType = new List<Type>();
                if (Appsettings.app(new string[] { "AppSettings", "RedisCachingAOP", "Enabled" }).ObjToBool())
                {
                    cacheType.Add(typeof(BlogRedisCacheAOP));
                }
                if (Appsettings.app(new string[] { "AppSettings", "MemoryCachingAOP", "Enabled" }).ObjToBool())
                {
                    cacheType.Add(typeof(BlogCacheAOP));
                }
                if (Appsettings.app(new string[] { "AppSettings", "LogAOP", "Enabled" }).ObjToBool())
                {
                    cacheType.Add(typeof(BlogLogAOP));
                }

                builder.RegisterAssemblyTypes(assemblysServices)
                          .AsImplementedInterfaces()
                          .InstancePerLifetimeScope()
                          .EnableInterfaceInterceptors()//引用Autofac.Extras.DynamicProxy;
                                                        // 如果你想注入两个,就这么写  InterceptedBy(typeof(BlogCacheAOP), typeof(BlogLogAOP));
                                                        // 如果想使用Redis缓存,请必须开启 redis 服务,端口号我的是6319,如果不一样还是无效,否则请使用memory缓存 BlogCacheAOP
                          .InterceptedBy(cacheType.ToArray());//允许将拦截器服务的列表分配给注册。 
                #endregion

                #region Repository.dll 注入,有对应接口
                var repositoryDllFile = Path.Combine(basePath, "Blog.Core.Repository.dll");
                var assemblysRepository = Assembly.LoadFrom(repositoryDllFile);
                builder.RegisterAssemblyTypes(assemblysRepository).AsImplementedInterfaces();
            }
            catch (Exception ex)
            {
                throw new Exception("※※★※※ 如果你是第一次下载项目,请先对整个解决方案dotnet build(F6编译),然后再对api层 dotnet run(F5执行),\n因为解耦了,如果你是发布的模式,请检查bin文件夹是否存在Repository.dll和service.dll ※※★※※" + ex.Message + "\n" + ex.InnerException);
            }
            #endregion
            #endregion


            #region 没有接口层的服务层注入

            ////因为没有接口层,所以不能实现解耦,只能用 Load 方法。
            ////注意如果使用没有接口的服务,并想对其使用 AOP 拦截,就必须设置为虚方法
            ////var assemblysServicesNoInterfaces = Assembly.Load("Blog.Core.Services");
            ////builder.RegisterAssemblyTypes(assemblysServicesNoInterfaces);

            #endregion

            #region 没有接口的单独类 class 注入
            ////只能注入该类中的虚方法
            builder.RegisterAssemblyTypes(Assembly.GetAssembly(typeof(Love)))
                .EnableClassInterceptors()
                .InterceptedBy(typeof(BlogLogAOP));

            #endregion

            //这里不要再 build 了
            //var ApplicationContainer = builder.Build();

        }

program.cs

 public static IHostBuilder CreateHostBuilder(string[] args) =>
   Host.CreateDefaultBuilder(args)
     .UseServiceProviderFactory(new AutofacServiceProviderFactory()) //<--NOTE THIS
     .ConfigureWebHostDefaults(webBuilder =>
     {
         webBuilder
           .UseStartup<Startup>()
           .UseUrls("http://localhost:8081")
           .ConfigureLogging((hostingContext, builder) =>
           {
               builder.ClearProviders();
               builder.SetMinimumLevel(LogLevel.Trace);
               builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
               builder.AddConsole();
               builder.AddDebug();
           });
     });

3、变化的核心点

就是将我们的Autofac的容器,从 configureService 中,转向了我们的宿主机中了,步骤是:

1、删除 ConfigureService 中的所有 Autofac 配置内容;

2、将刚刚删除的配置内容,拷贝到新建一个 ConfigureContainer  方法中;

3、在 ConfigureContainer 方法中,不要进行 build 操作,然后 Main 入口方法中的 Build() 去执行。

4、在 Program.cs 的 CreateHostBuilder 中,新增服务工厂实例。

好了,到现在,我们可以尝试看看 Autofac 依赖注入框架,已经可以正常的使用了。

五、Sqlsugar 部分

随着netcore 3.0 的更新,sqlsugar当然也要做相应的优化处理,主要是为了配合 3.0 做处理,作者凯旋兄还是很负责的,及时做了调整,目前 sqlsugar 的版本是 5.0.9 ,我们如果使用 netcore 3.0 的话,就必须要使用。

1、这个 5.0.9 的版本,如果不使用的话,可能会有一个映射错误:

如果遇到了这个错误,直接不要问,更新到最新版本就行。

2、如果更新了以后,发现还有错误,一个《未将对象引用到对象的实例》:

这个时候,你可以尝试重新生成下数据库,好像只需要创建下表结构就行,数据可以导入, 记得做好生产环境数据库备份

其他还没有发现什么问题。

六、Authorization 部分

这个地方其实很简单,刚刚在将 swagger 的时候,我也说到了,有一个地方需要我们注意, 就是安全校验的配置上,现在发生了变化,从服务添加变成了过滤器:

之前我的 Blog.Core 项目使用了权限过滤器公约,这样就算 controller 没有配置 Authorize 的话,也会默认采用这种权限过滤器,

但是现在不行了,必须要在每一个 controller 上配置,才能在 swagger 中出现那个 小锁 的标志,所以我又都在 controller 上,加上了 [Authorize(Permissions.Name)]

如果不配置的话,是没有小锁标志,也就不会启动权限认证的作用的,只有配置了的才有,不仅如此,大家也可以看到,在左侧已经把该接口对应的权限也写上了:

七、JSON 部分

在netcore 3.0 中,它内置了一个 json 工具—— System.Text.Json,而作为改善 ASP.NET Core 共享框架的工作的一部分,已从 ASP.NET Core 共享框架中删除Json.NET 。 如果你的应用程序使用Newtonsoft.Json特定的功能(如 JsonPatch 或转换器),或者如果它是特定于格式 Newtonsoft.Json的类型,那我们就需要重新引用它。

简单来说,就是 3.0 内置了 Text.Json 框架,你可以直接使用,但是我没有用这个,因为我好像中间出现了一个序列化错误,而且我还要取消默认的驼峰命名,所以我还是采用的之前的 Newtonsoft.json,具体的使用方法请看:

1、如果使用 .net core 3.0 内置的 System.Text.Json ,配置方法如下:

services.AddMvc().AddJsonOptions(options =>
{
    options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);
    options.JsonSerializerOptions.PropertyNamingPolicy = null;
});

2、如果使用 Newtonsoft.Json ,配置方法如下:

services.AddControllers()
    .AddNewtonsoftJson(options =>
        options.SerializerSettings.ContractResolver = new DefaultContractResolver());

八、SignalR 部分

这个很简单,官方中间件取消了  UseSignalR 中间件,而放到了  UseEndpoints 短路中间件中,配置如下:

 app.UseRouting();
 app.UseEndpoints(endpoints =>
 {
     endpoints.MapHub<ChatHub>("/api2/chatHub");

     endpoints.MapControllerRoute(
         name: "default",
         pattern: "{controller=Home}/{action=Index}/{id?}");
 });

九、CORS 部分

已经不支持向所有域名开放了,所以下边的 Policy 可以删除了,其他的不用变化:

  c.AddPolicy("AllRequests", policy =>
  {
      policy
      .AllowAnyOrigin()//允许任何源
      .AllowAnyMethod()//允许任何方式
      .AllowAnyHeader()//允许任何头
      .AllowCredentials();//允许cookie
  });

然后就是要注意中间件的顺序,这里记得还要带上 policy 的名称 ,还是 app.UseCors("LimitRequests");:

其他补充中

如果你有其他的用到的,是我没有使用到的, 或者我上文没有提到的注意点,

欢迎想问提问和反馈,我会在这里,给你署名写上,让更多的小伙伴可以学会学号。

谢谢。

十、Github && Gitee

https://github.com/anjoy8/Blog.Core

https://gitee.com/laozhangIsPhi/Blog.Core

app.UseStaticFiles(); app.UseRouting(); app.UseCors(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapHub<ChatHub>( "/chat" ); endpoints.MapControllerRoute( "default" , "{controller=Home}/{action=Index}/{id?}" ); });

我来评几句
登录后评论

已发表评论数()

相关站点

热门文章