ASP.NET Core RESTful风格学习总结(五万字持续更新)

ASP.NET Core RESTful风格学习总结持

  • 项目设置:
    • 项目依赖:
      • Dependencies文件夹:
        • Microsoft.NETCore.App:
        • Microsoft.AspNetXore.App:
      • 依赖管理NuGet:
    • 项目的运行时设置appsetting.json:
    • 项目启动IIS服务器与.NET Core内建的KESTREL服务器
    • 项目的配置信息(启动)设置launchSettings.json
  • Program.cs和Startup.cs
    • 项目启动入口:Program.cs
      • CreateHostBuilder
        • CreateHostBuilder所有的配置过程:
        • CreateDefaultBuilder方法实现的四件事:
    • Startup.cs:
      • ConfigureServices注入各种服务组件的依赖
      • Configure配置http请求通道(Request Pipline):
        • Configure完整代码
      • 环境变量与中间件:
  • 路由
    • 两种路由模式:
      • 传统路由(使用路由表) Conventional routing:**
      • **特征注释路由 Attribute routing**
      • 自定义路由
  • MVC和三层架构
    • MVC
      • 一.什么是MVC
      • 二.视图View
      • 三.模型Model
      • 四,控制器Controller
      • 五,MVC架构的数据如何流动
        • 图示:
        • 步骤解读:
        • 详解:
      • 六.MVC的优点
        • (1)耦合性低
        • (2)可复用性高
        • (3)可维护性高
      • 七.MVC的缺点
        • (1)定义不明确,学习曲线陡
        • (2)结构复杂
        • (3)数据流动效率低
    • 三层架构
      • **一.UI层:**
      • **二.BLL:**
      • **三.DAL层**:
      • 四.三层架构的数据如何流动
        • 图示:
        • 步骤:
        • 详解:
    • MVC VS 三层架构
      • 相同点:
      • 不同点:
  • Model
    • 数据模型设计
      • 业务设计
        • (1)DDD (Domain Driven Design领域驱动开发)
        • (2)根据业务模块建立数据模型
    • 模型的作用
      • 一.映射数据库,对象化数据
      • 二.获取数据,更新数据,传递数据,保存数据等
      • 三.处理业务逻辑,视为Business Layer业务层
      • 四.View Model 与视图数据绑定
    • 模型使用
    • ApiController
      • 标准模板ApiController:
      • 三种方式构建ApiController:
        • 一,直接在Api名称后加上Controller
        • 二,在类前加上[Controller]属性
        • 三,普通类继承Mvc的Controller父类
  • 数据库
  • REST
      • 一.简介
      • 二.RESTful的基本特点
      • 三.6个约束限制
      • 成熟度模型
        • LEVEL 0:
        • LEVEL 1:
        • LEVEL 2:
        • LEVEL 3 超媒体控制:
        • Restful api范例
      • 内容协商与数据格式
        • 内容协商
        • Head
  • DTO
      • 数据模型的两个不稳定因素
      • 使用AutoMapper (NuGet)
      • Automapper的嵌套映射
  • 单点登录JWT与用户身份验证
        • (1)单点登录与JWT
          • 1.JWT的作用:
          • 2.用户授权Authorization vs 身份认证Authentication
          • 3.传统的Session登录 -有状态登录
          • 4.单点登录JWT -无状态登录
        • (2)有状态登录与无状态登录
          • Session vs JWT
        • (3)JWT原理剖析与实例解释
          • JWT原理
        • JWT优缺点
          • JWT的优点:
          • JWT的缺点:
        • 个人理解:
        • JWT注册服务依赖
          • ConfigureServices
          • Configure
        • Claim是什么?
        • 身份认证的数据库链接对象IdentityDbContext
          • 安装框架
          • 修改数据库上下文继承对象
          • 注册IdentityDbCoontext框架服务依赖
          • _signInManager.PasswordSignInAsync(String, String, Boolean, Boolean)
          • IDentity指定使用JWT的Bear验证
          • Identity数据库的表
  • API
      • 继承ControllerBase和继承Controller:
      • [ApiController]
      • [Route("api/[controller]")]
      • IActionResult
      • http atrribute
      • 代码
      • 向API传入参数
        • FromQuery和FromRoute的区别
      • ASP.NETCore 的数据验证
        • 制定数据验证规则
          • DTO vs Model
          • ASP.NET Core自带的数据验证框架
          • 属性级别数据验证
          • 类级别数据验证
          • 输出422状态码
        • 检测数据
          • ASP.NET Core: ModelState
          • 提交错误信息
            • 状态码:
            • 错误信息:ModelState会提供
  • Http状态码
      • 作用:
      • Http状态码五大类
      • Post
        • 安全性与幂等
          • 安全性 (取值)
          • 幂等性(赋值)
      • Put
        • put vs patch
      • Patch
        • Json patch 操作
        • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-izobKRMM-1644390731165)(NoteImage/image-20220126030325153.png)]
  • EF
      • 三种数据库创建方式:
        • 命令行创建:(mac不适用)
        • dotnet 命令
        • Windows 使用dotnet cli注意事项
    • EFCore CodeFirst 注解特性
      • [Table]
      • [Key]
      • [Column]
      • [Required]
      • [StringLength] 与 [MaxLength]
      • [NotMapped]
      • [Index]
      • [ForeignKey]
  • Linq
      • C# Linq First 和 FirstOrDefault的区别
      • IQueryable 延迟执行
        • 延迟执行的目的:
        • Entity Frameword的最佳工具:Linq
      • ToList与FirstOrDefult
  • Docker
      • Docker是什么?
      • Docker的三个概念
      • 下载镜像
      • 列出镜像列表
      • 配置并启用sql镜像
  • 思考
    • 1. Kestrel服务器 vs IIS服务器?
    • 2. 什么是中间件?什么是请求通道?
    • 3.schema是数据库中的什么?
    • 4.DDD(领域驱动设计),你必须知道的贫血模型和充血模型
      • 贫血模型和充血模型
    • 5.HTTP状态码 201 和 204 有什么区别?
    • 6.如何正确理解HTTP状态码?
    • 7.什么是内容协商?
    • 8.Model与DTO有什么区别?
    • 9.AutoMapper是什么?
    • 10.什么是HEAD请求?
    • 11.什么是jwt?
    • 12.jwt与session有什么区别?
    • 13.authorization与authentication有什么区别?
  • 重点详解
      • AppDbcontext:
      • CreatedAtRoute

项目设置:

项目依赖:

Dependencies文件夹:

项目所有的服务依赖及框架都会安装在这个文件夹

其中框架文件夹中包含两个框架:

Microsoft.NETCore.App:

是项目的基础框架,它包含了对代码编译,运行,部署的处理。

Microsoft.AspNetXore.App:

基于基础框架引入的应用层框架,它包含了一系列应用层的服务,认证服务,授权服务,诊断服务,http的请求处理服务,以及文件访问,日志记录,依赖注入等等。

依赖管理NuGet:

用于项目构建和依赖解析的工具,可以完成项目依赖的自动安装,解决依赖冲突。

项目的运行时设置appsetting.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

Logging:日志设置

​ LogLevel:日志级别;

​ Default:日志默认信息;

​ …

​ AllowedHosts:对托管服务器的设置

appsetting.json文件能保存的设置远远对于上面我们所看到的内容

例如数据库链接设置,第三方信息,账号密码等等

项目启动IIS服务器与.NET Core内建的KESTREL服务器

IIS服务器与.NET Core内建的KESTREL服务器

  • IIS 服务器只能运行于windows上
  • .NET Core 服务器 KESTREL可以跨平台

项目的配置信息(启动)设置launchSettings.json

{
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:4123",
      "sslPort": 44304
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "FakeXieCheng": {
      "commandName": "Project",
      "dotnetRunMessages": "true",
      "launchBrowser": true,
      "applicationUrl": "https://localhost:5001;http://localhost:5000",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}
  • launchSettings.json只处理项目的启动设置
  • “profiles”:服务器端口的信息定义在这里
  • ”IIS Express“是IIS服务器的配置信息
  • ”项目名“是.NET Core 服务器KESTREL的配置信息
  • “applicationUrl”:定义了https和http的端口信息,可随意修改端口号

Program.cs和Startup.cs

项目启动入口:Program.cs

  • Program.cs中定义了项目的主方法Main函数,而Main函数只做了一件事情:通过CreateHostBuilder创建并运行了虚拟托管服务器

CreateHostBuilder

CreateHostBuilder所有的配置过程:

在其源码中注释可以了解到CreateHostBuilder所有的配置过程:

  • 查看程序的运行环境

  • 通过运行环境启用相应的配置文件appsettings.json

  • 加载程序集assembly运行系统的所有核心代码

  • 设置环境变量,日志,以及系统的反转控制IOC容器

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup();
            });
    

CreateHostBuilder 方法中Host为虚拟网站托管主机

CreateDefaultBuilder方法实现的四件事:

  • 加载主机以及应用程序的配置信息
  • 配置日志记录
  • 设置WEB服务器
  • 设置ASP.NET应用程序的托管程序

