Asp.net.Core
前言:
Asp的侧重点有2个MVC和WebApi,虽然说WebApi是MVC的一部分都是可以算是分2部分来说。
Asp. MVC
1.1基本概念:
Model(模型)、View(视图)、Controller(控制器) 控制器与视图之间的数据传递就是模型
Controller(控制器)就是我们的实现类,View(视图)就是数据渲染(Render)之后得到的效果页面、模型就是只有属性的普通类
Models/Person.cs
新的类写法 =>只能说方便
public record Person(string Name,bool IsVip,DateTime CreatedDateTime)
Views/Test/Demo1.cshtml
@model WebApplication1.Models.Person
<div>姓名:@Model.Name</div>
<div>是否Vip:@Model.IsVip</div>
<div>时间:@Model.CreatedDateTime</div>
Controllers/TestController.cs
public class TestController : Controller
{
public IActionResult Demo1()
{
var p1 = new Person("Tome",true,DateTime.Now);
return View(p1);
}
}
然后跑的时候直接就是Test/Demo1就可以得到页面效果 反正就控制器那个文件夹里面把Controller去掉就是网页访问路径之一 然后Demo1就是访问的视图
只能说写起来的感觉跟以前写PHP的MCV差不多
Asp.WebApi
1.1直接来个列子看一下基本接口是怎么写的
细节:其实控制器可以不显式的去继承自任何类
就好比下方的代码TestController : ControllerBase 其实 Test是可以不去继承 ControllerBase也是可以执行的,但是不继承的话会有一些方法是无法直接调用,一般来说 继承一下 ControllerBase就可以不用动他了
Controllers.TestController.cs
namespace WebApplication1.Controllers
{
[Route("api/[controller]")] //这个就是接口的访问路径
[ApiController]
public class TestController : ControllerBase
{
//当要发出get请求的时候需要添加一个Http
//如果是发出Post delet update 都一样 [HttpPost]
[HttpGet]
//这个就是请求里面所包含的方法
public Person GetPerson()
{
return new Person("帝后云曦",18);
}
[HttpPost]
public string[] SaveNote(SaveNoteRequest req)
{
System.IO.File.WriteAllText(req.Title+".text",req.Content);
return new string[] {"OK",req.Title};
}
}
}
Person.cs
public record Person(string Name,int Age);
1.2了解一下Rest
Rest是WebApi的一种风格,WbeApi有两种风格一种是(面向过程)RPC、面向REST
Rpc跟Rest对比
Rpc:想到什么干什么,不用考虑那么多,方便 一把搜哈
Rest: 凸显一个专业
Rpc:控制器/操作方法的形式把服务端的代码当成方法去调用(简单直接)
就写前端的时候最常见的:(使用的是querystring)
/Person/GetAll
/Person/GetByid?id=8
/Person/DeleteByid/8;
Rest:按照HTTP语义来使用Http协议
1、Url用于资源的定位: /user/888、/user/8888/orders
//获得用户编号为888的所有东西
2、Http胃词:就是请求的方式 Get、Post..
3、幂等: 可以理解成 一个行为不管触发多少次结果都是一样的就是幂等
//抽象一点来说 lise.Add(3) 这个行为就不是幂等的每次触发这个行为都会新添加3条数据
4、Get的相应可以被缓存
5、服务器通过状态码来反映资源的获取结果
//接口请求触发了之后 如果成功会返回200状态码 如果失败会直接给有关该错误的状态码比如:404
上点代码熟悉一下
[Route("[controller]/[action]")]
[ApiController]
public class Test : ControllerBase
{
[HttpGet]
public Person[] GetAll()
{
return new Person[] {
new Person(1,"云曦", 16),
new Person(2,"月禅",16),
new Person(3,"清漪",16),
new Person(4,"荒",16) };
}
[HttpGet]
public Person? GetById(long id)
//这里添加?是用来表示可为null的类型
{
if (id == 1)
{
return new Person(1, "云曦", 16);
}
else if( id == 2)
{
return new Person(2, "月禅", 16);
}else if( id == 3)
{
return new Person(3, "清漪", 16);
}else if( id == 4)
{
return new Person(4, "荒", 16);
}
else
{
return null;
}
}
[HttpPost]
public string AddNew(Person p)
{
return "OK";
}
}
//注意上面的{id}是路由参数并不是querystring
//就拿Get{id}为例子:运用到浏览器的路径是:Person/6 而不是Person?id=6
Rest的实现
就好比 我想要Person/aaa的路径系统会优先寻找名为aaa的控制器
注意:如果控制器存在一个没有添加HttpGet/Post等的public方法 可以处理这种请求但是Swagger会报错 可以用``
为了实现这样的路径方式:
[Route("[controller]/[action]")]优先匹配action方法的名字
[ApiExplorerSettings(IgnoreApi =true)]
public void Read()
{
Console.WriteLine("你好");
}
1.3WebApi的异步及返回值
Action的异步方法:
一般来说如果我们去使用异步的时候不是await...async这样去写吗,然后为了更加的好的去分辨异步方法我们会在方法名后面添加async => ActionAsyncm。但是Action方法可以同步也可以异步,异步的Action方法名一般不需要Async结尾
IActionResult:
这个类型是用于我们后端自定义的状态码,正常抛异常不使用这个东西那么会出现一大堆错误乱码,我们使用这一个的话,可以去自定义错误的状态码,更加的直观
[HttpGet]
public IActionResult<int> GetCj2(int id)
//使用泛型指定就不用写那些什么OK之类的东西了
{
if(id == 1)
{
//return OK(88);
return 88;
}else if(id == 2)
{
return 99;
}else
{
return NotFound("id错误");
}
} 这里面的OK NotFound是 ControllerBase里面关于JSON的一种方法
1.4 Asp中依赖注入的使用
首先Aps中的依赖注入 不像是控制程序那样写那么多东西,项目创建的时候在Program.cs就已经给你配置好了
打个比方,我创建了一个Tester.cs的实体类里面包含一个添加的方法,然后我想要在其他地方使用它,我通过依赖注入的方式去把这个方法注入 这样
Tester.cs
public class Tester
{
public int Add(int x,int y)
{
return x + y;
}
}
Program.cs
var builder = WebApplication.CreateBuilder(args);.
builder.Services.AddControllers(); // 直接使用这个进行依赖注入
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
//上面的这三个Add就已经是帮你配置好使用依赖注入的配置了
//你想要进行依赖注入就AddScoped<依赖名称> 这样子就已经算是创建好了
builder.Services.AddScoped<Tester>();
var app = builder.Build();
TestController.cs
[Route("api/[controller]/[action]")]
[ApiController]
public class TestController : ControllerBase
{
private readonly Tester tester;
//声明了一个只读的字段 tester 他的类型是 Tester.cs里面的配置
//readonly就是一个私有字段
public TestController(Tester tester)
{
this.tester = tester;
}
//使用依赖注入的方式把将一个 Tester 对象注入到 TestController 中
//
[HttpGet]
public int Add1()
{
return tester.Add(1, 3);
}
}
那么问题来了,有多个项目的时候,如果想要使用其他项目中的东西,那么我不就要都在Asp中创建好几个依赖注入,有没有一种方法是可以让项目自己进行服务的注册
首先安装一下用到配置的包
Install-Package Zack.Commons
安装好之后在每个项目里面创建一个实现了IModuleInitializer接口的类
ModelIntics.cs
namespace ClassLibrary1
{
internal class ModelIntics : IModuleInitializer
{
public void Initialize(IServiceCollection services)
{
services.AddScoped<Class1>();
}
}
}
然后在Asp项目中就不需要去创建这个类 只需要在Program.cs中实现
var asms =ReflectionHelper.GetAllReferencedAssemblies();
builder.Services.RunModuleInitializers(asms);
其余的正常使用就可以了
1.5使用EFCore+依赖注入进行一个CURD的数据库操作
先看一下项目目录
首先要先配置好EFCore数据库迁移所需要的东西
BookDbContext.cs
public class BookDbContext:DbContext
{
public BookDbContext(DbContextOptions<BookDbContext> options) : base(options) { }
public DbSet<Book> Books { get; set; } //这个Books就是数据库迁移出来的表名
}
然后依赖注入一下:
builder.Services.AddDbContext<BookDbContext>(option =>
{
option.UseSqlServer(builder.Configuration.GetConnectionString("BookConn"));
});
在那个app的json文件里面配置一下数据库的连接字符串
"ConnectionStrings": {
"BookConn": "Server="";Database=Demo1;Trusted_Connection=True;MultipleActiveResultSets=true"
},
然后直接 add 和 update
重点:出现那些什么找不到DbContext的目录删干净一点重新迁移一下就可以了
直接上接口功能:
public class BooklController : ControllerBase
{
private readonly BookDbContext _db;
public BooklController(BookDbContext db)
{
_db = db;
}
[HttpGet]
public async Task<ActionResult<IEnumerable<Book>>> GetAll()
{
var list = _db.Books.ToList(); return Ok(list);
}
[HttpPost]
public async Task<ActionResult<IEnumerable<Book>>> SeletTitle(string Title)
{
var selt = _db.Books.Where(x => x.Title == Title).ToList();
return Ok(selt);
}
[HttpDelete]
public async Task<ActionResult<IEnumerable<Book>>> DeltOne(string Title)
{
var xp = _db.Books.FirstOrDefault(e => e.Title == Title);
if (xp != null)
{
_db.Books.Remove(xp); // 将书籍添加到上下文的删除集合中
await _db.SaveChangesAsync(); // 提交更改到数据库
return Ok("删除成功");
}
else
{
return NotFound("没有找到要删除的书籍");
}
}
总结:个人感觉相当于SqlSuger 有一点繁琐,但是还好把
缓存
缓存概念
缓存的命中 、缓存的命中率、缓存的数据不一致
缓存数据的不一致:就可以看一下上面的图,一开始我数据库给出的结果是60 ,但是后面这个id为1给出的结果修改成了62,我们再去调用这个id=1的条件的时候,因为我们缓存中已经保存了结果是60最后出来的结果也是60,但实际数据应该是62 这就是缓存数据的不一致。
1.客户端缓存
首先来看一下cache-control这是一个响应报文头,服务器如果返回cache-control:max-age=60表示服务器只是浏览器端可以缓存这个响应60秒
我们使用的话其实比较简单,只要给需要进行缓存控制的控制器的操作方法添加
[ResponseCache(Duration=20)] //其中Duration="" 这个是代表这个缓存能保存多少秒
//缓存20秒之后失效
[HttpGet]
public async Task<ActionResult<IEnumerable<YunXi>>> AAA()
{
return await db.Queryable<YunXi>().ToListAsync();
}
2.内存缓存
把缓存的数据放到应用程序的内存,内存缓存中保存的是一系列的建值,类型跟Dictionary一样
内存缓存的数据时保存在当前运行网站程序的内存中,他和进程是有关系的,不同网站的内存缓存时不会相互干扰,但是网站重启之后,数据就会清空
内存缓存的用法:
builder.Services.AddMemoryCache(); //先注入一下
然后注入到控制器中:
public class TestController : ControllerBase
{
private readonly IMemoryCache _cache;
public TestController(ISqlSugarClient db, IMemoryCache cache)
{
_cache = cache;
}
注入完之后开始去写接口:
[HttpGet]
public async Task<ActionResult<IEnumerable<yunxi>>> AAA()
{
var cacheKey = "GetAllYunxi";
//这里首先是定义一个用于内存缓存的键
if (_cache.TryGetValue(cacheKey, out List<yunxi> yunxiList))
{
return yunxiList;
//然后使用方法TryGetValue 去获取内存缓存中的数据,拿到的话就直接返回缓存中的结果
}
//如果缓存不存在数据那就从数据库中查询数据
yunxiList = await _db.Queryable<yunxi>().ToListAsync();
//这个部分是设置了缓存的的绝对过期值:10分钟。
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromMinutes(10));
//然后使用set方法将查询到的数据添加到内存缓存中
_cache.Set(cacheKey, yunxiList, cacheEntryOptions);
return yunxiList;
}
中间件
中间件:MVC框架、响应缓存、身份验证、Swagger之类的都是内置中间件
三个概念:Map、Use、Run 其中:Map是用来定义一个管道可以处理那些请求,Ues和Run是用来定义管道
看一下Program.cs文件中的Use 那个就是直接引用的内置中间件,如果想要自己去自定义:定义一个中间件的类然后 定义好之后use一下就差不多了 to be continue..