diff --git "a/\350\265\226\345\277\227\347\224\237/20240527_API\347\232\204\346\240\270\345\277\203\350\277\207\347\250\213.md" "b/\350\265\226\345\277\227\347\224\237/20240527_API\347\232\204\346\240\270\345\277\203\350\277\207\347\250\213.md" new file mode 100644 index 0000000000000000000000000000000000000000..bc3ab1af97255000f5ea612bf25b7ea1dedc1828 --- /dev/null +++ "b/\350\265\226\345\277\227\347\224\237/20240527_API\347\232\204\346\240\270\345\277\203\350\277\207\347\250\213.md" @@ -0,0 +1,213 @@ +### 一、路由 +#### 1.概念 +根据路由配置确定如何处理该请求。路由定义了不同 URL 地址对应的控制器和操作方法 +#### 2.示例 +~~~c# +[Route("[controller]")] //定义控制器的路由前缀,即/blogs +// BlogsController类继承自ControllerBase,它是一个用于构建API的基类(不包含视图支持) +public class BlogsController : ControllerBase +{ + // 处理对/blogs的GET请求 + public IActionResult Index(){ + return Ok("999"); //返回一个包含字符串"999"的200 OK响应 + } + + [Route("{id}")] //定义Single方法的路由模板 + // 处理对/blogs/{id}的GET请求,接受一个整型参数id + public IActionResult Single(int id) + { + return Ok(id); //返回一个包含id值的200 OK响应 + } +} + +// 上面两个函数可以合并写为以下形式: + [Route("{id?}")] + public IActionResult Index(){ + // 代码块 + } + public IActionResult Single(int id) + { + // 代码块 + } +~~~ +#### 3.拓展:关于控制器 +1. 控制器是应用程序负责处理用户请求并生成响应的类 +2. 本身带有多个action方法,而每个action方法对应着一个特定的HTTP请求类型,可以接受参数、调用服务、查询数据库等等 +3. 通常继承自ControllerBase类(不包含视图)或Controller类 +### 二、模型绑定 +#### 1.概念 +- 一旦根据路由确定要调用的操作方法,就会尝试从请求中提取数据,将其绑定到操作方法的参数或模型对象上 +- 模型绑定不仅可以处理基本的数据类型,还可以解析复杂的数据 +#### 2.Action特性 +- [FromQuery]:从HTTP请求的查询字符串中获取参数的值 +- [FromForm]:从表单中获取参数的值 +- [FromHeader]:从HTTP 请求的头部信息中获取参数值 +- [FromBody]:从请求的内容体获取参数值 +- [FromServices]:从依赖注入容器中获取参数值 +- [FromRoute]:从路由中获取参数值 +- [BindRequiredAttribute]:如果没有值绑定到此参数,或绑定不成功,这个特性将添加一个ModelState错误 +~~~c# +// 这里需要下载包 +using Microsoft.AspNetCore.Mvc.ModelBinding; +namespace Api; +public class BlogDto +{ + [BindRequired] //强调该值必须填写 + public string Title {get;set;}; +} +~~~ +- [BindNever]:在进行模板绑定时,忽略此参数 +- [ApiController]:位于命名空间下方,会尝试自动获取参数值,不再需要为参数指定上述其他特性 +#### 3.示例 +~~~c# + // Dto/BlogDto.cs + public class BlogDto + { + public string Title {get;set;} = null!; + public string Author {get;set;} = null!; + public string? Flag {get;set;} + } + // BlogsController.cs + [HttpPost] + public ActionResult Post(BlogDto blogDto) + { + return blogDto; + } +~~~ +#### 4.拓展:什么是VO、DTO、DO、PO +VO、DTO、DO、PO 是常见的命名约定,通常用于表示不同的数据对象或数据传输对象 +- VO(View Object):视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来 +- DTO(Data Transfer Object):数据传输对象,用于在不同层之间传输数据 +- DO(Domain Object):领域对象,从现实世界中抽象出来的有形或无形的业务实体,例如商品、订单、用户等 +- PO(Persistent Object):持久化对象,数据需要被保存到持久存储介质(比如数据库、文件系统等)中,PO就是用来表示这些持久化数据的对象 + +### 三、模型验证 +是指数据被使用之前的验证过程,它发生在模型绑定之后 +#### 1.数据注解 +使用Required特性为属性添加需要的数据验证 +~~~c# + using System.ComponentModel.DataAnnotations; + namespace Api; + public class BlogDto + { + [MinLength(2,ErrorMessage="标题不能少于2个字符")] + public string Title {get;set;} = null!; + [Required(ErrorMessage="作者不能为空")] + public string Author {get;set;} = null!; + public string? Flag {get;set;} + } + + // BlogsController.cs + // 检查对象是否满足指定的条件 + public ActionResult Put(int id,BlogDto blogDto) + { + if (!ModelState.IsValid){ + return BadRequest(ModelState); + }else + { + + return Ok(new { id, blogDto }); + } + } +~~~ +#### 2.继承自ValidationAttribute类的自定义验证 +~~~c# + // Atrribute/NoSpaceAttribute.cs + using System; + using System.ComponentModel.DataAnnotations; + + public class NoSpaceAttribute : ValidationAttribute + { + protected override ValidationResult IsValid(object value, ValidationContext validationContext) + { + if (value != null && value.ToString().Contains(" ")) + { + // 返回如果包含空格的错误验证消息 + return new ValidationResult("字符串不能包含空格"); + } + else + { + // 返回成功验证结果 + return ValidationResult.Success; + } + } + } + // 在属性中使用 + [NoSpace] + public string Author {get;set;} = null!; +~~~ +#### 3.使用Model实现IValidatableObject接口自定义验证 +~~~c# + using System; + using System.Collections.Generic; + using System.ComponentModel.DataAnnotations; + + namespace Api + { + public class BlogDto : IValidatableObject + { + public string Title { get; set; } = null!; + + public string Author { get; set; } = null!; + public string? Flag { get; set; } + + public IEnumerable Validate (ValidationContext validationContext) + { + if (IsContainSpace(Title)) + { + yield return new ValidationResult("标题不能包含空格", new[] { nameof(Title) }); + } + } + + private bool IsContainSpace(string value) + { + return value?.Contains(" ") ?? false; + } + } + } +~~~ +### 四、执行控制器方法Action +#### 1.概念 +- 一个控制器包括一个或多个Action,而Action是控制器中的一些public类型的函数,它们可以接受参数、执行相关逻辑,最终返回一个结果,该结果作为HTTP响应返回给客户端 +- 如果要让一个Action不起作用,在它上方添加[NonAction] +#### 2.常见的返回结果类型 +Action根据实际需求,可以接受参数也可以不接受,可以返回任何类型的值 +##### 1.状态码 +1. Ok():操作成功200 +2. BadRequest():错误请求400 +3. NoContent():操作成功都是不返回任何内容204 +4. NotFound():资源找不到404 +5. Unauthorized():未授权401 +##### 2.包含对象的状态码 +这一类结果继承自ObjectResult,包括OkObjectResult、CreateResult和NotObjectResult等等 +~~~C# + [HttpPut("{id}")] + public ActionResult Put(int id,BlogDto blogDto) + { + var res = new ObjectResult(new {id,blogDto}); + return res; + } +~~~ +###### 3.重定向结果 +~~~c# + // 重定向到指定url + return Redirect("http://www.baidu.com/"); + // 重定向到当前应用程序中的另一个url + return LocalRedirect("/blogs/users"); // Location: /blogs/users + // 重定向到指定Action + return RedirectToAction("index"); // Location: /Blogs + // 重定向到指定的路由 + return RedirectToRoute("default", new { controller = "Blogs", action = "Details" }); +~~~ +##### 4.内容结果 +~~~c# + // 返回视图ViewResult、PartialViewResult + // 返回JSON字符串 + return new JsonResult(new { + msg = "This is a JSON result", + data = blogDto + }); + // Content返回一个字符串 + return Content("我是添加数据"); +~~~ +此外还可以使用ActionResult类,即可以表示ActionResult对象,也可以表示一个具体类型(由泛型参数T指定) diff --git "a/\350\265\226\345\277\227\347\224\237/20240528_API\347\232\204\346\240\270\345\277\203\350\277\207\347\250\2132.md" "b/\350\265\226\345\277\227\347\224\237/20240528_API\347\232\204\346\240\270\345\277\203\350\277\207\347\250\2132.md" new file mode 100644 index 0000000000000000000000000000000000000000..59f218b35d7b8968ee0099818130f4a300a8e61e --- /dev/null +++ "b/\350\265\226\345\277\227\347\224\237/20240528_API\347\232\204\346\240\270\345\277\203\350\277\207\347\250\2132.md" @@ -0,0 +1,172 @@ +### 五、过滤器 +#### 1.概念 +过滤器和中间件相似,能够在某些功能前后执行,通常用于执行日志记录、身份验证、授权、异常处理和结果转换等操作 +#### 2.过滤器类型 +1. 授权过滤器(Authorization Filter):最先执行,用于检查用户是否有权访问资源,并在未经授权时拦截请求,实现IAsyncAuthorizationFilter或IAuthorizationFilter接口 +2. 资源过滤器(Resource Filter):在Authorization后执行,并在其他过滤器之前和之后执行,实现IAsyncResourceFilter或IResourceFilter接口 +3. 动作过滤器(Action Filter):在执行Action之前或之后执行自定义逻辑,而且在模型绑定后执行,实现IAsyncActionFilter或IActionFilter接口 +4. 异常过滤器(Exception Filter):用于捕获并处理动作方法中抛出的异常,实现IAsyncExceptionFilter或IExceptionFilter接口 +5. 结果过滤器(Result Filter):在IActionResult执行的前后执行,能够控制Action的执行结果,比如格式化结果等。需要注意的是,它只有在Action方法成功执行完成后才会运行,实现IAsyncResultFilter或IResultFilter接口 +#### 3.示例 +~~~c# + // 对于每次请求都需要设置return结果,这里可以使用Result过滤器格式化结果 + // 1. Dto/ApiResult.cs + public class ApiResult + { + public int Code { get; set; } + public string? Msg { get; set; } + public object Data { get; set; } + } + // 2. Filters/ApiResultFilter.cs 实现接口 + using Microsoft.AspNetCore.Mvc; + using Microsoft.AspNetCore.Mvc.Filters; + using System.Threading.Tasks; + namespace Api.Filters; + public class ApiResultFilter:IAsyncResultFilter{ + public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) + { + // 判断返回结果是否为内容,如果是则给context.Result赋值一个新的实例对象ApiResult + if (context.Result is ObjectResult objectResult) + { + context.Result = new ObjectResult(new ApiResult + { + Code = 1000, + Msg = "请求成功", + Data = objectResult.Value + }); + } + else + { + context.Result = new ObjectResult(new ApiResult { Code = 4000, Msg = "无效请求" }); + } + // 必须记得调用,否则不能执行下一个Action + await next(); + } + } + // 3. Startup.cs 注册服务 + public void ConfigureServices(IServiceCollection services) + { + // 全局过滤 + services.AddControllers(options => { + options.Filters.Add(); + }); + } +~~~ + +### 六、配置 +#### 1.概念 +- 采用键值对来表示配置项,并且支持多种形式的配置源,包括文件(支持JSON、XML和INI格式)、命令行参数、环境变量、.NET内存对象等等 +- 要访问配置,需要使用ConfigurationBuilder类,位于Microsoft.Extensions.Configuration命名空间下,实现IConfigurationBuilder接口,该接口包括两个重要的方法 +~~~c# + public interface IConfiguration + { + // Add添加不同形式的配置源 + IConfigurationBuilder Add(IConfigurationSource source); + // Build把配置源生成为程序可访问的配置项 + IConfigurationRoot Build(); + } +~~~ +#### 2.访问JSON配置文件 +~~~c# + // appsetting.json + { + "MySettings": { + "Setting1": "Value1", + "Setting2": "Value2" + } + } + // startup.cs + // IConfiguration 用于读取应用程序的配置 + private readonly IConfiguration _configuration; + public Startup(IConfiguration configuration) + { + _configuration = configuration; + } + public void Configure(IApplicationBuilder app) + { + app.UseRouting(); + app.UseEndpoints(endpoints => { + endpoints.MapControllers(); + + }); + } + public void ConfigureServices(IServiceCollection services) + { + // 输出配置值并打印到控制台 + var setting1 = _configuration["MySettings:Setting1"]; + Console.WriteLine($"Setting1: {setting1}"); + } +~~~ +#### 3.访问其他配置源 +1. 使用AddXmlFile添加XML文件 +2. 使用AddIniFile添加Ini文件 +3. 使用AddInMemoryCollection添加KeyValuePair类型的集合,键和值的类型均为字符串 +4. AddEnvronmentVariables添加当前系统的所有环境变量 +### 七、日志 +#### 1.概念 +- 日志不会为应用程序增加实质性的功能,但是能够让开发人员跟踪程序的运行、调试程序以及记录错误信息 +- 日志两种类型: + 1. 系统日志:系统在运行时向外输出的记录信息 + 2. 用户记录日志:开发人员在程序中适当的位置调用与日志功能相关的API输出 +- 由以下接口组成:(都位于Microsoft.Extensions.Logging命名空间下 + - Ilogger:实际执行记录日志操作的方法 + - IloggerProvider:用于创建ILogger对象 + - IloggerFactory:通过ILoggerProvider对象创建ILogger对象 +- 日志级别: + 1. Trace:级别最低 + 2. Debug:用于记录调试信息 + 3. Information:用于记录应用程序的执行流程信息 + 4. Warning:用于记录应用程序出现的轻微错误或其他不会导致程序停止的警告信息 + 5. Critical:严重级别最高,用于记录引起应用程序崩溃、灾难性故障等信息 +#### 2.Serilog +Serilog 是一款广受欢迎的日志库,它提供了强大的日志记录功能 +##### 1.安装 +~~~js +dotnet add package Serilog +~~~ +##### 2.配置Serilog +~~~c# + public class Program + { + public static void Main(string[] args) + { + // 设置 Serilog 日志配置 + Log.Logger = new LoggerConfiguration() + .WriteTo.Console() //日志输出到控制台 + .CreateLogger(); //创建并初始化 Log.Logger 静态实例 + CreateWebHostBuilder(args).Build().Run(); + } + public static IWebHostBuilder CreateWebHostBuilder(string[] args) + { + return WebHost.CreateDefaultBuilder(args) + .UseSerilog() // 将 Serilog 集成到 ASP.NET Core + .UseStartup(); + } + } +~~~ +##### 3.记录日志 +~~~c# + namespace Api.Controllers + { + [ApiController] + [Route("[controller]")] + public class LogController : ControllerBase + { + // 声明了一个只读的 _logger 字段,用于记录日志 + private readonly ILogger _logger; + // 构造函数通过依赖注入获取 + public LogController(ILogger logger) + { + _logger = logger; + } + + [HttpGet] + public IActionResult Index() + { + // 记录一条信息级别的日志,表示访问了首页 + _logger.LogInformation("访问了首页"); + return Ok("Welcome to the homepage"); + } + } + } +~~~ diff --git "a/\350\265\226\345\277\227\347\224\237/20240530_\351\241\271\347\233\256\350\256\276\350\256\241.md" "b/\350\265\226\345\277\227\347\224\237/20240530_\351\241\271\347\233\256\350\256\276\350\256\241.md" new file mode 100644 index 0000000000000000000000000000000000000000..ec8ebbc05dd61be1a4a95557645117202386c256 --- /dev/null +++ "b/\350\265\226\345\277\227\347\224\237/20240530_\351\241\271\347\233\256\350\256\276\350\256\241.md" @@ -0,0 +1,39 @@ +# 一、项目设计 +## 一、主体结构 +1. Resultfull风格的WebApi +2. Asp.net core 8.0 +3. 采用传统控制器模式 + +## 二、模型设计(例如:图书管理系统) +### 1.作者表Authors +1. 姓名 AuthorName +2. 性别 Gender +3. 出生年月 Birthday +### 2.图书表Books +1. 书名BookName +2. 出版社Publiser +3. 作者AuthorId +4. 价格Price + +## 三、内存型数据库(自定义的一个集合类) +在应用程序中定义一个类,专门提供模拟数据 + +## 四、仓储系统(准备用来接入数据库的一套接口) +### 1.通用型仓储接口 +定义了通用的数据存取操作方法,例如增删改查 +### 2.个性化的仓储接口 +针对特定业务需求而定义的数据存取接口,通常继承自通用型仓储接口,通过扩展通用接口来实现特定业务需求的操作 + +## 五、利用主体结构,具体实现Resultfull风格的关于两个表的CRUD +### 1.作者表 +- 获取作者列表 get/api/authors +- 获取指定作者 get/api/authors/29 +- 新增作者信息 post/api/authors +- 修改作者信息 put/api/authors/29 +- 删除作者信息 delete/api/authors/29 +### 2.图书表-部署于作者表底下 +- 获取图书列表 get/api/authors/9/books +- 获取指定图书 get/api/authors/9/books/29 +- 新增图书信息 post/api/authors/9/books +- 修改图书信息 put/api/authors/9/books/29 +- 删除图书信息 delete/api/authors/9/books/29 \ No newline at end of file diff --git "a/\350\265\226\345\277\227\347\224\237/20240531_\344\273\223\345\202\250.md" "b/\350\265\226\345\277\227\347\224\237/20240531_\344\273\223\345\202\250.md" new file mode 100644 index 0000000000000000000000000000000000000000..d1259cd04a9624b127383cdc41be279f54ca2f6e --- /dev/null +++ "b/\350\265\226\345\277\227\347\224\237/20240531_\344\273\223\345\202\250.md" @@ -0,0 +1,58 @@ +### 仓储模式 +``` +作为领域驱动设计的一部分,在系统设计中的使用非常广泛,它主要用于解除业务逻辑层与数据访问层之间的耦合,使业务逻辑层在存储、访问数据库时无须关心数据的来源及存储方式 +``` +### 接口 +I实例名Repository +```c# + //通过Id获取所有作者的实现 + 实例对象? GetAuthorById(Guid guid); + + //获取所有作者的方法 + //函数三要素(函数名,形参,返回值) + ICollection<实例对象> GetAllAuthors(); +``` + +实例名Repository +```c# + public class 实例名Repository:I实例名Repository + { + public ICollection<实例名> GetAllAuthors() + { + //从持久化数据库中获取 + return 名Db.Instance.实例名.ToList(); + //return [.. 名Db.Instance.实例名]; + } + public 实例名? GetAuthorById(Guid guid) + { + return 名Db.Instance.实例名.SingleOrDefault(item=>item.Id==guid); + } + } +``` + +控制器 +```c# + private readonly I实例名Repository _实例名Repository; + public 实例名Controller(I实例名Repository 实例名Repository) + { + _实例名Repository=实例名Repository; + } + //获取get的函数中 + [HttpGet("{id?}")] + public IActionResult Get(int id) + { + if(id.ToString==""){ + return Ok(_实例名Repository.GetAllAuthors()); + }else{ + return Ok(_实例名Repository.GetAuthorById(id)); + } + } +``` +Startup.cs +```c# + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(); + //新加 + services.AddScoped(); + } \ No newline at end of file