Startup.cs:

默认生成

Startup:

  • 依赖注入:ConfigureServices中注入各种服务组件的依赖
  • 中间件:在Configure中创建中间件,设置请求通道

ConfigureServices注入各种服务组件的依赖

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext(options => options.UseSqlServer("name=ConnectionStrings:DefaultConnection"));

        services.AddMvc();
        services.AddTransient();
        services.AddTransient();
    }

其中注册ASP.NET Core 自带的服务和自定义服务

例如注册ASP.NET Core自带服务的MVC组件

services.AddMvc();

​ 注册控制器组件

services.AddControllers();

而ASP.NET Core Mvc组件的各种方法都可以使用了

Configure配置http请求通道(Request Pipline):

Configure:在运行时被调用,ASP.NET Core 提供了内置的IOC容器

  • 检查&处理http请求

  • 交由中间件MIddleware处理

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P17xFXBp-1644390731150)(E:/Note/image-20220109211554168.png)]

  • 如果当前环境为Development则使用开发者异常界面

    if (env.IsDevelopment())
              {
                  app.UseDeveloperExceptionPage();
              }
    

Configure完整代码

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
      {
          if (env.IsDevelopment())
          {
              app.UseDeveloperExceptionPage();
          }
          else
          {
              app.UseExceptionHandler("/Home/Error");
              // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
              app.UseHsts();
          }
          app.UseHttpsRedirection();
          app.UseStaticFiles();
          app.UseAuthentication();
          app.UseRouting();
     //新中间件短路直接响应
             /* app.Run(async (context) => {
                  await context.Response.WriteAsync("HI");
              });*/
          app.UseAuthorization();

  app.UseEndpoints(endpoints =>
      {
      //在这里进行新路由
              endpoints.MapGet("/test", async context =>
               {
                   await context.Response.WriteAsync("HI");
               });
          endpoints.MapControllerRoute(
              name: "default",
              pattern: "{controller=Home}/{action=Index}/{id?}");
          endpoints.MapRazorPages();
      });

  }

使用Mvc的映射代替直接对路由进行映射

endpoints.MapControllers();

启动Mvc路由映射中间件

环境变量与中间件:

  1. 请求通道由IApplicationBuilder创建

  2. IWebHostEnvironment虚拟主机的环境变量:

    开发过程中,往往会分成:

    开发环境 Development

    集成环境 Intergration

    测试环境 Testing

    预发布环境 Staging

    生成环境 production[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SaA8hDgA-1644390731152)(E:/Note/2.jpg)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NEJB4FDI-1644390731152)(E:/Note/1-16418041944201.jpg)]

  3. 每个中间件都可以截获,修改,传递请求对象,输出响应对象,例如代码中

             //新中间件短路直接响应
               /* app.Run(async (context) => {
                    await context.Response.WriteAsync("HI");
                });*/
    
  4. 在特定情况下,某些中间件可以做短路处理,直接向前端输出响应对象

  5. 所有中间件共用一个请求通道

  6. 不同中间件之前可以通过嵌套来获得更强大的处理能力

  7. 每一中间件截获的请求对象都来自于上一中间件的响应对象,所以中间件是有一定顺序的

路由

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DIwP0sFv-1644390731153)(E:/Note/d1b8b6fe52e6ba23694f905a3ae5448.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jyR7J8Kp-1644390731153)(E:/Note/a67f3344679a35d5396973d397e9ead.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qvXONIk3-1644390731153)(E:/Note/fc77d646e9e0256e44b72d983c0c697.jpg)]

两种路由模式:

传统路由(使用路由表) Conventional routing:**

  1. Url被分段解析为Controller和action两个部分

    例如:{域名}/{controller}/{action}

  2. 使用路由表(routing table)来设置路由规则,使用中间件:

    例如:

        routes.MapRoute(
    
        name:"default",
    
        templete:"{controller=Home}/{action = index}"
    )
    

3.使用默认路由:

 app.UseEndpoints(endpoints =>
            {
endpoints.MapDefaultControllerRoute();
            });

特征注释路由 Attribute routing

  app.UseEndpoints(endpoints =>
        {
            //特征注释路由来映射Controller与action的关系   
            endpoints.MapControllers();
        });

HomeController中

直接方式:

namespace Puzi.Controllers
{
    [Route("Home")]
    public class HomeController : Controller
    {
        [Route("index")]
        public IActionResult Index()
        {
            return View();
        }
        [Route("privacy")]
        public IActionResult Privacy()
        {
            return View();
        }
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0C1cLBlE-1644390731154)(E:/Note/1-16418041944201.jpg)]

映射方式:

[Route("[Controller]")]将会映射HomeController在内的所有Controller

namespace Puzi.Controllers
{
    [Route("[Controller]")]
    public class HomeController : Controller
    {
        [Route("[action]")]
        public IActionResult Index()
        {
            return View();
        }
        [Route("[action]")]
        public IActionResult Privacy()
        {
            return View();
        }
    }
}
namespace Puzi.Controllers
{
    [Route("[Controller]/[action]")]
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }
        public IActionResult Privacy()
        {
            return View();
        }
    }
}

自定义路由

   endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
  • 将统配所有Controller和action

  • controller=Home, action=Index将会使网站打开以后页面根目录会默认到Home与index

  • 同时特征注释路由可以与其它方式通用以增强效果

    namespace Puzi.Controllers
    {
        [Route("PhoneShop/[Controller]/[action]")]
        public class PhoneController : Controller
        {
            public String test()
            {
                return "hi";
            }
            public IActionResult Index()
            {
                return View();
            }
        }
    }
    

MVC和三层架构

MVC

一.什么是MVC

  • 软件工程的架构方式
  • 模型(Model),视图(View)和控制器(Controller)
  • 分离业务操作,数据显示,逻辑控制

二.视图View

  • 用户交互界面
  • 仅展示数据,不处理数据

三.模型Model

  • MVC架构的核心
  • 表示业务模型或数据模型
  • 业务逻辑,如算法实现,数据的管理,输出对象的封装等

四,控制器Controller

  • 接受用户的输入,并调用模型和视图去完成用户的请求处理
  • 不处理数据

五,MVC架构的数据如何流动

图示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IE2bVAIQ-1644390731154)(E:/Note/8b536afafa3a19e8ec83fc76f5253da.jpg)]

步骤解读:

  1. url打开主页
  2. 请求信息通过请求通道传递到主页对应的控制器HomeController
  3. HomeController寻找对应的数据模型
  4. 数据模型去对应的数据库获得HomeController希望得到的原始数据
  5. 数据模型进行业务操作,将原始数据封装;例如封装物品的名称,价格,图片等等
  6. 将封装好的数据交回给HomeController控制器
  7. HomeController取得数据后,会将这些数据传递给视图View
  8. 视图拿到数据后会把数据添加进动态Html中
  9. 通过http的形式把html文件发送到用户的浏览器
  10. 用户查看到主页信息。
  11. 用户点击继续查看
  12. 形成闭环

详解:

​ url打开主页,然后请求信息就会通过请求通道传递到主页对应的控制器HomeController,随后,HomeController开始响应,HomeController会去寻找对应的数据模型,随后数据模型开始工作,它会去对应的数据库获得HomeController希望得到的原始数据,然后进行业务操作,将原始数据封装一下,比如封装物品的名称,价格,图片等等,随后将封装好的数据交回给HomeController控制器,当HomeController取得数据后,就会将这些数据传递给视图View,而视图拿到数据后会把数据添加进动态Html中,最后通过http的形式把html文件发送到用户的浏览器中,最后,用户便能看到主页信息,随后用户点击继续查看,从此,形成闭环。

六.MVC的优点

(1)耦合性低

视图层和业务层可以分离:

可以只更改视图层代码而不需要重新编写模型和控制器的代码,比如,改写jsp,html,css或js的代码,并不需要重启服务器。

同样,一个应用的业务流程或业务规则发生改变,只需要改变mvc的模型,因为模型与控制器,视图相分离,所以很容易改变业务程序的数据层和业务规则。

(2)可复用性高

MVC的所有组件都具有高可复用性,随着技术的不断进步,需要越来越多的方式访问应用程序,MVC模式允许各种各样不同样式的视图来访问同一服务端的代码,多个视图可共用一个模型,比如,用户可以通过web应用,也可以通过手机app来订购同样的产品,虽然订购的方式不一样,但是处理订购的业务是一样的,由于模型返回的数据并没有发生改变,所以同样的功能可以被不同界面使用

(3)可维护性高

分离视图和业务逻辑可以使得web应用易于维护和修改,比如,如果向更改业务逻辑,只需要更改业务逻辑,如果想更改视图,只需要去更改视图,如果想增加功能,只需要增加功能。便于维护和后期代码更新。

七.MVC的缺点

(1)定义不明确,学习曲线陡

