Program.cs
启用OpenAPI支持:(Swagger支持)
顶级语句:
使用控制器:miniAPI
HTTP与HTTPS的区别
1.HTTPS协议需要到CA申请证书,一般免费的证书比较少,因而需要一定费用。
2.HTTP是超文本传输协议,信息是明文传输,HTTPS则是具有安全性的SSL加密传输协议。
3.HTTP和HTTPS使用的是完全不同的链接方式,用的端口也不一样,前者是80端口,后者是443端口。
4.HTTP的链接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比HTTP协议安全。
.Net 6 Program.cs设计模式 :建造者模式
23种设计模式之一 用于创建复杂对象,将复杂对象的创建和使用分离
中间件:中间件是一种装配到应用管道以处理请求和响应的软件
- 选择是否将请求传递到管道中的下一个组件。
- 可在管道中的下一个组件前后执行工作。
顺序:通过(请求委托-带着请求上下文)一级一级往下传递
ExceptionHandler=>HSTS(HTTPStrictTranspostSecurity)=>HttpsRedirection=>StaticFiles=>Routing=>CORS=>Authentication=>Authorization=>Custom middlewares=>Endpoint
DI是IOC的实现
ASP.NET Core 支持依赖关系注入 (DI) 软件设计模式,这是一种在类及其依赖关系之间实现控制反转(IOC)的技术。
跨域
后端跨域:
AllowAnyOrigin:允许所有的请求源(需要设置白名单)
.WithOrigins(" ","192.168.1.1",...)
AllowAnyMethod:允许所有的请求方法
AllowAnyHeader:允许所有的请求头
前端跨域(vue.config.js):
项目文件
bin
bin用来保存项目生成后程序集,是默认情况下的输出文件目录,也就是你的工程编译的结果(dll或者exe),包括其它你设置了需要输出的文件,比如:配置,资源文件等内容。这个文件夹是默认的输出路径,我们可以通过:项目属性—>配置属性—>输出路径来修改。
.cs+.csproj = bin/Debug/.Net 6/.dll
obj
用来保存每个模块的编译结果,在.NET中,编译是分模块进行的,编译整个完成后会合并为一个.DLL或.EXE保存到bin目录下。因为每次编译时默认都是采用增量编译,即只重新编译改变了的模块,obj保存每个模块的编译结果,用来加快编译速度。是否采用增量编译,可以通过:项目属性—>配置属性—>高级—>增量编译来设置
switch新语法糖
query = flag switch
{
//1=> query.OrderBy(x => x.销量),
2 => query.OrderBy(x => x.Price),
//3 => query.OrderByDescending(x => x.销量),
4 => query.OrderByDescending(x => x.Price),
_ => query.AsQueryable(),
};
部署
部署是分发要安装到其他计算机上的已完成应用程序或组件的过程。对于控制台应用程序或基于 Windows 窗体的智能客户端应用程序,有两个部署选项可供选择:ClickOnce 和 Windows Installer。
Docker
请求头和响应头
请求头
由四部分组成:首行、header(头)、空行、body(存放url参数)。
首行:传参方法(GET或POST)+url+版本号(例如Http1.1)。
header :由多组参数(key-value)构成。
空行:标志着header的结束。
body:如果传参方法为GET,那么body为空,如果为POST那么传送的参数都在body中。
Accept:浏览器可接受的MIME类型。
Accept-Charset:浏览器可接受的字符集。
Accept-Encoding:浏览器能够进行解码的数据编码方式,比如gzip。
Accept-Language:浏览器所希望的语言种类,当服务器能够提供一种以上的语言版本时要用到。
Authorization:授权信息,通常出现在对服务器发送的WWW-Authenticate头的应答中。
Connection:表示是否需要持久连接。如果 Servlet 看到这里的值为“Keep-Alive”,或者看到请求使 用的是HTTP 1.1(HTTP 1.1默认进行持久连接),它就可以利用持久连接的优点,当页面包含多个 元素时(例如Applet,图片),显著地减少下载所需要的时间。要实现这一点,Servlet需要在应答 中发送一个Content-Length头,最简单的实现方法是:先把内容写入ByteArrayOutputStream,然后 在正式写出内容之前计算它的大小。
Content-Length:表示请求消息正文的长度。
Content-Type:也叫互联网媒体类型( Internet Media Type )或者 MIME 类型,在 HTTP协议消息 头中,它用来表示具体请求中的媒体类型信息。例如, text/html 代表 HTML 格式, image/gif 代表 GIF 图片, application/json 代表JSON 类型
Cookie:设置cookie,这是最重要的请求头信息之一
Host:头字段用于指示请求的目标主机的主机名或IP地址。
From:请求发送者的email地址,由一些特殊的Web客户程序使用,浏览器不会用到它。 Host:初始URL中的主机和端口。
If-Modified-Since:只有当所请求的内容在指定的日期之后又经过修改才返回它,否则返回304“Not Modified”应答。
Pragma(/ˈpræɡmə/):指定“no-cache”值表示服务器必须返回一个刷新后的文档,即使它是代理服务器而且已经 有了页面的本地拷贝。
Referer:包含一个URL,用户从该URL代表的页面出发访问当前请求的页面。
User-Agent(/ˈeɪdʒənt/):浏览器类型,如果Servlet返回的内容与浏览器类型有关则该值非常有用。
UA-Pixels,UA-Color,UA-OS,UA-CPU:由某些版本的IE浏览器所发送的非标准的请求头,表示屏幕大小、颜色深度、操作系统和CPU。
响应头
Accept-Patch:指定服务器所支持的文档补丁格式
Accept-Ranges:服务器所支持的内容范围
Content-Disposition:对已知MIME类型资源的描述,浏览器可以根据这个响应头决定是对返回资源的动作,如:
将其下载或是打开。
Content-Encoding:响应资源所使用的编码类型。
Content-Language:响就内容所使用的语言
Content-Length:响应消息体的长度,用8进制字节表示
Content-Type:当前内容的MIME类型
Date:此条消息被发送时的日期和时间(以RFC 7231中定义的"HTTP日期"格式来表示)
Expires:指定一个日期/时间,超过该时间则认为此回应已经过期
Server:服务器的名称
根据规格返回相应SKU
public GoodDTO GetSKU(int goodId, string? skuValue)
{
string jsonValue = "";
Dictionary<string, string> dic = new Dictionary<string, string>();
if (skuValue != null)
{
string json = skuValue.Trim(',');
var jsonArray = json.Split(',');
foreach (var item in jsonArray)
{
var keys = item.Split(':');
dic[keys[0]] = keys[1];
}
jsonValue = JsonConvert.SerializeObject(dic);
}
var entity = (from a in _context.SKUs
join b in _context.Goods on a.GoodId equals b.GoodId
where (string.IsNullOrEmpty(jsonValue) || a.SKU_Name == jsonValue) &&
a.GoodId == goodId
select new GoodDTO
{
GoodId = a.GoodId,
GoodName = b.GoodName,
Price = a.Price,
Picture = a.Picture,
SKU_No = a.SKU_No,
SKU_Description = a.SKU_Description,
Stock = a.Stock,
BuyCount = 0
}).FirstOrDefault();
return entity ?? new GoodDTO();
路由
请求URL映射到控制器方法的过程,这个映射过程由路由规则定义。
Routing is responsible for matching incoming HTTP requests and dispatching those requests to the app's executable endpoints. Endpoints are the app's units of executable request-handling code. Endpoints are defined in the app and configured when the app starts. The endpoint matching process can extract values from the request's URL and provide those values for request processing. Using endpoint information from the app, routing is also able to generate URLs that map to endpoints.
自定义登录验证中间件
//Program.cs开启内存缓存
builder.Services.AddMemoryCache();
编写自定义中间件:(异步)
//A task that represents the completion of request processing
//用来执行下一个中间件
private readonly RequestDelegate _next;
private readonly IMemoryCache _cache;
/// <summary>
/// 构造方法注入
/// </summary>
/// <param name="next"></param>
/// <param name="cache"></param>
public LoginMiddleware(RequestDelegate next, IMemoryCache cache)
{
_next = next;
_cache = cache;
}
//Invoke 方法固定 编写中间件的逻辑
//Task 异步返回值
//HttpContext Http上下文对象--包含请求和响应的所有信息
public async Task InvokeAsync(HttpContext context)
{
//判断当前URL地址是否为购物车的地址
if (context.Request.Path == "/api/Good/ShoppingCartAdd")
{
//如果是购物车的地址,判断当前用户是否登录
string token = _cache.Get<string>("token");
//如何判断你是否登录了呢?
if (string.IsNullOrEmpty(token))
{
//如果没有登录,就返回一个错误消息
await context.Response.WriteAsync("Please log in first!");
context.Response.StatusCode = 500;
//前台使用响应拦截器处理
return;
}
//(http协议,请求时无状态)如果登录了,验证前后台token是否一致
var requestToken = context.Request.Headers["token"];
if (token != requestToken)
{
await context.Response.WriteAsync("you have no permission!");
context.Response.StatusCode = 401;
return;
}
//如果登录了,就执行下个中间件
}
await _next(context);
}
使用中间件:
//自定义中间件
app.UseMiddleware<LoginMiddleware>();
用户登录:请求和响应使用不同的DTO(RequestLoginDTO和ResponseLoginDTO),保护用户数据
/// <summary>
/// 用户登录(企业)
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpPost]
public IActionResult UserLogin(RequestLoginDTO request)
{
ApiResult apiResult;
if (request == null)
{
return NotFound("对象无效");
}
if (string.IsNullOrEmpty(request.UserName))
{
return NotFound("用户名为空");
}
if (string.IsNullOrEmpty(request.UserPwd))
{
return NotFound("密码为空");
}
var result = _services.UserLogin(request.UserName, request.UserPwd);
if (result == null)
{
apiResult = new ApiResult()
{
Code = 500,
Msg = "登录失败",
};
return Ok(apiResult);
}
else
{
ResponseLoginDTO response = new ResponseLoginDTO()
{
UserId = result.UserId,
UserName = result.UserName,
Name = result.Name,
};
apiResult = new ApiResult()
{
Code = 200,
Msg = "登录成功",
Data = response,
};
string token = _services.CreateTokenString();
//string token = $"{result.UserId}-{DateTime.Now.Ticks}";
//正常情况下,token应该存储到redis中,这里为了方便,直接存储到内存中
_cache.Set("token", token);
apiResult.Token = token;
return Ok(apiResult);
}
}
异步编程(Asynchronous Programming)
避免阻止线程,增加 Web 应用程序中的吞吐量,从而释放线程来为 Web 应用程序中的其他请求提供服务。
异步(底层是状态基,是由多个状态的调度完成)不一定是多线程,有可能是多线程,多线程一定不是异步
多线程的弊端:线程安全、资源占用。
Axios拦截器(interceptor)
定义:在请求或响应被 then 或 catch 处理前拦截它们
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 2xx 范围内的状态码都会触发该函数。
// 对响应数据做点什么
return response;
}, function (error) {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么
return Promise.reject(error);
});
用Hash类型将数据缓存到Redis中
private List<GoodDTO> SetCartRedis(int userId)
{
var query = from a in _context.ShoppingCarts
join b in _context.SKUs on a.SKU_No equals b.SKU_No
join c in _context.Goods on b.GoodId equals c.GoodId
where a.UserId == userId
select new GoodDTO
{
Stock = b.Stock,
GoodId = b.GoodId,
SKU_No = a.SKU_No,
GoodName = c.GoodName,
Picture = b.Picture,
Price = b.Price,
BuyCount = a.BuyCount,
TotalAmount = a.TotalAmount,
UserId = a.UserId,
};
var list = query.ToList();
//将数据存储到Redis中
//设置随机过期时间,防止缓存雪崩
//var random = new Random();
//var randomNumber = random.Next(3, 5);
//_client.Set("carts", list, randomNumber);
//存
_client.HSet("Carts", userId.ToString(), list);
//取
//var list = _client.HGet<List<GoodDTO>>("Carts", userId.ToString());
return list;
}
Redis五大类型
Redis 非关系型数据库
字符串类型是Redis中最基本的数据存储类型,它是一个由字节组成的序列,在Redis中是二进制安全的。这意味着该类型可以接受任何格式数据,如JPEG图像数据和Json对象说明信息。它是标准的key-value,通常用于存储字符串、整数和浮点。Value可容纳高达512MB的数据。
Redis hash 是一个键值(key=>value)对集合。Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。Redis的Hash结构可以使你像在数据库中Update一个属性一样只修改某一项属性值。和String略像,但value中存放的是一张表,一般用于多个个体的详细事项排列,String也可以做到,但要比hash麻烦许多。
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。Redis的列表允许用户从序列的两端推入或者弹出元素,列表由多个字符串值组成的有序可重复的序列,是链表结构,所以向列表两端添加元素的时间复杂度为0(1),获取越接近两端的元素速度就越快。这意味着,即使有数以千万计的元素列表,也可以极快地获得10条记录在头部或尾部。可列入名单的要素最多只有4294967295个。
Redis 的 Set 是 string 类型的无序集合。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 0(1)。所谓集合就是一堆不重复值的组合,并且是没有顺序的。在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis还提供了诸如collection、union和differences等操作,使得实现诸如commandism、poperhike、secondfriends这样的功能变得很容易,或者选择是将结果返回给客户机,还是将它们保存到使用不同命令的新的集合中。
sorted set也叫Redis zset ,和set 一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。
Computed和Methods的区别
Computed可以把计算结果缓存,如果监听的数据源发生变化,才会重新计算,否则直接使用缓存的数据
缓存的优点:引入缓存可以提高系统性能、减少网络传输、缓解服务器负载、支持离线访问、保护原始数据以及提供数据一致性。
Linq左外连接
var list = (from a in _context.Goods
join b in _context.SKUs on a.GId equals b.GId into c
from sku in c.DefaultIfEmpty()
group sku by new { a.GId, a.GName, a.GPicture, a.GPrice } into d
select new
{
d.Key.GId,
d.Key.GName,
d.Key.GPicture,
d.Key.GPrice,
TotalStock = d.Sum(x => x.SKUStock)
}).ToList();
return list;
反射
//定义一个获取反射内容的方法
void getreflectioninfo(assembly myassembly)
{
type[] typeArr=myassemby.Gettypes();//获取类型
foreach (type type in typeArr)//针对每个类型获取详细信息
{
//获取类型的结构信息
constructorinfo[] myconstructors=type.GetConstructors;
//获取类型的字段信息
fieldinfo[] myfields=type.GetFiedls()
//获取方法信息
MethodInfo myMethodInfo=type.GetMethods();
//获取属性信息
propertyInfo[] myproperties=type.GetProperties
//获取事件信息
EventInfo[] Myevents=type.GetEvents;
}
}
加载(loading)
//线程休眠
Thread.Sleep(2000);
<!--服务方式-->
openFullScreen2() {
const loading = this.$loading({
lock: true,
text: 'Loading',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
<!--访问API-->
setTimeout(() => {
loading.close();
}, 2000);
}
多字段排序(Linq.Dynamic)
.NET中OrderBy和ThenBy的语义是不同的,如:list.OrderBy(x=>x.A).OrderBy(x=>x.B),那么最终只会根据B进行排序;list.OrderBy(x=>x.A).ThenBy(x=>x.B),那么最终会先根据A、再根据B进行排序,类似sql中的order by 语句。
这里使用动态LINQ:
1、下包System.Linq.Dynamic.Core
2、using System.Linq.Dynamic.Core
if (!string.IsNullOrEmpty(orderRules))
{
var ruleArray = orderRules.Split(',');
string orderString = "";
for (int i = 0; i < ruleArray.Length; i++)
{
//销量
if (ruleArray[i] == "1")
{
ruleArray[i] = "SaleCount desc";
}
//单价
if (ruleArray[i] == "2")
{
ruleArray[i] = "GPrice desc";
}
orderString += ruleArray[i] + ',';
}
//Func<Good, object> sortBy = x =>new {x.SaleCount desc,x.GPrice desc}
orderString = orderString.Trim(',');
list = list.AsQueryable().OrderBy(orderString).ToList();
SQL循环添加测试数据
declare @i int
set @i=1
while(@i<15)
begin
insert into Goods values (CONCAT('测试数据',@i),'0','0','','',GetDate(),'4')
set @i+=1
end
vue获取来源路由
beforeRouteEnter(to, from, next) {
// ...
next((vm) => {
vm.originPath = from.path;
});
},
// 在组件内部使用
this.$router.push({ path: '/destination', query: { from: this.$route.fullPath }}); // 跳转到目标页面并传递来源路由信息
// 在目标页面中获取来源路由
mounted() {
const sourceRoute = this.$route.query.from; // 获取来源路由信息
}
vue封装公共操作前端页面缓存方法(sessionStorage、localStorage、cookie)
export const session = {
set(key, value) {
window.sessionStorage.setItem(key, value);
},
get(key) {
return window.sessionStorage.getItem(key);
},
remove(key) {
window.sessionStorage.removeItem(key);
}
}
export const local = {
set(key, value) {
window.localStorage.setItem(key, value);
},
get(key) {
return window.localStorage.getItem(key);
},
remove(key) {
window.localStorage.removeItem(key);
}
}
import {session,local} from "../../store/storageHelper.js"
ASP.Net内置对象
Request
Response
Server
Session
Cookie
Application
Swagger原样输出
//原样输出
services.AddControllers().AddNewtonsoftJson(options => {
// 忽略循环引用
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
// 不使用驼峰
options.SerializerSettings.ContractResolver = new DefaultContractResolver();
// 设置时间格式
options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
// 如字段为null值,该字段不会返回到前端
// options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
});