From 294de722d9931866eeba4b8638bb5847678cbbb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=97=E7=8E=89=E6=95=8Fa?= <2647486093@qq.com> Date: Sun, 2 Jun 2024 21:18:44 +0800 Subject: [PATCH] api --- ...7_\350\277\207\346\273\244\345\231\250.md" | 121 +++++ ...240528_serilog\346\227\245\345\277\227.md" | 97 ++++ ...52\346\200\247\344\273\223\345\202\250.md" | 441 ++++++++++++++++++ ...32\347\224\250\344\273\223\345\202\250.md" | 10 + 4 files changed, 669 insertions(+) create mode 100644 "\346\236\227\347\216\211\346\225\217/20240527_\350\277\207\346\273\244\345\231\250.md" create mode 100644 "\346\236\227\347\216\211\346\225\217/20240528_serilog\346\227\245\345\277\227.md" create mode 100644 "\346\236\227\347\216\211\346\225\217/20240530_\344\270\252\346\200\247\344\273\223\345\202\250.md" create mode 100644 "\346\236\227\347\216\211\346\225\217/20240531_\351\200\232\347\224\250\344\273\223\345\202\250.md" diff --git "a/\346\236\227\347\216\211\346\225\217/20240527_\350\277\207\346\273\244\345\231\250.md" "b/\346\236\227\347\216\211\346\225\217/20240527_\350\277\207\346\273\244\345\231\250.md" new file mode 100644 index 0000000..901f4a3 --- /dev/null +++ "b/\346\236\227\347\216\211\346\225\217/20240527_\350\277\207\346\273\244\345\231\250.md" @@ -0,0 +1,121 @@ +# 过滤器 +## ResultFailter 结果过滤器 + +使数据以一定结构返回 +```cs +// Models 定义结果结构类 +public class ApiResult +{ + public int Code { get; set; } + public string? Msg { get; set; } + public Object? Data { get; set; } +} + +// Filters 定义结果过滤器 +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; + +public class ApiResponse : IAsyncResultFilter +{ + public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) + { + if (context is ObjectResult objectResult) + { + context.Result=new ObjectResult(new ApiResult + { + Code=1000, + Msg="成功", + Data=objectResult.Value + }); + } + else + { + context.Result=new ObjectResult(new ApiResult{Code=1000}); + } + await next(); + } +} + + +// Startup.cs 使用过滤器 +public void ConfigureServices(IServiceCollection services) +{ + services.AddControllers(option=> + { + // 全局注册过滤器 + option.Filters.Add(); + }); + // 获取appseting.json里MySql的值 + _configuration.GetConnectionString("MySql"); +} +``` +https://zzzcode.ai/efcore/code-explain + +## ActionFilter + +```cs +// Filters 定义行为过滤器 +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; + +public class ApiActionFilter : IAsyncActionFilter +{ + public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + // ActionExecutingContext.ActionDescriptor 获取Action信息 + var descriptor=context.ActionDescriptor; + // Parameters 获取路由参数 + foreach (var param in descriptor.Parameters) + { + if (param.ParameterType==typeof(string)) + { + var argument=context.ActionArguments[param.Name]; + // 是否包含空格 + if (argument.ToString().Contains(" ")) + { + // 设置响应头 + context.HttpContext.Response.Headers["X-ParameterValidation"]="Fail"; + // 设置返回文本 + context.Result=new BadRequestObjectResult($"不能包含空格:{param.Name}"); + break; + } + } + } + if (context.Result===null) + { + var str=await next(); + context.HttpContext.Response.Headers["X-ParameterValidation"]="Success"; + } + } +} + + +// startup.cs 配置过滤器 + public void ConfigureServices(IServiceCollection service) + { + service.AddControllers(option=> + { + // 全局配置 + option.Filters.Add(); + }); + } +``` +单应用 +```cs +// 单应用 + [ActionParameterFilter] + [HttpPost] + public ActionResult Add(BookDto book) + { + return book; + } +using Microsoft.AspNetCore.Mvc.Filters; +namespace BookDemo; +public sealed class ActionParameterFilter : ActionFilterAttribute +{ + public override void OnActionExecuted(ActionExecutedContext context) + { + base.OnActionExecuted(context); + } +} +``` diff --git "a/\346\236\227\347\216\211\346\225\217/20240528_serilog\346\227\245\345\277\227.md" "b/\346\236\227\347\216\211\346\225\217/20240528_serilog\346\227\245\345\277\227.md" new file mode 100644 index 0000000..06ebec7 --- /dev/null +++ "b/\346\236\227\347\216\211\346\225\217/20240528_serilog\346\227\245\345\277\227.md" @@ -0,0 +1,97 @@ +# serilog 结构化日志记录库 +https://www.cnblogs.com/mq0036/p/8479956.html +1. `dotnet add package Serilog` +2. `dotnet add package Serilog.Sinks.Console` +日志写入数据库 +日志输出到控制台 +## 依赖注入 +```cs +public class Startup +{ + public void Configure(IApplicationBuilder app,IHostingEnvironment env,ILogger logger) + { + app.UseRouting(); + app.UseEndpoints(ep=> + { + ep.MapControllers(); + }); + // 将日志添加到依赖注入容器中 + app.Run(async cont=> + { + // ILogger扩展方法LogInforMation,对应Information信息级别 + // 输出到控制台中 + logger.LogInformation("TEST测试"); + // 输出到页面,当访问路径不存在时,就会返回该内容 + await cont.Response.WriteAsync("HI"); + }); + } +} +``` +## 法一: +```cs +using Serilog; + +public static class Programs +{ + public static void Main(string[] args) + { + createHostBuild(args).Build().Run(); + // 方法一 + // LoggerConfiguration提供流式接口用于构建一个日志通道 + using (var log = new LoggerConfiguration() + // 添加控制器到管道中 + .WriteTo.Console() + // 组装并返回一个实现ILogger接口的Logger对象 + // Logger对象实现IDisposablr,因此可以在using中调用 + .CreateLogger()) + { + // 触发记录日志 + log.Information("Hellow,Serilog"); + log.Warning("Bey,Serilog"); + + } + } +} +``` + +## 法二:在应用程序的其他类里面如何获得这个log对象 +```cs +// 在其他应用程序获取log对象,静态Log +// Log类提供所有与ILogger接口相同的方法 +Log.Logger = new LoggerConfiguration() +.WriteTo.Console() +.CreateLogger(); +Log.Information("hellow,serilog"); +// 关闭,而不是使用using +Log.CloseAndFlush(); +``` +## 法三:将ILogger作为参数注入 +```cs +public class Startup{ + public void Configure(IApplicationBuilder app,ILogger logger) + { + app.Run(async context=> + { + logger.LogInformation("测试"); + await context.Response.WriteAsync("Hi"); + }); + } +} + + +public static class Programs +{ + public static void Main(string[] args) + { + createHostBuild(args).Build().Run(); + } + public static IHostBuilder createHostBuild(string[] args) + { + return Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(b => + { + b.UseStartup(); + }); + + } +} +``` \ No newline at end of file diff --git "a/\346\236\227\347\216\211\346\225\217/20240530_\344\270\252\346\200\247\344\273\223\345\202\250.md" "b/\346\236\227\347\216\211\346\225\217/20240530_\344\270\252\346\200\247\344\273\223\345\202\250.md" new file mode 100644 index 0000000..6c71383 --- /dev/null +++ "b/\346\236\227\347\216\211\346\225\217/20240530_\344\270\252\346\200\247\344\273\223\345\202\250.md" @@ -0,0 +1,441 @@ +# 个性仓储 +## Dto文件夹 +创建数据模型 +**AuthorDto.cs** +```cs +public class AuthorDto +{ + public int Id { get; set; } + public string Name { get; set; }=null!; + public int Age { get; set; } +} +``` +**BookDto.cs** +```cs +public class BookDto +{ + public int Id { get; set; } + public string Name { get; set; }=null!; + public double? Price { get; set; } + // 外键 + public int AuthorId{get;set;} +} +``` +### 增删改操作的数据模型 +**AuthorForCreateDto.cs** +```cs +using System.ComponentModel.DataAnnotations; + +// 资源创建之前,id还没有值,不建议使用与获取数据相同的Dto +// 创建资源DTO,包含除Id外的所有属性 +public class AuthorForCreateDto +{ + // 属性限制 + [Required(ErrorMessage ="作者不能为空")] + public string Name { get; set; }=null!; + public int Age { get; set; } +} +``` +**BookForCreateDto.cs** +```cs +using System.ComponentModel.DataAnnotations; + +public class BookForCreateDto +{ + [Required(ErrorMessage ="图书名不为空")] + public string Name { get; set; }=null!; + public double? Price { get; set; } +} +``` +## Data文件夹 +添加数据种子,提供BookData.Current操作数据集 +**BookData.cs** +```cs +public class BookData +{ + // 返回数据集合体的实例,并使用static声明,使外界能够直接使用 + public static BookData Current {get;}=new BookData(); + // 创建数据集合 + public List Books{get;set;} + public List Authors{get;set;} + // 使用构造函数插入数据 + public BookData() + { + Books=new List{ + new BookDto{ + Id=1, + Name="Love Story", + AuthorId=1, + Price=15.5 + }, + }; + + Authors=new List{ + new AuthorDto{ + Id=1, + Name="Ling", + Age=20 + }, + }; + } +} +``` +## Services文件夹 +创建个性仓储 +### 接口 IXXXRepository +定义操作数据的方法:增删改查 + +**IAuthorRepository.cs** +```cs +// 定义仓储接口 +public interface IAuthorRepository +{ + // 返回值类型 方法名(参数); + + // 获取所有作者 + IEnumerable GetAuthors(); + // 获取指定作者信息 + AuthorDto GetAuthor(int authorId); + // 返回bool值,判断作者是否存在 + bool IsAuthorExists(int authorId); + // 添加资源的方法 + void AddAuthor(AuthorDto author); + // 删除作者 + void DelAuthor(AuthorDto author); + // 修改作者 + void PutAuthor(int authorId,AuthorForCreateDto authorDto); +} +``` + +**IBookRepository.cs** +```cs +public interface IBookRepository +{ + // 获取指定作者的所有图书 + IEnumerable GetBooksForAuthor(int authorId); + // 获取指定作者的指定图书 + BookDto GetBookForAuthor (int authorId,int bookId); + // 添加图书 + void AddBook(BookDto bookDto); + // 删除图书 + void DelBook(BookDto book); + // 修改图书 + void PutBook(int authorId,int bookId,BookForCreateDto book); +} +``` +**Startup.cs** +```cs +public class Startup +{ + public void ConfigureServices(IServiceCollection service) + { + service.AddControllers(option=> + { + option.Filters.Add(); + }); + // 将仓储接口添加到依赖注入容器中 + service.AddScoped(); + service.AddScoped(); + } +} +``` +### 实现接口 + +**AuthorRepository.cs** +```cs +// 继承接口,实现对应的方法 +public class AuthorRepository : IAuthorRepository +{ + // 添加作者 Add + public void AddAuthor(AuthorDto author) + { + BookData.Current.Authors.Add(author); + } + + // 删除图书 + public void DelAuthor(AuthorDto author) + { + // 删除所有图书与指定作者 + BookData.Current.Books.RemoveAll(b=>b.AuthorId==author.Id); + BookData.Current.Authors.Remove(author); + } + + // 获取指定作者信息 FirstOrDefault + public AuthorDto GetAuthor(int authorId) + { + var author=BookData.Current.Authors.FirstOrDefault(au=>au.Id==authorId); + return author; + } + + // 获取所有作者 Current + public IEnumerable GetAuthors() + { + // 根据BookData.Current.Authors 获取Author的数据集,对此进行操作 + return BookData.Current.Authors; + } + + // 返回bool值,判断作者是否存在 Any + public bool IsAuthorExists(int authorId) + { + // 只要有一个符合就返回true + return BookData.Current.Authors.Any(au=>au.Id==authorId); + } + + // 修改作者 + // 如果只给了一个值,那么修改后没有给新值的属性为默认值null 0 + public void PutAuthor(int authorId,AuthorForCreateDto authorDto) + { + var author=GetAuthor(authorId); + author.Name=authorDto.Name; + author.Age=authorDto.Age; + } +} +``` + + +**BookRepository.cs** +```cs +public class BookRepository : IBookRepository +{ + // 获取指定作者的所有图书 Where(AuthorId) + public IEnumerable GetBooksForAuthor(int authorId) + { + return BookData.Current.Books.Where(b=>b.AuthorId==authorId).ToList(); + } + + // 获取指定作者的指定图书 FirstOrDefault(authorId,bookId) + public BookDto GetBookForAuthor(int authorId, int bookId) + { + return BookData.Current.Books.FirstOrDefault(bk=>bk.AuthorId==authorId&&bk.Id==bookId); + } + + // 添加图书 Add + public void AddBook(BookDto book) + { + BookData.Current.Books.Add(book); + } + + // 删除图书 Remove + public void DelBook(BookDto book) + { + BookData.Current.Books.Remove(book); + } + + // 修改图书 GetBookForAuthor + public void PutBook(int authorId, int bookId, BookForCreateDto bookDto) + { + var book=GetBookForAuthor(authorId,bookId); + book.Name=bookDto.Name; + book.Price=bookDto.Price; + } +} +``` +## Controller文件夹 +控制器,使用依赖注入,通过IXXXRepository操作数据 +**AuthorController** +```cs +using Microsoft.AspNetCore.Mvc; + +// 自动对模型进行验证,失败返回400 +[ApiController] +[Route("api/[controller]/author")] +public class AuthorController:ControllerBase +{ + // 一、构造函数注入,获取IAuthorRepository接口,使用接口中的方法在Action中操作数据 + public IAuthorRepository AuthorRepository{get;} + public AuthorController(IAuthorRepository authorRepository) + { + AuthorRepository=authorRepository; + } + + // [请求方法] + // public 返回值类型 方法名(参数){} + + // 获取集合 + [HttpGet] + public ActionResult> GetAuthors() + { + return AuthorRepository.GetAuthors().ToList(); + } + + // 获取指定ID + // 由于CreatedAtRoute指向GetAuhor方法的URL,因此需要定义一个路由名称 + [HttpGet("{authorId}",Name =nameof(GetAuthor))] + public ActionResult GetAuthor(int authorId) + { + var author= AuthorRepository.GetAuthor(authorId); + if (author==null)return NotFound(); + return author; + } + + // 添加作者,使用特定DTO作为参数 + [HttpPost] + public IActionResult CreateAuthor(AuthorForCreateDto authorForCreateDto) + { + // 将AuthorForCreateDto 转化为 AuthorDto + var authorDto=new AuthorDto + { + Name=authorForCreateDto.Name, + Age=authorForCreateDto.Age + }; + // 使用接口方法添加数据 + AuthorRepository.AddAuthor(authorDto); + + // 添加成功后,调用CreatedAtRoute(Action路由名称,Action参数的匿名对象,添加成功后的资源本身) + // CreatedAtRoute 返回201,并在响应头添加 Location=创建资源的URL + + // 返回新增的数据 + return CreatedAtRoute(nameof(GetAuthor),new {authorId=authorDto.Id},authorDto); + } + + // 删除作者 + [HttpDelete("{authorId}")] + public IActionResult DelAuthor(int authorId) + { + var author=AuthorRepository.GetAuthor(authorId); + if(author==null)return NotFound(); + + AuthorRepository.DelAuthor(author); + return NoContent(); + } + + // 修改作者 + // 如果只给了一个值,那么修改后没有给新值的属性为默认值null 0 + [HttpPut("{authorId}")] + public IActionResult PutAuthor(int authorId,AuthorForCreateDto authorDto) + { + var author = AuthorRepository.GetAuthor(authorId); + if(author==null)return NotFound(); + AuthorRepository.PutAuthor(authorId,authorDto); + return NoContent(); + } + + // 部分修改 + [HttpPatch("{authorId}")] + public IActionResult PartPutAuthor(int authorId,JsonPatchDocument patchDocument) + { + var author=authorRepository.GetAuthor(authorId); + if(author==null)return NotFound(); + + var auhtorToPatch=new authorForCreateDto + { + Name=author.Name, + Price=author.Price, + }; + + JsonPatchDocument.ApplyTo(auhtorToPatch,ModelState); + if(!ModelState.IsValid)return BadRequest(ModelState); + + authorRepository.PutAuthor(authorId,auhtorToPatch); + return NoContent(); + } +} +``` +**BookController.cs** +```cs +using Microsoft.AspNetCore.Mvc; + +[ApiController] +[Route("api/[controller]/author/{authorId}/books")] +public class BookController : ControllerBase +{ + // 依赖注入 + // 通过BookRepository使用接口里面实现的方法 + public IBookRepository BookRepository { get; } + public IAuthorRepository AuthorRepository { get; } + public BookController(IBookRepository bookRepository, IAuthorRepository authorRepository) + { + BookRepository = bookRepository; + AuthorRepository = authorRepository; + } + + // 先判断作者是否存在,再查找图书 + + // 获取指定作者的所有图书 + [HttpGet] + public ActionResult> GetBooks(int authorId) + { + if (!AuthorRepository.IsAuthorExists(authorId)) return NotFound(); + return BookRepository.GetBooksForAuthor(authorId).ToList(); + + } + + // 获取指定作者的指定图书 + [HttpGet("{bookId}",Name ="GetBook")] + public ActionResult GetBook(int authorId, int bookId) + { + if (!AuthorRepository.IsAuthorExists(authorId)) return NotFound(); + + var book = BookRepository.GetBookForAuthor(authorId, bookId); + if (book == null)return NotFound(); + return book; + } + + // 添加图书 + [HttpPost] + public IActionResult CreateBook(int authorId,BookForCreateDto bookForCreateDto) + { + if (!AuthorRepository.IsAuthorExists(authorId)) return NotFound(); + + // 设置除Id外的属性 + var book=new BookDto + { + Price=bookForCreateDto.Price, + Name=bookForCreateDto.Name, + // 添加外键 + AuthorId=authorId + }; + // 使用接口方法添加数据 + BookRepository.AddBook(book); + // 返回新增数据的值 + return CreatedAtRoute(nameof(GetBook),new {authorId=book.AuthorId,bookId=book.Id},book); + } + + // 删除指定图书 + [HttpDelete("{bookId}")] + public IActionResult DelBook(int authorId,int bookId) + { + if (!AuthorRepository.IsAuthorExists(authorId)) return NotFound(); + // 根据id获取指定数据 + var book=BookRepository.GetBookForAuthor(authorId,bookId); + if(book==null)return NotFound(); + // 删除指定数据 + BookRepository.DelBook(book); + return NoContent(); + } + + // 修改图书 + [HttpPut("{bookId}")] + public IActionResult PutBook(int authorId,int bookId,BookForCreateDto bookDto) + { + if (!AuthorRepository.IsAuthorExists(authorId)) return NotFound(); + var book=BookRepository.GetBookForAuthor(authorId,bookId); + if(book==null)return NotFound(); + bookRepository.PutBook(authorId,bookId,bookDto); + return NoContent(); + } + + // 部分修改图书 + [HttpPatch("{bookId}")] + // JsonPatchDocument值从请求消息正文中获取,表示一个JSON Patch对象 + public IActionResult PartPutBook(int authorId,int bookId,JsonPatchDocument patchDocument) + { + if (!AuthorRepository.IsAuthorExists(authorId)) return NotFound(); + var book=bookRepository.GetBookForAuthor(authorId,bookId); + if(book==null)return NotFound(); + + var bookToPatch=new BookForCreateDto + { + Name=book.Name, + Price=book.Price, + }; + // 将相应的修改方法应用到新建对象上 + patchDocument.ApplyTo(bookToPatch,ModelState); + // 错误返回400 + if(!ModelState.IsValid)return BadRequest(ModelState); + bookRepository.PutBook(authorId,bookId,bookToPatch); + // 204 + return NoContent(); + } +} +``` \ No newline at end of file diff --git "a/\346\236\227\347\216\211\346\225\217/20240531_\351\200\232\347\224\250\344\273\223\345\202\250.md" "b/\346\236\227\347\216\211\346\225\217/20240531_\351\200\232\347\224\250\344\273\223\345\202\250.md" new file mode 100644 index 0000000..168d293 --- /dev/null +++ "b/\346\236\227\347\216\211\346\225\217/20240531_\351\200\232\347\224\250\344\273\223\345\202\250.md" @@ -0,0 +1,10 @@ +# 代码提示 +[点击](https://code.visualstudio.com/),下载zip x64版本的VSCode +.Net下载:设置=》setting.json 查看路径 +环境变量中添加dotnet路径 +# Guid +GUID 作为主键的好处:唯一性、无需往返数据库、无法被猜出来 +GUID 作为主键的缺点:存储空间大(16字节)、没有顺序 (ABP 生成器解决了guid无序问题。) + +ID 作为主键的好处:存储空间小,int类型只存储4字节,long也就8字节 +ID 作为主键的缺点:主键Id要往返数据库、id自增号容易被猜测出来 \ No newline at end of file -- Gitee