大家都是依照自己的经验来解释和使用MVC,而MVC内部原理比较复杂,组合了多种设计模式,完全理解MVC较为困难,对新手不友好

(2)结构复杂

MVC并不适合小型,甚至中型项目,对于简单的界面,严格遵循MVC反而增加结构的复杂性,产生过多的数据操作导致运行效率底下。

(3)数据流动效率低

依据操作模型的接口不同,视图可能需要多次调用才能获得足够的数据显示,对于未变化的数据,频繁的访问模型,也可能造成操作性能下降。

三层架构

一.UI层:

表示用户界面

二.BLL:

业务逻辑层,处理核心业务以及数据封装

三.DAL层:

表示数据访问

四.三层架构的数据如何流动

图示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ph8NP4mH-1644390731155)(E:/Note/1-16418293170482.jpg)]

步骤:

  1. 根据数据库创建DAL。
  2. DAL来获取和映射数据。
  3. DAL得到数据后,传递给BLL业务逻辑层进行数据验证,数据转换以及对象封装。
  4. 封装好的对象传递给UI层显示给用户

详解:

首先,需要一个数据库,根据数据库,创建DAL来获取和映射数据,得到元组数据后,传递给BLL业务逻辑层进行数据验证,数据转换以及对象封装,最后,封装好的对象传递给UI层显示给用户

MVC VS 三层架构

相同点:

  • 由三部分构成

不同点:

  • 三层架构面向接口编程,而三个层级之间完全解耦,完全可替换。自下而上
  • MVC每个部分都是紧密结合的,它的核心不是解耦,而是重用(即同样的Model,可以使用不同的控制器,搭配不同的视图,实现不同的内容)。
  • 三层架构是自下而上的,而MVC是水平结构的,只有调用关系,而没有层级关系,所有的数据流动和显示都是通过数据绑定,事件驱动所处理的

Model

数据模型设计

业务设计

(1)DDD (Domain Driven Design领域驱动开发)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NFMIqZQN-1644390731155)(E:/Note/1-16418944085013.jpg)]

(2)根据业务模块建立数据模型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RfMFlZ4Q-1644390731156)(E:/Note/1-16418946167484.jpg)]

模型的作用

一.映射数据库,对象化数据

模型,也就是一个类,一个class,这个类,包含各种各样的数据属性,这些数据属性可以映射给相应的数据库,而我们可以通过模型的数据映射,把数据库中相对独立的数据串联起来。

二.获取数据,更新数据,传递数据,保存数据等

三.处理业务逻辑,视为Business Layer业务层

系统的核心业务应当被分离到不同的模型中单独处理,以便系统的扩展

四.View Model 与视图数据绑定

使数据和视图直接绑定,甚至可以在视图上做数据的验证

模型使用

  • 定义不明确,经验很重要
  • 可以是简单的POCO类
  • 也可以是复杂的充血型领域模型

数据库

使vs2019自带数据库:右键数据库点击属性即可查看链接字符串

option.UseSqlServer(@"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=FakeXieChengDb;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False");

# Controller

使用docker:

option.UseSqlServer("server=localhost;Database=FakeXieChengDb;Use Id=sa;Password=yourStrong(!)Password");

ApiController

标准模板ApiController:

using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860

namespace FakeXieCheng.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class TestApiController : ControllerBase
    {
        // GET: api/
        [HttpGet]
        public IEnumerable Get()
        {
            return new string[] { "value1", "value2" };
        }

    // GET api//5
    [HttpGet("{id}")]
    public string Get(int id)
    {
        return "value";
    }

    // POST api/
    [HttpPost]
    public void Post([FromBody] string value)
    {
    }

    // PUT api//5
    [HttpPut("{id}")]
    public void Put(int id, [FromBody] string value)
   {
    }

    // DELETE api//5
    [HttpDelete("{id}")]
    public void Delete(int id)
    {
    }
}

}

三种方式构建ApiController:

一,直接在Api名称后加上Controller

using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace FakeXieCheng.Controllers
{
    [Route("api/shoudongapi")]
    public class ShoudongApiController
    {
        [HttpGet]
        public IEnumerable Get()
        {
            return new string[] { "value1", "value2" };
        }
    }
}

二,在类前加上[Controller]属性

using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace FakeXieCheng.Controllers
{
    [Route("api/shoudongapi")]
    [Controller]
    public class ShoudongApi
    {
        [HttpGet]
        public IEnumerable Get()
        {
            return new string[] { "value1", "value2" };
        }
    }
}

三,普通类继承Mvc的Controller父类

using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace FakeXieCheng.Controllers
{
    [Route("api/shoudongapi")]
    public class ShoudongApi:Controller
    {
        [HttpGet]
        public IEnumerable Get()
        {
            return new string[] { "value1", "value2" };
        }
    }
}

数据库

数据迁移文件必须是相同数据库

Microsoft.EntityFrameworkCore.SqlServer

配置数据库

   services.AddDbContext(op =>
            op.UseSqlServer(Configuration["DbContext:ConnectionString"])
            )

REST

一.简介

REpresentational State Transfer 表征性状态转移

REST VS RESTFUL

REST 名词

RESTFUL 形容词

二.RESTful的基本特点

无状态:一次调用就能返回结果,不存在打开链接访问数据库然后关闭连接这种依赖于上次调用的这种情况,也就是说持久性的链接,有状态的链接,不属于RESTFUL的范畴

**面向"资源"*无论是数据还是服务,在RESTFUL这里都是资源,只存在名词不存在动词

**使用http的动词:**使用http的动词表示API的操作:

  • GET:查看
  • POST: 创建
  • PUT:更新
  • PATCH:部分更新
  • DELETE:删除

eg:HTTP GET api/v1/touristRoute

HATOAS 超媒体即应用状态引擎:

好用:面对对象(资源),如增删改查

不好用:如登录:面向过程,如登录

三.6个约束限制

Client-Server

前后端分离

无状态

请求独立

分层系统

代码分层:表示层,业务逻辑层,数据访问层

统一接口

前后端使用接口的格式必须一致,数据统一

API自我发现

好比翻页,点击下一页,不能由我们去改变页数的url而应该由服务其自动给出下一页的url

可缓存

服务器应该给客户端提供一个可缓存的方法,使其可以缓存服务器的响应内容,通过在响应头部中输出缓存相关的信息,我们可以有效的防止客户端频繁的与服务器交互,同时也可以避免客户端出现脏读的错误操作

按需代码

在特殊情况中,服务器可以临时给前端发送代码,让前端去按需进行,去实现服务器的某些特殊的功能,比如javascript,php的代码,让前端去执行,总之,安装业务需求即可

绝大部分后端服务不会100%满足6个约束,

restful只是一种风格,不要本末倒置,能实现需求才是王道

成熟度模型

LEVEL 0:

  • 只要有个api
  • 通过http传输数据
  • 比如:简单对象访问协议SOAP

LEVEL 1:

  • 面向资源

LEVEL 2:

  • HTTP语法

LEVEL 3 超媒体控制:

  • HATEOAS超媒体即应用状态引擎的实现
  • api的自我发现机制,比如我们查看一个订单,而返回的响应中不仅包含订单本身的内容,还会包含取消订单的url,同样,可能还包含如何更新订单的链接的url
  • 超媒体(Hypermedia)=多媒体(Multimedia)+超文本(Hypertext)

Restful api范例

GitHub https://developer.github.com/v3/

豆瓣API https://developers.douban.com/wiki/?title=api_v2

内容协商与数据格式

内容协商

内容协商,允许客户端和服务器通过协商来决定相互之间的数据传输格式,语言等内容

Head

  • 请求头部的媒体类型定义“accept”与“Content-type”
  • 前端使用head传入使用的媒体类型比如“application/JSON,application/xml”可以切换服务器返回的数据格式,如果省略这个head一般来说,后端会以json作为返回的默认格式,但是,当前端传入一个后端无法识别的媒体类型以后,会返回错误代码406 unacceptable,相当于告诉前端无法失败
  • ASP.NET Core 支持内容协商,自动化处理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TZIQqJ3D-1644390731156)(E:/Note/typora-study-notes/NoteImage/image-20220123101806613.png)]

当我们代码并没有截获head里面的application/xml,目前无法处理对xml的请求,所以就返回了默认的数据结构json,另外,如果我们的api不支持,我们应当抛出异常

 services.AddControllers(setupAction => {
                setupAction.ReturnHttpNotAcceptable = true; //设置截取head头部,遇到无法识别时抛出异常
            });
  • 如果设置为false,那么所有的api都会忽略请求的头部默认返回json格式
  • 如果设置为ture,那么截取head头部,遇到无法识别时抛出异常么
        services.AddControllers(setupAction => {
            setupAction.ReturnHttpNotAcceptable = true; //配置,设置截取head头部,遇到无法识别时抛出异常                  }).AddXmlDataContractSerializerFormatters(); //配置,允许服务器处理xml

HTTP HEAD

HEAD与GET类似,但没有响应主体,即api没有返回值,Head请求可以用于相关资源的头部信息,在支持缓存的系统中,这些信息可以用来检测获得的信息是否有效,或者说可以检测出信息最近是否有被修改过,同时HEAD请求可以用于检测资源是否存在,如果存在则返回正常,否则返回404

DTO

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JP6erMyp-1644390731157)(E:/Note/typora-study-notes/NoteImage/image-20220123105601348.png)]

