• .NET5梳理


    一、前言

    为什么要搭建.Net Core/.NET5/.NET6/.NETX等平台,跨平台!!!而且比.Net 更容易搭建,速度也更快,所有的包均有Nuget提供,不再像以前的单纯引入组件。

    1、.net core 框架性能测试

    http://www.techempower.com/benchmarks/ 我们可以通过这个web框架性能测试来看看 aspcore 的性能

    2、.net core 执行过程 

    3、中间件执行过程

    启动的时候先执行该中间件类的构造函数,然后一路 Next() ;下去,返回的时候,正好是反向的,执行的是该类的逻辑部分:

    4、AOP切面

    二、创建Core项目

    1、创建过程

    具体的创建过程直接看官网文档即可Get started with ASP.NET Core

    2、注意事项

    开启.net开发前,需要先下载相应的SDK和Runtime。可以根据需要官网下载

    • SDK 和 RunTime 的区别:SDK 是用来开发 NetCore 的,内部捆绑了 Runtime 运行时;但是如果只想运行 NetCore 项目的话,只需要在服务器中安装 Runtime 运行时即可。
    • 判断安装成功:cmd中直接运行dotnet,如果有结果说明安装成功。

    • Https问题:如下图,启用了https安全访问协议,如果启用的话,每次接口都需要是https:xxx,如果是测试可以不用勾选。有很多小伙伴勾选了这个,但是还是一直使用 http 协议去访问,导致找不到响应的接口地址。如果你的项目已经创建好了,每次访问都是HTTPS的,但是你不想这么做,可以在 launthSettings.json 文件中,把sslPort 端口号改成0即可。

    三、项目整体结构分析

    1、Program.cs

    using HYEfficiency.Extensions;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Hosting;
    
    namespace HYEfficiencyCore
    {
        public class Program
        {
            public static void Main(string[] args)
            {
                CreateHostBuilder(args).Build().Run();
            }
    
            public static IHostBuilder CreateHostBuilder(string[] args) =>
                Host.CreateDefaultBuilder(args)
                    .UseSeriLog()
                    .ConfigureWebHostDefaults(webBuilder =>
                    {
                        webBuilder.UseStartup<Startup>();
                    });
        }
    }

    这个Program是程序的入口,Main方法里面的内容主要是用来配置和运行程序的。因为我们的web程序需要一个宿主,所以 BuildWebHost这个方法就创建了一个WebHostBuilder。而且我们还需要Web Server,asp.net core 自带了两种http servers,一个是WebListener, 它只能用于windows系统; 另一个是kestrel, 它是跨平台的。kestrel是默认的web server,就是通过UseKestrel()这个方法来启用的。但是我们开发的时候使用的是IIS Express,调用UseIISIntegration()这个方法是启用IIS Express, 它作为Kestrel的Reverse Proxy server来用。

    如果在windows服务器上部署的话, 就应该使用IIS作为Kestrel的反向代理服务器来管理和代理请求。如果在linux上的话, 可以使用apache, nginx等等的作为kestrel的proxy server。当然也可以单独使用kestrel作为web 服务器,但是使用iis作为reverse proxy还是有很多有优点的: 例如IIS可以过滤请求,管理证书,程序崩溃时自动重启等。

    UseStartup<Startup>(), 这句话表示在程序启动的时候, 我们会调用Startup这个类,Build()完之后返回一个实现了IWebHost接口的实例(WebHostBuilder),然后调用Run()就会运行Web程序,并且阻止这个调用的线程,直到程序关闭。

    关于Program.cs具体可以参考.NET Core项目解读 启动原理(program.cs和startup.cs)

    2、Startup.cs

    using HYEfficiency.Extensions;
    using HYEfficiency.Helpers;
    using HYEfficiency.Models;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using System.IdentityModel.Tokens.Jwt;
    
    namespace HYEfficiencyCore
    {
        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.AddControllers();
            }
    
            // 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.UseHttpsRedirection();
    
                app.UseRouting();
    
                app.UseAuthorization();
    
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapControllers();
                });
            }
        }
    }

    ConfigureServices方法是用来把services注册到容器中去,并配置这些services。这个容器是用来进行dependency injection的,所有注入的services在以后写代码的时候,都可以将它们注入(inject)进去。例如上面的Configure方法的参数, app, env, loggerFactory都是注入进去的services。

    Configure方法是asp.net core程序用来具体指定如何处理每个http请求的,例如我们可以让这个程序知道我使用mvc来处理http请求,那就调用app.UseMvc()这个方法就行. 但是目前, 所有的http请求都会导致返回"Hello World!"。

    看一看我们项目的最后,Configure方法是如何配置的:

    复制代码
            public void Configure(IApplicationBuilder app, IHostingEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    // 在开发环境中,使用异常页面,这样可以暴露错误堆栈信息,所以不要放在生产环境。
                    app.UseDeveloperExceptionPage();
                }
                else
                {
                    app.UseExceptionHandler("/Error");
                    // 在非开发环境中,使用HTTP严格安全传输(or HSTS) 对于保护web安全是非常重要的。
                    // 强制实施 HTTPS 在 ASP.NET Core,配合 app.UseHttpsRedirection
                    //app.UseHsts();
    
                }
    
                #region Swagger
                app.UseSwagger();
                app.UseSwaggerUI(c =>
                {
                    //之前是写死的
                    //c.SwaggerEndpoint("/swagger/v1/swagger.json", "ApiHelp V1");
                    //c.RoutePrefix = "";//路径配置,设置为空,表示直接在根域名(localhost:8001)访问该文件,注意localhost:8001/swagger是访问不到的,去launchSettings.json把launchUrl去掉
    
                    //根据版本名称倒序 遍历展示
                    typeof(ApiVersions).GetEnumNames().OrderByDescending(e => e).ToList().ForEach(version =>
                    {
    // 注意这个 ApiName 和 要和上边 ConfigureServices 中配置swagger的name要大小写一致,具体查看我的blog.core源码 c.SwaggerEndpoint($"/swagger/{version}/swagger.json", $"{ApiName} {version}"); }); }); #endregion #region Authen //app.UseMiddleware<JwtTokenAuth>();//注意此授权方法已经放弃,请使用下边的官方验证方法。但是如果你还想传User的全局变量,还是可以继续使用中间件 app.UseAuthentication(); #endregion #region CORS //跨域第二种方法,使用策略,详细策略信息在ConfigureService中 app.UseCors("LimitRequests");//将 CORS 中间件添加到 web 应用程序管线中, 以允许跨域请求。 //跨域第一种版本,请要ConfigureService中配置服务 services.AddCors(); // app.UseCors(options => options.WithOrigins("http://localhost:8021").AllowAnyHeader() //.AllowAnyMethod()); #endregion // 跳转https app.UseHttpsRedirection(); // 使用静态文件 app.UseStaticFiles(); // 使用cookie app.UseCookiePolicy(); // 返回错误码 app.UseStatusCodePages();//把错误码返回前台,比如是404 app.UseMvc(); }
    复制代码

    3、appsettings.json

    4、wwwroot

    5、launchSettings.json

    四、核心知识点 

    1、Routing路由

    路由有两种方式:Convention-based (按约定),attribute-based(基于路由属性配置的)。其中convention-based (基于约定的) 主要用于MVC (返回View或者Razor Page那种的)。Web api 推荐使用attribute-based。这种基于属性配置的路由可以配置Controller或者Action级别, uri会根据Http method然后被匹配到一个controller里具体的action上。

    常用的Http Method有:

    • Get,查询 HttpGet  '/api/product', '/api/product/1'
    • POST,创建 HttpPost, '/api/product'
    • PUT 整体修改更新 HttpPut, '/api/product/1'
    • PATCH 部分更新 HttpPatch, '/api/product/1'
    • DELETE 删除,HttpDelete, '/api/product/1

    还有一个Route属性(attribute)也可以用于Controller层,它可以控制action级的URI前缀。

    复制代码
    namespace Api.Controllers
    {
        //[Route("api/product")]
        [Route("api/[controller]")]
        public class ProductController: Controller
        {
            [HttpGet]
            public JsonResult GetProducts()
            {
                return new JsonResult(new List<Product>
                {
                    new Product
                    {
                        Id = 1,
                        Name = "牛奶",
                        Price = 2.5f
                    },
                    new Product
                    {
                        Id = 2,
                        Name = "面包",
                        Price = 4.5f
                    }
                });
            }
        }
    }
     
    复制代码

    使用[Route("api/[controller]")],它使得整个Controller下面所有action的uri前缀变成了"/api/product", 其中[controller]表示XxxController.cs中的Xxx(其实是小写)。也可以具体指定, [Route("api/product")],这样做的好处是,如果ProductController重构以后改名了,只要不改Route里面的内容,那么请求的地址不会发生变化。然后在GetProducts方法上面,写上HttpGet,也可以写HttpGet(),它里面还可以加参数,例如: HttpGet("all"), 那么这个Action的请求的地址就变成了 "/api/product/All"。

    2、内容协商 Content Negotiation

    如果 web api提供了多种内容格式,那么可以通过Accept Header来选择最好的内容返回格式: 例如:application/json, application/xml等等。如果设定的格式在web api里面没有,那么web api就会使用默认的格式。asp.net core 默认提供的是json格式,也可以配置xml等格式。目前只考虑 Output formatter,就是返回的内容格式。如果想输出xml格式,就配置这里: 

    3、Validation 验证

    针对上面的Post方法,  如果请求没有Body,参数product就会是null,这个我们已经判断了;如果body里面的数据所包含的属性在product中不存在,那么这个属性就会被忽略。但是如果body数据的属性有问题,比如说name没有填写, 或者name太长,那么在执行action方法的时候就会报错,这时候框架会自动抛出500异常,表示是服务器的错误,这是不对的。这种错误是由客户端引起的,所以需要返回400 Bad Request错误。验证Model/实体,asp.net core 内置可以使用 Data Annotations进行。 

    复制代码
     
    using System;
    using System.ComponentModel.DataAnnotations;
    
    namespace CoreBackend.Api.Dtos
    {
        public class ProductCreation
        {
            [Display(Name = "产品名称")]
            [Required(ErrorMessage = "{0}是必填项")]
            // [MinLength(2, ErrorMessage = "{0}的最小长度是{1}")]
            // [MaxLength(10, ErrorMessage = "{0}的长度不可以超过{1}")]
         [StringLength(10, MinimumLength = 2, ErrorMessage = "{0}的长度应该不小于{2}, 不大于{1}")] public string Name { get; set; } [Display(Name = "价格")] [Range(0, Double.MaxValue, ErrorMessage = "{0}的值必须大于{1}")] public float Price { get; set; } } }
     
    复制代码

    这些Data Annotation (理解为用于验证的注解),可以在System.ComponentModel.DataAnnotation找到,例如[Required]表示必填, [MinLength]表示最小长度, [StringLength]可以同时验证最小和最大长度,[Range]表示数值的范围等等很多。[Display(Name="xxx")]的用处是给属性起一个比较友好的名字。其他的验证注解都有一个属性叫做 ErrorMessage (string), 表示如果验证失败,就会把ErrorMessage的内容添加到错误结果里面去。这个ErrorMessage可以使用参数,{0}表示Display的Name属性,{1}表示当前注解的第一个变量,{2}表示当前注解的第二个变量。在Controller里面添加验证逻辑:

    复制代码
          
    [HttpPost]
            public IActionResult Post([FromBody] ProductCreation product)
            {
                if (product == null)
                {
                    return BadRequest();
                }
    
                if (!ModelState.IsValid)
                {
                    return BadRequest(ModelState);
                }
    
                var maxId = ProductService.Max(x => x.Id);
                var newProduct = new Product
                {
                    Id = ++maxId,
                    Name = product.Name,
                    Price = product.Price
                };
                ProductService.Add(newProduct);
    
                return CreatedAtRoute("GetProduct", new { id = newProduct.Id }, newProduct);
            }
     
    复制代码

     

  • 相关阅读:
    事件DOMContentLoaded与load的区别
    JavaScript的执行环境
    JS中函数运行的执行次序
    正则表达式30分钟入门教程
    mysql数据库备份
    杂篇
    memcached
    mysql问题解决
    php学习
    apache 安装
  • 原文地址:https://www.cnblogs.com/qtiger/p/15305800.html
Copyright © 2020-2023  润新知