项目所有的服务依赖及框架都会安装在这个文件夹
其中框架文件夹中包含两个框架:
是项目的基础框架,它包含了对代码编译,运行,部署的处理。
基于基础框架引入的应用层框架,它包含了一系列应用层的服务,认证服务,授权服务,诊断服务,http的请求处理服务,以及文件访问,日志记录,依赖注入等等。
用于项目构建和依赖解析的工具,可以完成项目依赖的自动安装,解决依赖冲突。
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
Logging:日志设置
LogLevel:日志级别;
Default:日志默认信息;
…
AllowedHosts:对托管服务器的设置
appsetting.json文件能保存的设置远远对于上面我们所看到的内容
例如数据库链接设置,第三方信息,账号密码等等
IIS服务器与.NET Core内建的KESTREL服务器
{
"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"
}
}
}
}
在其源码中注释可以了解到CreateHostBuilder所有的配置过程:
查看程序的运行环境
通过运行环境启用相应的配置文件appsettings.json
加载程序集assembly运行系统的所有核心代码
设置环境变量,日志,以及系统的反转控制IOC容器
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup();
});
CreateHostBuilder 方法中Host为虚拟网站托管主机
默认生成
Startup:
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:在运行时被调用,ASP.NET Core 提供了内置的IOC容器
检查&处理http请求
交由中间件MIddleware处理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P17xFXBp-1644390731150)(E:/Note/image-20220109211554168.png)]
如果当前环境为Development则使用开发者异常界面
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
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路由映射中间件
请求通道由IApplicationBuilder创建
IWebHostEnvironment虚拟主机的环境变量:
开发过程中,往往会分成:
开发环境 Development
集成环境 Intergration
测试环境 Testing
预发布环境 Staging
生成环境 production[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SaA8hDgA-1644390731152)(E:/Note/2.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NEJB4FDI-1644390731152)(E:/Note/1-16418041944201.jpg)]
每个中间件都可以截获,修改,传递请求对象,输出响应对象,例如代码中
//新中间件短路直接响应
/* app.Run(async (context) => {
await context.Response.WriteAsync("HI");
});*/
在特定情况下,某些中间件可以做短路处理,直接向前端输出响应对象
所有中间件共用一个请求通道
不同中间件之前可以通过嵌套来获得更强大的处理能力
每一中间件截获的请求对象都来自于上一中间件的响应对象,所以中间件是有一定顺序的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DIwP0sFv-1644390731153)(E:/Note/d1b8b6fe52e6ba23694f905a3ae5448.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jyR7J8Kp-1644390731153)(E:/Note/a67f3344679a35d5396973d397e9ead.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qvXONIk3-1644390731153)(E:/Note/fc77d646e9e0256e44b72d983c0c697.jpg)]
Url被分段解析为Controller和action两个部分
例如:{域名}/{controller}/{action}
使用路由表(routing table)来设置路由规则,使用中间件:
例如:
routes.MapRoute(
name:"default",
templete:"{controller=Home}/{action = index}"
)
3.使用默认路由:
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
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();
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IE2bVAIQ-1644390731154)(E:/Note/8b536afafa3a19e8ec83fc76f5253da.jpg)]
url打开主页,然后请求信息就会通过请求通道传递到主页对应的控制器HomeController,随后,HomeController开始响应,HomeController会去寻找对应的数据模型,随后数据模型开始工作,它会去对应的数据库获得HomeController希望得到的原始数据,然后进行业务操作,将原始数据封装一下,比如封装物品的名称,价格,图片等等,随后将封装好的数据交回给HomeController控制器,当HomeController取得数据后,就会将这些数据传递给视图View,而视图拿到数据后会把数据添加进动态Html中,最后通过http的形式把html文件发送到用户的浏览器中,最后,用户便能看到主页信息,随后用户点击继续查看,从此,形成闭环。
视图层和业务层可以分离:
可以只更改视图层代码而不需要重新编写模型和控制器的代码,比如,改写jsp,html,css或js的代码,并不需要重启服务器。
同样,一个应用的业务流程或业务规则发生改变,只需要改变mvc的模型,因为模型与控制器,视图相分离,所以很容易改变业务程序的数据层和业务规则。
MVC的所有组件都具有高可复用性,随着技术的不断进步,需要越来越多的方式访问应用程序,MVC模式允许各种各样不同样式的视图来访问同一服务端的代码,多个视图可共用一个模型,比如,用户可以通过web应用,也可以通过手机app来订购同样的产品,虽然订购的方式不一样,但是处理订购的业务是一样的,由于模型返回的数据并没有发生改变,所以同样的功能可以被不同界面使用
分离视图和业务逻辑可以使得web应用易于维护和修改,比如,如果向更改业务逻辑,只需要更改业务逻辑,如果想更改视图,只需要去更改视图,如果想增加功能,只需要增加功能。便于维护和后期代码更新。
大家都是依照自己的经验来解释和使用MVC,而MVC内部原理比较复杂,组合了多种设计模式,完全理解MVC较为困难,对新手不友好
MVC并不适合小型,甚至中型项目,对于简单的界面,严格遵循MVC反而增加结构的复杂性,产生过多的数据操作导致运行效率底下。
依据操作模型的接口不同,视图可能需要多次调用才能获得足够的数据显示,对于未变化的数据,频繁的访问模型,也可能造成操作性能下降。
表示用户界面
业务逻辑层,处理核心业务以及数据封装
表示数据访问
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ph8NP4mH-1644390731155)(E:/Note/1-16418293170482.jpg)]
首先,需要一个数据库,根据数据库,创建DAL来获取和映射数据,得到元组数据后,传递给BLL业务逻辑层进行数据验证,数据转换以及对象封装,最后,封装好的对象传递给UI层显示给用户
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NFMIqZQN-1644390731155)(E:/Note/1-16418944085013.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RfMFlZ4Q-1644390731156)(E:/Note/1-16418946167484.jpg)]
模型,也就是一个类,一个class,这个类,包含各种各样的数据属性,这些数据属性可以映射给相应的数据库,而我们可以通过模型的数据映射,把数据库中相对独立的数据串联起来。
系统的核心业务应当被分离到不同的模型中单独处理,以便系统的扩展
使数据和视图直接绑定,甚至可以在视图上做数据的验证
数据库
使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");
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)
{
}
}
}
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" };
}
}
}
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" };
}
}
}
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"])
)
REpresentational State Transfer 表征性状态转移
REST VS RESTFUL
REST 名词
RESTFUL 形容词
无状态:一次调用就能返回结果,不存在打开链接访问数据库然后关闭连接这种依赖于上次调用的这种情况,也就是说持久性的链接,有状态的链接,不属于RESTFUL的范畴
**面向"资源"*无论是数据还是服务,在RESTFUL这里都是资源,只存在名词不存在动词
**使用http的动词:**使用http的动词表示API的操作:
eg:HTTP GET api/v1/touristRoute
HATOAS 超媒体即应用状态引擎:
好用:面对对象(资源),如增删改查
不好用:如登录:面向过程,如登录
Client-Server
前后端分离
无状态
请求独立
分层系统
代码分层:表示层,业务逻辑层,数据访问层
统一接口
前后端使用接口的格式必须一致,数据统一
API自我发现
好比翻页,点击下一页,不能由我们去改变页数的url而应该由服务其自动给出下一页的url
可缓存
服务器应该给客户端提供一个可缓存的方法,使其可以缓存服务器的响应内容,通过在响应头部中输出缓存相关的信息,我们可以有效的防止客户端频繁的与服务器交互,同时也可以避免客户端出现脏读的错误操作
按需代码
在特殊情况中,服务器可以临时给前端发送代码,让前端去按需进行,去实现服务器的某些特殊的功能,比如javascript,php的代码,让前端去执行,总之,安装业务需求即可
绝大部分后端服务不会100%满足6个约束,
restful只是一种风格,不要本末倒置,能实现需求才是王道
GitHub https://developer.github.com/v3/
豆瓣API https://developers.douban.com/wiki/?title=api_v2
内容协商,允许客户端和服务器通过协商来决定相互之间的数据传输格式,语言等内容
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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头部,遇到无法识别时抛出异常
});
services.AddControllers(setupAction => {
setupAction.ReturnHttpNotAcceptable = true; //配置,设置截取head头部,遇到无法识别时抛出异常 }).AddXmlDataContractSerializerFormatters(); //配置,允许服务器处理xml
HTTP HEAD
HEAD与GET类似,但没有响应主体,即api没有返回值,Head请求可以用于相关资源的头部信息,在支持缓存的系统中,这些信息可以用来检测获得的信息是否有效,或者说可以检测出信息最近是否有被修改过,同时HEAD请求可以用于检测资源是否存在,如果存在则返回正常,否则返回404
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JP6erMyp-1644390731157)(E:/Note/typora-study-notes/NoteImage/image-20220123105601348.png)]
Model:面向业务
DTO则是面向界面,面向UI的,按照界面的需求给予数据
直接向前端返回数据模型。会暴露系统的业务核心
使用DTO的时候可以屏蔽我们不需要暴露的核心业务
颗粒度太粗,也就是输出数据无法精细调整
通过不同DTO的组合,又可以调整输出数据的结果,从而解决颗粒度太粗的问题
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)]
显示旅游路线的同时就应当显示出一系列图片,所以需要两个表的链接
public IEnumerable GetTouristRoutes()
{
return _context.TouristRoutes.Include(t => t.TouristRoutePictures);
}
表示两张表Include通过外键链接
join 不通过外键手动表链接
通过 include和join 可以实现数据的立即加载(Eager Load)此外,ef还提供了另一种加载方式 延时加载(Lazy load)
JSON Web Token:
JWT的作用是用户授权Authorization,而不是用户的身份认证Authentication
用户认证是指的是使用用户名,密码来验证当前用户的身份验证,也就是登录
返回的错误状态码应当是: 401 Unauthorized 未授权
用户授权是指当前用户有足够的权限访问特定的资源
返回的错误码应当是:403 forbidden 禁止访问
彻底改变了用户授权与认证的过程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3B3yWLhO-1644390731158)(NoteImage/image-20220129141943747.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pRs28ETE-1644390731158)(NoteImage/image-20220129142250870.png)]
后端使用私钥解密密码
Session 需要保存在服务器上,而Session ID则保存在cookie中
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)]
无状态,简单,方便,完美支持分布式部署
非对称性加密RSA加密,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。
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)
};
});
//你在哪
app.UseRouting();
//你是谁
app.UseAuthentication();
//你有什么权限
app.UseAuthorization();
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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)]
NuGet:Microsoft.AspNetCore.Identity.EntityFrameWorkCore
public class AppDbContext:IdentityDbContext//DbContext
IdentityUser指的是身份认证的数据库结构,相当于UserModel,也就是用户模型,
其中定义了用户的基本信息比如Id,name,email等,这些内容的定义都是由.Net Core自动完成的,有了数据库User以后,如果还不存在Uesr 表 IdentityDbContext还可以帮我们更新数据库,自动为我们添加用户表
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;
尝试以异步操作的形式登录指定的 userName
和 password
组合。
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
指示登录失败时是否应锁定用户帐户的标志。
返回
表示包含登录尝试的异步操作的任务对象 。
Identity框架的多角色验证的默认中间件并不是JWT
所以我们必须在Action函数上显式的指定使用JWT的Bear验证(也可以直接在类上使用)
[Authorize(AuthenticationSchemes = "Bearer")]
//AuthenticationSchemes 认证方案
AspNetUser 用户模型
AspNetUserClaim 保存直接与用户相关权限的,比如只有张三可以删除某某
AspNetUserLogins 处理第三方登录,比如微信登录时登录信息
AspNetUserRoles 保存用户角色,多对多
AspNetUserToken原先是记录用户session的,比如说延迟莫用户登录时常=长
Ctrl + f 全局搜索
继承ControllerBase:有多种可返回的状态码
继承Controller :比ControllerBase有更多功能,比如view视图,如果只写api则只使用ControllerBase
在Controller运行过程中,传递各种元素行为的声明性标签,这些元素包括,像类,方法,结构,枚举,组件等等。
[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表示状态码两百
}
}
}
使用Attribute:[FromXXX]
[FromQuery] :请求url的参数字符串
[FromBody] :请求主体数据
[FromRoute] :MVC架构下的Route路由URL的参数
[FromService] : 数据来源于已经注入的服务依赖
FromQuery 参数来自地址栏:
https://api.fakexiecheng.com/search?pageNumber=1&query=埃及 其中参数与url片段使用 ?相隔开
FromRoute:url片段的一部分
https://api.fakexiecheng.com/touristRoutes/ ID
最好使用FromQuery或FromBody来指明参数
如果没有对数据做任何限制,而直接保存数据,或者让这些数据参与业务,那么这样必然会引起极其严重的安全漏洞,所以我们必须在这些数据的源头上给这些数据做规范,也就是说,我们必须在DTO中给数据做规范限制
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;
}
}
}
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" }
};
};
}
ModelState 是一个键值对类型的结构,
包含当前数据模型状态,以及该模型相应的数据验证逻辑,通过调用ModelStates.IsValid提取验证结果,如果验证失败ModelState花卉提供验证失败的详细错误信息
400level 422Unprocessable Entity
请求格式正确,但是由于含有语意错误,无法响应
用户可以知道服务器端是正常处理了请求还是出现了什么错误
一个三位数字的状态码,和一个字符串格式状态消息组成
数字码便于程序进行处理,而消息字符串更方便人理解
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
同样的操作不管经过多少次调用,返回的数据或者产生的效果都是一致的 ,幂等性是数学概念,表达的是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:**对资源的所有字段进行更新
**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
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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)
Microsoft.EntityFrameworkCore
数据库创建工具entityframeworkCore tools
database first:
数据库优先,提前创建好数据库,然后与数据库相连接
model first:
model优先,先创建好数据模型models,然后根据models自动生成数据库
core first:
不创建实体模型,而是通过databaseContext数据库上下文关系创建数据库
数据迁移
add-migration [name]
数据迁移的代码就是创建数据库等操作的各种代码
根据数据迁移创建数据库
update-database
数据迁移
dotnet ef migrations add initialMigraltion
创建数据库:
dotnet ef database update
全局安装ef工具 :
donet tool install --global donet-ef
Table特性可以应用于一个领域类上面,用来在数据库中生成相应名称的数据表。它重写了EF 6和 EF Code 中默认的约定,根据默认约定,EF 6和EF Core创建的表的名称是实体名称+s(或者es),并且创建的数据表的列名称和实体属性名称一样。
name:数据表的名称
Schema:数据库的模式名称【可选的】
如下Student类:数据库生成的表名为Sc.StudentInfo
[Table("StudentInfo",Schema="Sc")]
public class Student
{
public int StudentID { get; set; }
public string Name { get; set; }
}
标识字段为主键。如果是多个字段都标识了[key],则默认生成联合主键。
Column特性,可以应用于实体的一个或者多个属性上面,用来配置数据库中数据表中列的列名、列的数据类型以及列的先后顺序。Column特性重写了默认的约定。按照EF 6和EF Core中的默认约定,将会创建和属性相同的列名称,并且数据表,列的顺序和实体中属性的顺序一致。
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特性可以应用于一个实体的一个或多个属性上面。EF将会为标识Required特性的属性,在数据库表的列中生成不为空(not null)的列。
StringLength特性可以应用于实体的string类型的属性上,它指定了属性的所允许的最大字符长度,然后对应在数据库中就生成相应长度的数据列(在SQL Server数据库中是,nvarchar类型)。
MaxLength特性指定了属性的值所允许的最大值,然后在数据库中就生成相应列的最大值。MaxLength特性可以应用于实体的String类型的属性和byte[]数组类型的属性上。
区别:[MaxLength] 适用的类型比[StringLength]多。
当我们不想实体类中的某个或者某些属性,不要映射成数据库中的列的时候。可以使用NotMapped特性,标识NotMapped特性在属性上面就行了。默认情况下,EF为实体的每个属性映射数据列。【必须包含get;和set;】。NotMapped特性重写了这个约定。
需要注意的是:EF不会为没有get;和set;的属性,创建列。
用来在特定的列上面创建索引,默认情况下,索引的名称是IX_{属性的名称}。
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; }
}
会自动把类名+属性作为外键关联
在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; }
}
Enumerable.First 方法
返回序列中的第一个元素。
Enumerable.FirstOrDefault 方法
返回序列中的第一个元素;如果序列中不包含任何元素,则返回默认值。
注: 在使用时,如果返回的是对象, 建议使用FirstOrDefault , 并对返回的对象进行判空操作
如果查询的数据不存在, 则返回 null
如果查询的数据不存在, 则抛System.InvalidOperationException异常
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本质
是一棵二叉树,也可以视为语法树般的结构
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是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是一个虚拟环境容器,可以将你的开发环境、代码、配置文件等一并打包到这个容器中,并发布和应用到任意平台中。比如,你在本地用Python开发网站后台,开发测试完成后,就可以将Python3及其依赖包、Flask及其各种插件、Mysql、Nginx等打包到一个容器中,然后部署到任意你想部署到的环境。
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
各个选项说明:
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
Kestrel可以跨平台,IIS只能在Windows上使用。
请求通道是Http请求从接收到处理完成,经过的一系列处理环节。
中间件就是一个个环节上的工具,可以根据这一块的框架自定义中间件。
不同的教材对schema的定义也不太一样,甚至是不同的数据库对Schema的使用也不完全一样,比如mysql中schema就相当于DB,sql server中schema却描述的是每张表的结构。一般来说,我们说到schema指的是数据库中表的结构、主键、外键、视图、各种关系的总和。
贫血模型:
充血模型
201:表示服务器执行成功,并且创建了新的资源;
204:服务器成功执行请求,但是没有返回信息。
HTTP 状态代码是服务器向客户端告知服务器是否已成功完成客户端发送的资源操作请求。
服务端和客户端通过协商决定数据传输时使用的数据格式和数据语言。
DTO封装的数据面向表现层,Model封装的数据面向业务逻辑层
一个基于约定的面向对象的映射器,它将一个Model对象 转成一个DTO对象,
从服务器获取资源,但服务器不会返回请求的实体数据,只会传回响应头,也就是资源的“元信息”。主要用来检查一个资源是否存在,可以减少网络请求资源。
JWT是JSON Web Token 作用是用户授权而不是用户身份认证。
session:用户使用用户名和密码来向服务端发送登录请求,服务端会判断用户名和密码是否正确,如果正确服务端会创建一个包含用户、角色和权限的session,并保存这个session,同时把sessionID以cookie的形式发送给前端,告知客户端验证成功,用户访问资源时像服务段发送包含sessionid的cookie的请求,服务器获取到cookie后去检查是否包含sessionid,如果包含就通过sessionid来确定用户身份和权限,如果sessionid与session相符,并且可以提供相应的权限,服务端就向客户端返回相应的资源数据。
JWT:用户使用用户名和密码登录,如果登录成功,服务器会返回加密文档,客户端收到jwt文档后保存下来,服务端不保存jwt文档。用户访问资源时将jwt放在请求头中发送给服务端,服务端收到jwt后,使用私钥解密jwt文档,如果解密成功并且数据有效,并且jwt描述的用户权限允许用户访问请求的资源,服务端就将资源数据返回给客户端。
简单说区别就是:session保存在服务器上,sessionid保存在前端cookie中;jwt信息值保存在客户端;jwt是无状态登录,完美支持分布式部署。
authorization:指当前用户是否有足够的权限访问特定的资源;
authentication:指使用用户名和密码来校验当前用户的身份,也就是登录,登陆失败返回错误码401,未授权返回403。
介于代码和数据库之间的连接器,在代码和数据库之间引导数据的流动在DbContext中有一个函数OnModelCreating(),我们需要重写一下这个内置函数来添加初始化数据
CreatedAtRoute(//会生成一个路由,将会告诉发送方,该如何获取这个新的资源 即简单的Hatoas(第三等级),通过头部的location信息,实现了api的自我发现
"GetTouristRouteById",//获得对象的函数名
new { touristRouteId = toursitRouteToReturn.Id},//该函数所需参数
toursitRouteToReturn);//201面向视图层的Dto对象