Model:面向业务

DTO则是面向界面,面向UI的,按照界面的需求给予数据

数据模型的两个不稳定因素

直接向前端返回数据模型。会暴露系统的业务核心

使用DTO的时候可以屏蔽我们不需要暴露的核心业务

颗粒度太粗,也就是输出数据无法精细调整

通过不同DTO的组合,又可以调整输出数据的结果,从而解决颗粒度太粗的问题

使用AutoMapper (NuGet)

AutoMapper.Extensions.Microsoft.DependencyInjection

除了包含AutoMapper所有功能以外,还包含一系列扩展工具,可以无缝衔接.NET Core 的IOC的反转控制容器

注册依赖 :

services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());//扫描profile文件 

profile文件 (配置文件,系统的映射关由profile管理)

automapper注册依赖后,会自动在项目中寻找名为

Profiles的文件夹,然后扫描这个文件夹中的所有文件,在XXXProfile的构建函数中,完成对对象的映射关系的配置

using AutoMapper;
using FakeXieCheng.Dtos;
using FakeXieCheng.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace FakeXieCheng.Profiles
{
    public class TouristRouteProfile:Profile
    {
        public TouristRouteProfile()
        {
            CreateMap()
                .ForMember(
                dest => dest.Price,
                opt => opt.MapFrom(src => src.OriginalPrice * (decimal)(src.DiscountPresent ?? 1))
                )
                .ForMember(
                dest => dest.TravelDays,
                opt => opt.MapFrom(src => src.TravelDays.ToString())
                )
                .ForMember(
                dest => dest.TripType,
                opt => opt.MapFrom(src => src.TripType.ToString())
                );

    }
}

}

Controller 中

        private ITouristRouteRespository _touristRouteRespository;
        private readonly IMapper _mapper;
        public TouristRoutesController(ITouristRouteRespository touristRouteRespository,IMapper mapper)
        {
            _touristRouteRespository = touristRouteRespository;
            _mapper = mapper;
        }

在构造函数中通过使用automapper的接口来注入服务依赖

        public IActionResult GetTouristRoute()
        {
            var touristRoutesFromRepo = _touristRouteRespository.GetTouristRoutes();
            if(touristRoutesFromRepo == null||touristRoutesFromRepo.Count()<=0)
            {
                return NotFound("没有旅游路线");
            }
            var touristRouteDto = _mapper.Map>(touristRoutesFromRepo);
            return Ok(touristRouteDto);
        }
        [HttpGet("{touristRouteId:Guid}")]
        public IActionResult GetTouristRouteById(Guid touristRouteId)
        {
            var touristRouteFromRepo = _touristRouteRespository.GetTouristRoute(touristRouteId);
            if (touristRouteFromRepo == null)
                return NotFound($"旅游路线{touristRouteId}找不到");
/*  var touristRouteDto = new TouristRouteDto()
          {
              Id = touristRouteFromRepo.Id,
              Title = touristRouteFromRepo.Title,
              Description = touristRouteFromRepo.Description,
              Price = touristRouteFromRepo.OriginalPrice * (decimal)touristRouteFromRepo.DiscountPresent,
              CreateTime = touristRouteFromRepo.CreateTime,
              UpdateTime = touristRouteFromRepo.UpdateTime,
              Features = touristRouteFromRepo.Features,
              Fees = touristRouteFromRepo.Fees,
              Notes = touristRouteFromRepo.Notes,
              Rating = touristRouteFromRepo.Rating,
              TravelDays = touristRouteFromRepo.TravelDays.ToString(),
              TripType = touristRouteFromRepo.TripType.ToString(),
              DepartureCity = touristRouteFromRepo.DepartureCity.ToString()
          };*/
        var touristRouteDto = _mapper.Map(touristRouteFromRepo);
        return Ok(touristRouteDto);//Ok表示状态码两百
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-slQ5RvLm-1644390731158)(E:/Note/typora-study-notes/NoteImage/image-20220123135109094.png)]

Automapper的嵌套映射

显示旅游路线的同时就应当显示出一系列图片,所以需要两个表的链接

public IEnumerable GetTouristRoutes()
    {
        return _context.TouristRoutes.Include(t => t.TouristRoutePictures);
    }

表示两张表Include通过外键链接

join 不通过外键手动表链接

通过 include和join 可以实现数据的立即加载(Eager Load)此外,ef还提供了另一种加载方式 延时加载(Lazy load)

单点登录JWT与用户身份验证

(1)单点登录与JWT

1.JWT的作用:

JSON Web Token:

JWT的作用是用户授权Authorization,而不是用户的身份认证Authentication

2.用户授权Authorization vs 身份认证Authentication

用户认证是指的是使用用户名,密码来验证当前用户的身份验证,也就是登录

返回的错误状态码应当是: 401 Unauthorized 未授权

用户授权是指当前用户有足够的权限访问特定的资源

返回的错误码应当是:403 forbidden 禁止访问

3.传统的Session登录 -有状态登录
  • 用户登录后,服务器会保存登录的session信息
  • Session ID 会通过cookie传递给前端
  • http请求 会附带cookie
4.单点登录JWT -无状态登录

彻底改变了用户授权与认证的过程

  • 替换cookie
  • JWT信息只需要保存在客户端
  • 无状态登录

(2)有状态登录与无状态登录

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3B3yWLhO-1644390731158)(NoteImage/image-20220129141943747.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pRs28ETE-1644390731158)(NoteImage/image-20220129142250870.png)]

后端使用私钥解密密码

Session vs JWT

Session 需要保存在服务器上,而Session ID则保存在cookie中

JWT信息只需要保存在客户端

(3)JWT原理剖析与实例解释

JWT原理

左边的JWT编码被分为三个部分:

红色部分:JWT的头部Header(页眉),具体描述了JWT当前所使用的编码算法比如图中 “alg”:“HS256”,这个算法将用于最后一个部分数字签名的验证

紫色部分:JWT的PayLoad(有效载荷),保存的就是具体的用户信息,该部分是可以自定义的,‘iat’ = issue at, 发布于,也就是当前JWT的创建时间,如果需要加上token(令牌)过期时间"exp":100000,则必须要有该字段iat

蓝色部分: Verify Signature(验证签名)也是JWT最重要的部分,这个部分等同于JWT的激光防伪标识,服务器通过这个数据签名来判断发送的token(令牌)是否有效,是否被篡改过,一旦数字签名失败,整个JWT将作废,数字签名使用非对称算法RSA加密,由此只能使用服务器私钥解密,其中your-256-bit-secret是用来输入服务器私钥以验证数字签名的

私钥位置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9IPwlSw5-1644390731159)(NoteImage/image-20220129150508555.png)]

Session,使用session实现单点登录需要保存多个session

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O7FMBsME-1644390731159)(NoteImage/image-20220129150922432.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fH53wzs6-1644390731160)(NoteImage/image-20220129150959102.png)]

JWT:JWT保存在浏览器中,只需要服务器只需要使用相同的私钥验证JWT,然后返回不同权限的资源即可

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-47l9K8Ov-1644390731160)(NoteImage/image-20220129151044364.png)]

JWT优缺点

JWT的优点:

无状态,简单,方便,完美支持分布式部署

非对称性加密RSA加密,Token安装性高

JWT的缺点:
  • 无状态,token一经发布则无法取消,也无法被服务器禁用,如果用户因为自身原因被黑客窃取,那么黑客就可以使用这个token伪装登录,而服务端对此没有任何办法,只能等待失效
  • JWT的前两部分没有加密仅仅使用base64编码而已,等于是明文传递,也就是说Token用户的信息等于是明文在传递,很容易造成用户的信息泄露

解决方法

第一个问题:

无解

第二个问题:

可以给网站加上ssl,使用https可以解决,JWT的数据可以使用https协议加密以后再进行传输

JWT安装

Microsoft.AspNetCore.Authentication.JwtBearer

     

using FakeXieCheng.Dtos;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace FakeXieCheng.Controllers
{
    [ApiController]
    [Route("auth")]
    public class AuthenticateController:ControllerBase
    {
        private readonly IConfiguration _configuration;
        public AuthenticateController(IConfiguration configuration)
        {
            _configuration = configuration;
        }
        [AllowAnonymous]//这个api允许任何人存在
        [HttpPost("login")]
        public IActionResult login([FromBody] LoginDto loginDto)
        {
            //1.验证用户名密码
            //2.创建JWT 
//header

        var signingAlgorithm = SecurityAlgorithms.HmacSha256;
        //payload
        var claims = new[]
        {
            new Claim(JwtRegisteredClaimNames.Sub,"fake_user_id")// id的专有名称 Sub
        };
        //signiture
        //需要读取appsetting配置文件,所以要把配置文件服务的依赖通过IOC容器从构建函数注入进来
        var secretByte = Encoding.UTF8.GetBytes(_configuration["Authentication:SecretKey"]);//将私钥以UTF8编码并输出字节
        var signingKey = new SymmetricSecurityKey(secretByte);//然后使用非对称算法对私钥进行加密
        var signingCredentials = new SigningCredentials(signingKey, signingAlgorithm); //最后使用HS256验证一下SigningCredentials表示用于生成数字签名的加密密钥和安全算法。非对称加密后的私钥
            var token = new JwtSecurityToken(
                issuer:_configuration["Authentication:Issuer"],
                audience: _configuration["Authentication:Audience"],
                claims,
                notBefore: DateTime.UtcNow,//发布时间
                expires:DateTime.UtcNow.AddDays(1),//有效期
                signingCredentials//数字签名

                /*
                issuer:"fakexiecheng.com",//发布token者
                audience:"fakexiecheng.com",//发布给,对于我们项目而言,应当是项目前端,
                因为我们将频繁使用issuer与audience,我们可以给该俩对象转移到配置文件中
                 */
                );//通过数据创建Jwt token
        var tokenStr = new JwtSecurityTokenHandler().WriteToken(token);//使用JwtSecurityTokenHandler来以字符串的形式输出token
        //3.return 200 ok
        return Ok(tokenStr);
    }
}
}

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "DbContext": {
    "ConnectionString": "server=localhost;Database=FakeXieChengDb;User Id=sa;Password=yourStrong(!)Password",
    // "MySQLConnectionString": "server=localhost;Database=FakeXieChengDb;uid=root;pwd=123456"
  },
  "Authentication": {
    "SecretKey": "suibianzifuchuan", //私钥
    "Issuer": "fakexiecheng.com", //发布token者   
    "Audience": "fakexiecheng.com" //发布给
  }
}

个人理解:

私钥转码后(原先string)使用RSA算法生成数字签名。数字签名和数据一起 合并为token ,而token以紧凑序列化格式将JwtSecurityToken序列化为 JWT字符串。

JwtSecurityToken一种SecurityToken旨在表示 JSON Web 令牌 (JWT),即构造成令牌

SecurityTokenHandlert旨在创建和验证 Json Web 令牌

JwtSecurityTokenHandler.WriteToken Method以紧凑序列化格式JwtSecurityToken序列化为 JWT。

JWT注册服务依赖

ConfigureServices
 services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)//注册JWT认证服务,AddAuthentication需要填入默认的身份类型
                .AddJwtBearer(options => //使用lamada配置JWT认证 ,Bearer : 承载
                {
                    var secretByte = Encoding.UTF8.GetBytes(Configuration["Authentication:SecretKey"]);
                    options.TokenValidationParameters = new TokenValidationParameters()//TokenValidationParameters :令牌验证参数
                    {
                        //第一部分,验证token的发布者
                        ValidateIssuer = true,
                        ValidIssuer = Configuration["Authentication:Issuer"],

​                    //第二部分,验证token的持有者
​                    ValidateAudience = true,
​                    ValidAudience = Configuration["Authentication:Audience"],

​                    //第三部分,验证token是否过期
​                    ValidateLifetime = true,

​                    //最后一部分,将token的私钥传入,并且进行加密
​                    IssuerSigningKey = new SymmetricSecurityKey(secretByte)

​                };
​               
​            });
Configure
      //你在哪
            app.UseRouting();
            //你是谁 
            app.UseAuthentication();
            //你有什么权限
            app.UseAuthorization();

Claim是什么?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4yd1raRA-1644390731161)(NoteImage/image-20220204120212065.png)]

  • 资源的所有权
  • 表述用户的身份,说明用户的角色,表示用户所具有的权限
  • 最小不可分割单位,使用的灵活度相当高,可以自由组合

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4p6RwMS0-1644390731161)(NoteImage/image-20220204120444894.png)]

TOKEN上包含的登机牌信息就是一个一个Claim

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3RDlOND3-1644390731162)(NoteImage/image-20220204120641181.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x2uS5rjX-1644390731162)(NoteImage/image-20220204120652114.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SvDffroH-1644390731162)(NoteImage/image-20220204151533664.png)]

身份认证的数据库链接对象IdentityDbContext

安装框架

NuGet:Microsoft.AspNetCore.Identity.EntityFrameWorkCore

修改数据库上下文继承对象
   public class AppDbContext:IdentityDbContext//DbContext

IdentityUser指的是身份认证的数据库结构,相当于UserModel,也就是用户模型,

其中定义了用户的基本信息比如Id,name,email等,这些内容的定义都是由.Net Core自动完成的,有了数据库User以后,如果还不存在Uesr 表 IdentityDbContext还可以帮我们更新数据库,自动为我们添加用户表

注册IdentityDbCoontext框架服务依赖
    services.AddIdentity()//用户数据模型,用户角色数据模型
            .AddEntityFrameworkStores();//使用AddEFstores来连接上下文关系对象 

随后更新数据库

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0O1lCYmv-1644390731163)(NoteImage/5f2ee21608c0a8d505000177.jpg)]

identity框架下的两个工具 SignlnManager $ UserManager

注入工具依赖

   
    private readonly IConfiguration _configuration;
        private readonly UserManager _userManager;
        private readonly SignInManager _signInManager;
        public AuthenticateController(
        IConfiguration configuration,
        UserManager userManager,
        SignInManager signInManager
        )
    {
        _configuration = configuration;
        _userManager = userManager;
        _signInManager = signInManager;
_signInManager.PasswordSignInAsync(String, String, Boolean, Boolean)

尝试以异步操作的形式登录指定的 userNamepassword 组合。

public virtual System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.SignInResult> PasswordSignInAsync (string userName, string password, bool isPersistent, bool lockoutOnFailure);

参数

  • userName

    String

登录所用的用户名。

  • password

    String

尝试登录时所用的密码。

  • isPersistent

    Boolean

指示在关闭浏览器后登录 cookie 是否应保持不变的标志。

  • lockoutOnFailure

    Boolean

指示登录失败时是否应锁定用户帐户的标志。

返回

  • Task<[SignInResult]>

表示包含登录尝试的异步操作的任务对象 。

IDentity指定使用JWT的Bear验证

Identity框架的多角色验证的默认中间件并不是JWT

所以我们必须在Action函数上显式的指定使用JWT的Bear验证(也可以直接在类上使用)

[Authorize(AuthenticationSchemes = "Bearer")]

//AuthenticationSchemes  认证方案
Identity数据库的表

AspNetUser 用户模型

AspNetUserClaim 保存直接与用户相关权限的,比如只有张三可以删除某某

AspNetUserLogins 处理第三方登录,比如微信登录时登录信息

AspNetUserRoles 保存用户角色,多对多

AspNetUserToken原先是记录用户session的,比如说延迟莫用户登录时常=长

Ctrl + f 全局搜索

API

继承ControllerBase和继承Controller:

继承ControllerBase:有多种可返回的状态码

继承Controller :比ControllerBase有更多功能,比如view视图,如果只写api则只使用ControllerBase

[ApiController]

在Controller运行过程中,传递各种元素行为的声明性标签,这些元素包括,像类,方法,结构,枚举,组件等等。

[Route(“api/[controller]”)]

  • 定义路由
  • 若Controller中只有一个IActionResult,则智能的映射action到路由中
  • 在ASP.NET api中,它的接口都叫做action函数

IActionResult

  • IActionResult 在不做任何说明的情况下会自动去匹配 [Route(“api/[controller]”)]的路由去处理get请求
  • 项目实战中,我们一本会加上httpget atrribute让该action去处理get请求

http atrribute

[HttpGet]

[HttpGet("{touristRouteId}")]指定传参,自己确定不会传入其他类型的参数时

[HttpGet("{touristRouteId:Guid}")]指定Guid类型,来数据验证

代码

namespace FakeXieCheng.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class TouristRoutesController : ControllerBase
    {
        private ITouristRouteRespository _touristRouteRespository;
        public TouristRoutesController(ITouristRouteRespository touristRouteRespository)
        {
            _touristRouteRespository = touristRouteRespository;
        }
        [HttpGet]
        public IActionResult GetTouristRoute()
        {
            var routes = _touristRouteRespository.GetTouristRoutes();
            return Ok(routes);
        }
        [HttpGet("{touristRouteId:Guid}")]
        public IActionResult GetTouristRouteById(Guid touristRouteId)
        {
            return Ok(_touristRouteRespository.GetTouristRoute(touristRouteId));//Ok表示状态码两百
        }

}

}

向API传入参数

使用Attribute:[FromXXX]

[FromQuery] :请求url的参数字符串

[FromBody] :请求主体数据

[FromRoute] :MVC架构下的Route路由URL的参数

[FromService] : 数据来源于已经注入的服务依赖

FromQuery和FromRoute的区别

FromQuery 参数来自地址栏:

https://api.fakexiecheng.com/search?pageNumber=1&query=埃及 其中参数与url片段使用 ?相隔开

FromRoute:url片段的一部分

https://api.fakexiecheng.com/touristRoutes/ ID

最好使用FromQuery或FromBody来指明参数

ASP.NETCore 的数据验证

  • 制定一个合理的数据检验规则
  • 检测数据
  • 提交错误信息,

制定数据验证规则

DTO vs Model

如果没有对数据做任何限制,而直接保存数据,或者让这些数据参与业务,那么这样必然会引起极其严重的安全漏洞,所以我们必须在这些数据的源头上给这些数据做规范,也就是说,我们必须在DTO中给数据做规范限制

ASP.NET Core自带的数据验证框架

ASP.NET Core自带了数据验证框架,我们可以充分利用这些方法给数据做限制,最常用的方法是使用数据注释方法

Data Annotation

属性级别数据验证

IValidatableObject

using FakeXieCheng.Models;
using FakeXieCheng.ValidationAttributes;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace FakeXieCheng.Dtos
{
 
    public class TouristRouteForCreationDto:IValidatableObject
    {
        [Required(ErrorMessage ="Title不可为空")]
        [MaxLength(100)]
        public string Title { get; set; }
        [Required]
        [MaxLength(1500)]
        public string Description { get; set; }
        public decimal Price { get; set; }//计算方式:原价*折扣
                                          // public decimal OriginalPrice { get; set; }//原价

        //   public double? DiscountPresent { get; set; }//折扣
        public DateTime CreateTime { get; set; }
        public DateTime? UpdateTime { get; set; }
        public DateTime? DepartureTime { get; set; }

        public string Features { get; set; }

        public string Fees { get; set; }


        public string Notes { get; set; }
        public double? Rating { get; set; }//评分
        public string TravelDays { get; set; }//旅行天数
        public string TripType { get; set; }//旅行类型
        public string DepartureCity { get; set; }//出发城市
        public ICollection TouristRoutePictures { get; set; }//同时映射父子资源,autoMapper在映射时会同时映射好几级的资源
    = new List();

        public IEnumerable Validate(ValidationContext validationContext)
        {
            if(Title == Description)
            {
                yield return new ValidationResult(
                    "路线名称必须与路线描述不同",//错误信息
                    new[] { "TouristRouteForCreationDto" }//错误路径
                    );
            }
        }
    }
}

    public IEnumerable Validate(ValidationContext validationContext)
    {
        if(Title == Description)
        {
            yield return new ValidationResult(
                "路线名称必须与路线描述不同",//错误信息
                new[] { "TouristRouteForCreationDto" }//错误路径
                );
        }
    }

可以实现数据的捆绑验证

类级别数据验证

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wMO9RAzY-1644390731163)(NoteImage/image-20220126020226400.png)]

using FakeXieCheng.Dtos;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;

namespace FakeXieCheng.ValidationAttributes
{
    public class TouristRouteTitleMustBeDifferentFromDescriptionAttribute:ValidationAttribute
    {
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            var touristRouteDto = (TouristRouteForCreationDto)validationContext.ObjectInstance;
            if(touristRouteDto.Title == touristRouteDto.Description)
            {
                return new ValidationResult(
                    "路线名称必须和路线描述不同",
                    new[] {"TouristRouteForCreationDto"}
                    );
            }
            return ValidationResult.Success;
        }
    }
}
输出422状态码

startup中.

 services.AddControllers(setupAction => {
                setupAction.ReturnHttpNotAcceptable = true; //配置,设置截取head头部,遇到无法识别时抛出异常
                   

​        }).AddXmlDataContractSerializerFormatters()
​        .ConfigureApiBehaviorOptions(setupAction => 
​        {
​            setupAction.InvalidModelStateResponseFactory = context =>
​            {
​                var problemDetail = new ValidationProblemDetails(context.ModelState)
​                {
​                    Type = "无所谓",
​                    Title = "数据验证失败",
​                    Status = StatusCodes.Status422UnprocessableEntity,
​                    Detail = "请看详细说明",
​                    Instance = context.HttpContext.Request.Path
​                };
​                problemDetail.Extensions.Add("traceId", context.HttpContext.TraceIdentifier);
​                return new UnprocessableEntityObjectResult(problemDetail)
​                {
​                    ContentTypes = { "application/problem+json" }
​                };
​            };
​        }

检测数据

ASP.NET Core: ModelState

ModelState 是一个键值对类型的结构,

包含当前数据模型状态,以及该模型相应的数据验证逻辑,通过调用ModelStates.IsValid提取验证结果,如果验证失败ModelState花卉提供验证失败的详细错误信息

提交错误信息
状态码:

400level 422Unprocessable Entity

请求格式正确,但是由于含有语意错误,无法响应

错误信息:ModelState会提供

Http状态码

作用:

  • 用户可以知道服务器端是正常处理了请求还是出现了什么错误

  • 一个三位数字的状态码,和一个字符串格式状态消息组成

  • 数字码便于程序进行处理,而消息字符串更方便人理解

Http状态码五大类

  • 1XX:Informational:信息状态码,表示接受的请求正在处理 -

  • 2XX:Success:成功状态码表示请求正常处理完毕 200OK,201Created,204 No Content

  • 3XX:Redirection:重定向状态码,表示需要客户端进行附加操作 301/302 Moved Permanentlty,304 Not Modified

  • 4XX:Client Error:客户端错误状态码,表示服务器无法处理请求 400 Bad Request,401 Unauthorized 403 Foridden,404 Not Found…

  • 5XX:Server Error:服务器错误状态码,表示服务器处理请求出错 500 Internal Server Error 502 Bad Gateway

Post

安全性与幂等

安全性 (取值)
  • 不会产生副作用,不会改变当前的资源状态
  • 包括那些可以被缓存,对资源无损预加载的方法eg GET,HEAD
幂等性(赋值)

同样的操作不管经过多少次调用,返回的数据或者产生的效果都是一致的 ,幂等性是数学概念,表达的是N次变换与1次变换的结果相同 eg:

a =1;幂等

a++;不幂等

HTTP方法的安全性和幂等性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K5VWoHPL-1644390731164)(NoteImage/image-20220125165140135.png)]

  [HttpGet("{touristRouteId:Guid}",Name = "GetTouristRouteById")]
        [HttpHead]
        public IActionResult GetTouristRouteById(Guid touristRouteId)
        {
            var touristRouteFromRepo = _touristRouteRespository.GetTouristRoute(touristRouteId);
            if (touristRouteFromRepo == null)
                return NotFound($"旅游路线{touristRouteId}找不到");

            var touristRouteDto = _mapper.Map(touristRouteFromRepo);
            return Ok(touristRouteDto);//Ok表示状态码两百
        }


[HttpPost]
        public IActionResult CreateTouristRoute([FromBody] TouristRouteForCreationDto touristRouteForCreationDto)
        {
            var touristRouteModel = _mapper.Map(touristRouteForCreationDto);
            _touristRouteRespository.AddTouristRoute(touristRouteModel);
            _touristRouteRespository.Save();
            var toursitRouteToReturn = _mapper.Map(touristRouteModel);
            return CreatedAtRoute(//会生成一个路由,将会告诉发送方,该如何获取这个新的资源 即简单的Hatoas(第三等级),通过头部的location信息,实现了api的自我发现
                "GetTouristRouteById",
                new { touristRouteId = toursitRouteToReturn.Id},
                 toursitRouteToReturn);//201
        }

Put

put vs patch

**put:**对资源的所有字段进行更新

**Patch:**对某个资源所选的某几个字段部分更新

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-akoZKxTZ-1644390731164)(NoteImage/image-20220126020851886.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZTxpXQfa-1644390731164)(NoteImage/image-20220126020925785.png)]

put 更新对象的全部属性,若只给予一部分字段,其余字段会被视为删去,同一个put更新请求,无论请求多少次对应的资源更新后不会再发生改变,这也是put的幂等性

而Patch仅仅更新对象的某一属性

 [HttpPut("{touristRouteId}")]
        public IActionResult UpdateTouristRoute(
            [FromRoute]Guid touristRouteId,
            [FromBody]TouristRouteForUpdateDto touristRouteForUpdateDto)
        {
            if(!_touristRouteRespository.TouristRouteExits(touristRouteId))
                {
                return NotFound("旅游路线找不到");
            }
            var touristRouteFromRepo = _touristRouteRespository.GetTouristRoute(touristRouteId);
            //1.映射dto
            //2.更新dto
            //3.映射model
            _mapper.Map(touristRouteForUpdateDto, touristRouteFromRepo);
            _touristRouteRespository.Save();/*因为在数据仓库中,我们调用的是底层的orm框架EF,
                                             * 而在EF中数据模型touristRouteFromRepo是根据上下文对象Context来追踪的,
                                             * 当我们在执行_mapper.Map方法时,数据模型中的数据其实已经被修改了,
                                             * 而这个时候,数据模型的追踪状态也就相应的发生了变化,
                                             * 模型的追踪状态是由EF上下文关系对象Context自我管理的,
                                             * 所以当我们保存数据库时,模型的追踪状态就会随着Context的保存,
                                             * 将更新后的状态写入数据库*/
            return NoContent();//204
        }

Patch

Json patch 操作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-izobKRMM-1644390731165)(NoteImage/image-20220126030325153.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ksFciR7k-1644390731165)(NoteImage/image-20220126030400383.png)]

Json Patch下载

microsoft.aspnetcore.jsonpatch

Json Patch依赖注入包

microsoft.aspnetcore.mvc.newt

配置json序列化设置,具体的配置信息就是在注册服务的时候给它创建一个 CamelCase的json对象

.AddNewtonsoftJson(setupAction =>
                {
                    setupAction.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
                })

patch请求的数据验证

TryValidateModel(touristRouteToPatch)

EF

Microsoft.EntityFrameworkCore

数据库创建工具entityframeworkCore tools

三种数据库创建方式:

database first:

数据库优先,提前创建好数据库,然后与数据库相连接

model first:

model优先,先创建好数据模型models,然后根据models自动生成数据库

core first:

不创建实体模型,而是通过databaseContext数据库上下文关系创建数据库

命令行创建:(mac不适用)

数据迁移

add-migration [name]

数据迁移的代码就是创建数据库等操作的各种代码

根据数据迁移创建数据库

update-database

dotnet 命令

数据迁移

dotnet ef migrations add initialMigraltion

创建数据库:

dotnet ef database update

Windows 使用dotnet cli注意事项

全局安装ef工具 :

donet tool install --global donet-ef

EFCore CodeFirst 注解特性

[Table]

Table特性可以应用于一个领域类上面,用来在数据库中生成相应名称的数据表。它重写了EF 6和 EF Code 中默认的约定,根据默认约定,EF 6和EF Core创建的表的名称是实体名称+s(或者es),并且创建的数据表的列名称和实体属性名称一样。

  • Table Attribute: [Table(string name, Properties:[Schema = string])

name:数据表的名称

Schema:数据库的模式名称【可选的】

如下Student类:数据库生成的表名为Sc.StudentInfo

[Table("StudentInfo",Schema="Sc")]
public class Student
{
    public int StudentID { get; set; }

public string Name { get; set; }

}

[Key]

标识字段为主键。如果是多个字段都标识了[key],则默认生成联合主键。

[Column]

Column特性,可以应用于实体的一个或者多个属性上面,用来配置数据库中数据表中列的列名、列的数据类型以及列的先后顺序。Column特性重写了默认的约定。按照EF 6和EF Core中的默认约定,将会创建和属性相同的列名称,并且数据表,列的顺序和实体中属性的顺序一致。

  • Column Attribute: [Column (string name, Properties:[Order = int],[TypeName = string])

name:表的数据列的名称

Order:列的顺序,从索引0开始【可选的】

TypeName:列的类型名称【可选的】

如下AddTime列:数据库生成的字段将是“CreateTime”而不是实体类中的AddTime,类型是“Date”而不是默认的DateTime。

decimal(18,0);18是定点精度,0是小数位

[Table(“StudentInfo”,Schema=“Sc”)]
public class Student
{
public int StudentID { get; set; }

public string Name { get; set; }

[Column("CreateTime",TypeName="Date")]
public DateTime AddTime { get; set; }

}

[Required]

Required特性可以应用于一个实体的一个或多个属性上面。EF将会为标识Required特性的属性,在数据库表的列中生成不为空(not null)的列。

[StringLength] 与 [MaxLength]

StringLength特性可以应用于实体的string类型的属性上,它指定了属性的所允许的最大字符长度,然后对应在数据库中就生成相应长度的数据列(在SQL Server数据库中是,nvarchar类型)。

MaxLength特性指定了属性的值所允许的最大值,然后在数据库中就生成相应列的最大值。MaxLength特性可以应用于实体的String类型的属性和byte[]数组类型的属性上。

区别:[MaxLength] 适用的类型比[StringLength]多。

[NotMapped]

当我们不想实体类中的某个或者某些属性,不要映射成数据库中的列的时候。可以使用NotMapped特性,标识NotMapped特性在属性上面就行了。默认情况下,EF为实体的每个属性映射数据列。【必须包含get;和set;】。NotMapped特性重写了这个约定。

需要注意的是:EF不会为没有get;和set;的属性,创建列。

[Index]

用来在特定的列上面创建索引,默认情况下,索引的名称是IX_{属性的名称}。

  • Column Attribute: [Index(string name, Properties:[IsClustered = bool],[IsUnique= bool])

name:索引名称

IsClustered : 是否是建聚合索引

IsUnique:是否是唯一索引

如下Name字段,数据库创建的索引名称“Idx_Student_Name”,为聚合索引,唯一索引。

  [Table("StudentInfo",Schema="Sc")]

public class Student
{
    public int StudentID { get; set; }

[Index("Idx_Student_Name",IsClustered=true,IsUnique=true)]
public string Name { get; set; }

[Column("CreateTime",TypeName="Date")]
public DateTime AddTime { get; set; }

}

[ForeignKey]

会自动把类名+属性作为外键关联

在EF 6和EF Core中,数据注解中的ForeignKey特性,是用来在两个实体间配置外键关系。

根据默认的约定,当属性的名称与相关实体的主键属性匹配时,EF将该属性作为外键属性。

ForeignKey Signature: [ForeignKey(name string)]
name:相关联的导航属性的名称或者相关联的外键属性名称

一般用法:

public class Student
{
    public int StudentID { get; set; }
    public string StudentName { get; set; }
        

[ForeignKey("TeacherId")]
public Teacher teacher{ get; set; }

}

public class Teacher
{
    public int TeacherId { get; set; }
    public string TeacherName { get; set; }
}

Linq

C# Linq First 和 FirstOrDefault的区别

Enumerable.First 方法

返回序列中的第一个元素。

Enumerable.FirstOrDefault 方法

返回序列中的第一个元素;如果序列中不包含任何元素,则返回默认值。

注: 在使用时,如果返回的是对象, 建议使用FirstOrDefault , 并对返回的对象进行判空操作

  1. FirstOrDefault

如果查询的数据不存在, 则返回 null

  1. First

如果查询的数据不存在, 则System.InvalidOperationException异常

image-20220118141916423

IQueryable 延迟执行

C# 中 Linq to SQL 的返回类型 ,可以叠加处理linq语句,最后统一访问数据库,而这个叠加过程就叫做延迟执行

 IQueryable result = _context.TouristRoutes.Include(t => t.TouristRoutePictures);

简单来说,这一步仅仅是生成了sql语句,并没有执行数据库查询的操作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XZ4Yv0KY-1644390731166)(E:/Note/typora-study-notes/NoteImage/image-20220124135831801.png)]

延迟执行的目的:

为后续动态表达提供可能

减少数据库的执行次数

IQueryable本质

是一棵二叉树,也可以视为语法树般的结构

Entity Frameword的最佳工具:Linq

  • Linq to sql
  • Linq to object
  • Linq to entity

TouristRoutesController :

         [HttpGet]
        [HttpHead] //api/touristRoutes?keyword=传入的参数   如果url中的参数命名与action中的不一致[FromQuery(Name="key")]
             public IActionResult GetTouristRoute([FromQuery]string keyword)
        {
            var touristRoutesFromRepo = _touristRouteRespository.GetTouristRoutes(keyword);
            if(touristRoutesFromRepo == null||touristRoutesFromRepo.Count()<=0)
            {
                return NotFound("没有旅游路线");
            }
            var touristRouteDto = _mapper.Map>(touristRoutesFromRepo);
            return Ok(touristRouteDto);
        }

TouristRouteRepository :

public IEnumerable GetTouristRoutes(string keyword)//keyword用于关键词搜索
    {
        IQueryable result = _context.TouristRoutes.Include(t => t.TouristRoutePictures);
        if (!string.IsNullOrWhiteSpace(keyword))    //keyword不等于空和不等于空格时
        {
            keyword = keyword.Trim();
            result.Where(t => t.Title.Contains(keyword));
        }
        return result.ToList();

        // return _context.TouristRoutes.Include(t => t.TouristRoutePictures);
    }

ToList与FirstOrDefult

ToList是Iqueryable的内建函数,通过调用Tolist,Iqueryable就会马上去执行数据库的访问,随后便会以包含了Tourist类型的数据的List返回;
FirstOrDefult返回第一条查询到的数据,也就是说处理的是单条数据,即返回一条TouristRoute类型的数据

函数式写法:

  switch (operatorType)
                {
                    case "largeThan":
                        result = result.Where(t => t.Rating >= ratingValue); break;
                    case "lessThan":
                        result = result.Where(t => t.Rating <= ratingValue); break;
                    case "equalTo":
                    default:
                        result = result.Where(t => t.Rating == ratingValue); break;

​            }
            result = operatorType switch
            {
                "largeThan" => result.Where(t => t.Rating >= ratingValue),
                "lessThan" => result.Where(t => t.Rating <= ratingValue),
                _ => result.Where(t => t.Rating == ratingValue),
            };

Docker

Docker是什么?

Docker是一个虚拟环境容器,可以将你的开发环境、代码、配置文件等一并打包到这个容器中,并发布和应用到任意平台中。比如,你在本地用Python开发网站后台,开发测试完成后,就可以将Python3及其依赖包、Flask及其各种插件、Mysql、Nginx等打包到一个容器中,然后部署到任意你想部署到的环境。

Docker的三个概念

  1. 镜像(Image):类似于虚拟机中的镜像,是一个包含有文件系统的面向Docker引擎的只读模板。任何应用程序运行都需要环境,而镜像就是用来提供这种运行环境的。例如一个Ubuntu镜像就是一个包含Ubuntu操作系统环境的模板,同理在该镜像上装上Apache软件,就可以称为Apache镜像。
  2. 容器(Container):类似于一个轻量级的沙盒,可以将其看作一个极简的Linux系统环境(包括root权限、进程空间、用户空间和网络空间等),以及运行在其中的应用程序。Docker引擎利用容器来运行、隔离各个应用。容器是镜像创建的应用实例,可以创建、启动、停止、删除容器,各个容器之间是是相互隔离的,互不影响。注意:镜像本身是只读的,容器从镜像启动时,Docker在镜像的上层创建一个可写层,镜像本身不变。
  3. 仓库(Repository):类似于代码仓库,这里是镜像仓库,是Docker用来集中存放镜像文件的地方。注意与注册服务器(Registry)的区别:注册服务器是存放仓库的地方,一般会有多个仓库;而仓库是存放镜像的地方,一般每个仓库存放一类镜像,每个镜像利用tag进行区分,比如Ubuntu仓库存放有多个版本(12.04、14.04等)的Ubuntu镜像。

下载镜像

sql:

docker pull mcr.microsoft.com/mssql/server:2019-CU13-ubuntu-20.04 

Microsoft SQL Server by Microsoft | Docker Hub

mysql:

vs使用扩展包Pomelo.EntityFrameworkCore.Mysql

docker pull mysql:latest

Oracle

Oracle.EntityFrameworkCore

https://www.nuget.org/packages/Oracle.EntityFrameworkCore/

针对安装了Docker for Windows的用户,您可以参考以下配置步骤:

在系统右下角托盘图标内右键菜单选择 Settings,打开配置窗口后左侧导航菜单选择 Docker Daemon。编辑窗口内的JSON串,填写下方加速器地址:

{
  "registry-mirrors": ["https://r7h0meva.mirror.aliyuncs.com"]
}

列出镜像列表

我们可以使用 docker images 来列出本地主机上的镜像。

runoob@runoob:~$ docker images           
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu              14.04               90d5884b1ee0        5 days ago          188 MB
php                 5.6                 f40e9e0f10c8        9 days ago          444.8 MB
nginx               latest              6f8d099c3adc        12 days ago         182.7 MB
mysql               5.6                 f2e8d6c772c0        3 weeks ago         324.6 MB
httpd               latest              02ef73cf1bc0        3 weeks ago         194.4 MB
ubuntu              15.10               4e3b13c8a266        4 weeks ago         136.3 MB
hello-world         latest              690ed74de00f        6 months ago        960 B
training/webapp     latest              6fae60ef3446        11 months ago       348.8 MB

各个选项说明:

  • **REPOSITORY:**表示镜像的仓库源
  • **TAG:**镜像的标签
  • **IMAGE ID:**镜像ID
  • **CREATED:**镜像创建时间
  • **SIZE:**镜像大小

配置并启用sql镜像

sql:

mac中单引号

docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=yourStrong(!)Password' -p 1433:1433 -d microsoft/mssql-server-linux

windows中双引号

docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=yourStrong(!)Password" -p 1433:1433 -d mcr.microsoft.com/mssql/server:2019-CU13-ubuntu-20.04 

mysql

docker run -itd --name mysql-test -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql

密码要复杂

正在运行的docker容器

docker ps

查看历史容器

docker ps -a

查看日志

docker logs  【id 】

Docker 停止容器->删除容器->删除镜像
应用场景:需要彻底删除一个正在运行的镜像

docker ps // 查看所有正在运行容器
docker stop containerId // containerId 是容器的ID
docker ps -a // 查看所有容器 $ docker ps -a -q // 查看所有容器ID 
docker stop (docker ps -a -q) // stop停止所有容器
docker rm (docker ps -a -q) // remove删除所有容器
docker images // 查看镜像列表
docker rmi ImageId //删除镜像 ImageId 是镜像的Id

思考

1. Kestrel服务器 vs IIS服务器?

Kestrel可以跨平台,IIS只能在Windows上使用。

2. 什么是中间件?什么是请求通道?

请求通道是Http请求从接收到处理完成,经过的一系列处理环节。
中间件就是一个个环节上的工具,可以根据这一块的框架自定义中间件。

3.schema是数据库中的什么?

不同的教材对schema的定义也不太一样,甚至是不同的数据库对Schema的使用也不完全一样,比如mysql中schema就相当于DB,sql server中schema却描述的是每张表的结构。一般来说,我们说到schema指的是数据库中表的结构、主键、外键、视图、各种关系的总和。

4.DDD(领域驱动设计),你必须知道的贫血模型和充血模型

贫血模型和充血模型

贫血模型

  • 定义对象的简单的属性值,没有业务逻辑上的方法(个人理解)没有找到官方解释

充血模型

  • 充血模型也就是我们在定义属性的同时也会定义方法,我们的属性是可以通过某些方式直接得到属性值,那我们也就可以在对象中嵌入方法直接创建出一个具有属性值的对象。也就是说这个对象不再需要我们在进行进一步的操作,这也就复合了OOP的三大特性之一的封装(个人理解)

5.HTTP状态码 201 和 204 有什么区别?

201:表示服务器执行成功,并且创建了新的资源;
204:服务器成功执行请求,但是没有返回信息。

6.如何正确理解HTTP状态码?

HTTP 状态代码是服务器向客户端告知服务器是否已成功完成客户端发送的资源操作请求。

7.什么是内容协商?

服务端和客户端通过协商决定数据传输时使用的数据格式和数据语言。

8.Model与DTO有什么区别?

DTO封装的数据面向表现层,Model封装的数据面向业务逻辑层

9.AutoMapper是什么?

一个基于约定的面向对象的映射器,它将一个Model对象 转成一个DTO对象,

10.什么是HEAD请求?

从服务器获取资源,但服务器不会返回请求的实体数据,只会传回响应头,也就是资源的“元信息”。主要用来检查一个资源是否存在,可以减少网络请求资源。

11.什么是jwt?

JWT是JSON Web Token 作用是用户授权而不是用户身份认证。

12.jwt与session有什么区别?

session:用户使用用户名和密码来向服务端发送登录请求,服务端会判断用户名和密码是否正确,如果正确服务端会创建一个包含用户、角色和权限的session,并保存这个session,同时把sessionID以cookie的形式发送给前端,告知客户端验证成功,用户访问资源时像服务段发送包含sessionid的cookie的请求,服务器获取到cookie后去检查是否包含sessionid,如果包含就通过sessionid来确定用户身份和权限,如果sessionid与session相符,并且可以提供相应的权限,服务端就向客户端返回相应的资源数据。
JWT:用户使用用户名和密码登录,如果登录成功,服务器会返回加密文档,客户端收到jwt文档后保存下来,服务端不保存jwt文档。用户访问资源时将jwt放在请求头中发送给服务端,服务端收到jwt后,使用私钥解密jwt文档,如果解密成功并且数据有效,并且jwt描述的用户权限允许用户访问请求的资源,服务端就将资源数据返回给客户端。
简单说区别就是:session保存在服务器上,sessionid保存在前端cookie中;jwt信息值保存在客户端;jwt是无状态登录,完美支持分布式部署。

13.authorization与authentication有什么区别?

authorization:指当前用户是否有足够的权限访问特定的资源;
authentication:指使用用户名和密码来校验当前用户的身份,也就是登录,登陆失败返回错误码401,未授权返回403。

重点详解

AppDbcontext:

介于代码和数据库之间的连接器,在代码和数据库之间引导数据的流动在DbContext中有一个函数OnModelCreating(),我们需要重写一下这个内置函数来添加初始化数据

CreatedAtRoute

CreatedAtRoute(//会生成一个路由,将会告诉发送方,该如何获取这个新的资源 即简单的Hatoas(第三等级),通过头部的location信息,实现了api的自我发现
                "GetTouristRouteById",//获得对象的函数名
                new { touristRouteId = toursitRouteToReturn.Id},//该函数所需参数
                 toursitRouteToReturn);//201面向视图层的Dto对象

你可能感兴趣的:(笔记,asp.net,服务器,后端)