.NET 之 WebApi学习笔记 (持续更新)

理解项目结构

我们创建一个 Web API 项目之后,初始项目结构是这样的,如下图:

  • Program.cs 文件代码分析:只保留几个主要代码所有 app.Usexxxx 的语句都用于添加中间件组件ASP.NET Core 只是一个中间件管道

    csharp 复制代码
    namespace WebApiDemo
    {
        public class Program
        {
            public static void Main(string[] args)
            {
                // 使用构建器
                var builder = WebApplication.CreateBuilder(args);
                // 构建应用程序
                var app = builder.Build();
                // 告诉应用程序 如果收到HTTP请求,重定向到HTTPS
                app.UseHttpsRedirection();
                // 持续运行
                app.Run();
            }
        }
    }

运行之后,控制台打印信息如下图

  • Properties (属性)文件里面的launchSettings.json 配置文件,配置端口

    kotlin 复制代码
    {
      "$schema": "https://json.schemastore.org/launchsettings.json",
      "profiles": {
        "http": {
          "commandName": "Project",
          "dotnetRunMessages": true,
          "launchBrowser": false,
          "applicationUrl": "http://localhost:5153", // HTTP 端口配置
          "environmentVariables": {
            "ASPNETCORE_ENVIRONMENT": "Development"
          }
        },
        "https": {
          "commandName": "Project",
          "dotnetRunMessages": true,
          "launchBrowser": true, // 运行时是否打开浏览器
          "applicationUrl": "https://localhost:7067;http://localhost:5153", // HTTPS 端口配置
          "environmentVariables": {
            "ASPNETCORE_ENVIRONMENT": "Development"
          }
        }
      }
    }

1、Controller 控制器 创建 Web API

1*、创建最小api

  • 举个栗子

    typescript 复制代码
    namespace WebApiDemo
    {
        public class Program
        {
            public static void Main(string[] args)
            {
                // 使用构建器
                var builder = WebApplication.CreateBuilder(args);
                // 构建应用程序
                var app = builder.Build();
                // 告诉应用程序 如果收到HTTP请求,重定向到HTTPS
                app.UseHttpsRedirection();
    ​
                // 最小API
                // Get请求
                app.MapGet("/", () => "Hello World!");
    ​
                // Get请求
                app.MapGet("/hello/{name}", (string name) => $"Hello {name}!");
    ​
                // Post请求,
                app.MapPost("/hello/{name}", (string name) => $"POST:Hello {name}!");
    ​
                // Put请求
                app.MapPut("/hello/{name}", (string name) => $"POST:Hello {name}!");
    ​
                // Delete请求
                app.MapDelete("/hello/{name}", (string name) => $"DELETE:Hello {name}!");
    ​
                // 持续运行
                app.Run();
            }
        }
    }

2、创建一个控制器

1、创建控制器(Controller)

Controller 是 ASP.NET Core Web API 中用于处理 HTTP 请求的组件,一个 Controller 通常负责一类相关业务,例如用户管理、订单管理、人员管理等。

1、创建 Controllers 文件夹

一般在项目根目录下创建一个 Controllers 文件夹,用于存放所有控制器类。

2、创建控制器类
  • 例如,在 Controllers 文件夹中新建一个 PersonController 类:
3、将普通类变成 Web API 控制器

要让 PersonController 能够处理 HTTP 请求,需要完成以下两步

  • 1、继承 ControllerBase

    ControllerBase 是所有 Web API 控制器的基类。Web API 项目一般都继承 ControllerBase;如果是 MVC 页面项目,则通常继承 Controller

    kotlin 复制代码
    public class PersonController:ControllerBase { }
  • 2、添加 [ApiController] 特性

    告诉 ASP.NET Core:这是一个 Web API 控制器。

    kotlin 复制代码
    [ApiController]
    public class PersonController : ControllerBase { }
  • 3、配置路由

    使用 [Route] 指定访问路径。

    api:接口统一前缀[controller]:表示控制器名称(去掉 Controller 后缀)

    下面代码对应的最终访问路径为:/api/person

    csharp 复制代码
    [ApiController] 
    [Route("api/[controller]")]
    public class PersonController : ControllerBase { }
  • 4、创建操作方法 Action

    控制器中的每一个公开方法(Action)通常对应一个接口,用于处理某一种 HTTP 请求。

    [HttpGet]:处理 GET 请求

    csharp 复制代码
    using Microsoft.AspNetCore.Mvc;
    ​
    [ApiController]
    [Route("api/[controller]")]
    public class PersonController : ControllerBase
    {
        [HttpGet]
        public string GetPerson()
        {
            return "Hello Person";
        }
    }

2、将 HTTP 请求映射到操作方法(Action)

ASP.NET Core Web API 中,HTTP 请求需要通过路由匹配,找到对应的 Controller 和 Action 方法

1、注册 Conroller 相关服务

在入口文件 Program.cs 中,通过构建器注册 Controller 所需的服务builder.Services.AddControllers(); 告诉 ASP.NET Core:当前项目需要使用 Controller,请准备好运行 Controller 所需的相关功能注意:AddControllers() 只是让 Controller 具备被创建和执行的能力,并不会自动让 Controller 的路由生效

  • 作用:注册 Controller 的创建和执行机制 注册模型绑定功能 注册参数校验功能 注册 JSON 序列化和反序列化功能 注册 Action 执行、过滤器等 MVC 相关服务 使 Controller 可以通过依赖注入获取所需对象、
2、注册 Controller 路由

创建应用程序对象之后,通过 app.MapControllers(); 注册 Controller 路由告诉 ASP.NET Core:读取项目中的 Controller 路由,并将符合条件的 HTTP 请求交给对应的 Action 方法处理

  • 作用

    读取 Controller 和 Action 上配置的特性路由将 HTTP 请求方式和请求路径映射到对应的 Action将 Controller 路由添加到 ASP.NET Core 的端点路由系统中使客户端能够通过 URL 访问 Controller 中的接口

  • 举个栗子

    • PersonController.cs
    csharp 复制代码
    using Microsoft.AspNetCore.Mvc;
    ​
    [ApiController]
    // Controller 基础路由
    [Route("api/[controller]")]
    public class PersonController : ControllerBase
    {
        [HttpGet]
        public string GetPerson()
        {
            return "Hello Person";
        }
    ​
        // 需要 id 参数 GET /api/user/{id}
        [HttpGet("{id}")]
        public string GetPersonById(int id)
        {
            return $"Hello Person with ID: {id}";
        }
    }
    • Program.cs
    csharp 复制代码
    namespace WebApiDemo
    {
        public class Program
        {
            public static void Main(string[] args)
            {
                // 使用构建器
                var builder = WebApplication.CreateBuilder(args);
    ​
                // 注册控制器服务
                builder.Services.AddControllers();
    ​
                // 构建应用程序
                var app = builder.Build();
                // 告诉应用程序 如果收到HTTP请求,重定向到HTTPS
                app.UseHttpsRedirection();
    ​
                // 注册控制器
                app.MapControllers();
    ​
                // 持续运行
                app.Run();
            }
        }
    }

2、模型绑定

定义:将 HTTP 请求中的数据映射到操作方法参数的过程

过程:读取 HTTP 请求 ---> 找到数据 ---> 转换类型 ---> 赋值给参数可以理解为: HTTP 请求 ---> Model Binder (模型绑定器) ---> Controller (方法参数)

1、HTTP 请求中 数据来源

1、Route(路由)

可以写 [FromRoute] 来限制参数只能来着路由,如果来自其他地方就报错

  • 举个栗子
csharp 复制代码
```

GET https://localhost:7165/api/person/1/女
```

-   Controller

```

[HttpGet("{id}/{gender}")]
public string GetPerson(string id,[FromRoute] string gender)
{
    return $"Helloe,the person{id}'s gender is {gender}";
}
```

2、Query(查询字符串)

可以写 [FromQuery] 来限制参数来源

  • 举个栗子
csharp 复制代码
```

GET https://localhost:7165/api/person/1?gender=女
```

-   Controller

```

[HttpGet("{id}")]
public string GetPerson(string id,[FromQuery] string gender)
{
    return $"Helloe,the person{id}'s gender is {gender}";
}
```
  • 多个参数 栗子

    bash 复制代码
    GET https://localhost:7165/api/person/1?gender=女&name=Omg
    • Contoller
    csharp 复制代码
    [HttpGet("{id}")]
    public string GetPerson(string id,[FromQuery] string gender,string name)
    {
        return $"Helloe,the person{id}'s gender is {gender} and name is {name}";
    }

3、Body(请求体)

请求体 也就是 我们前端平时写的 JSON 格式一般用于 POST 和 PUT 请求可以用 [FromBody] 限制参数来源

后端首先 需要先创建 类

  • 举个栗子
csharp 复制代码
```

POST https://localhost:7165/api/person
```

-   传参 JSON

```

{
    "id":1,
    "name":"YOUNG",
    "gender":"女",
    "age":22,
    "address":"重庆市渝北区"
}
```

-   Dto 类

```

namespace ControllerDemo1.Models
{
    public class PersonDto
    {
        public int Id { get; set; }
        public required string Name { get; set; }
        public string? Gender { get; set; }
        public int? Age { get; set; }
        public string? Address { get; set; }
​
    }
}
```

-   Controller

```

[HttpPost]
public string PostPerson([FromBody] PersonDto person)
{
    return $"POST信息:Id={person.Id},Name={person.Name},Gender={person.Gender},Age={person.Age},                  Address={person.Address}";
    // POST信息:Id=1,Name=YOUNG,Gender=女,Age=22,Address=重庆市渝北区
}
```

4、Form(表单)

可以通过 [FromForm] 限制参数来源

  • 举个栗子
csharp 复制代码
-   Controller

```

[HttpPost]
public string PostPerson([FromForm] PersonDto person)
{
    return $"POST信息:Id={person.Id},Name={person.Name},Gender={person.Gender},Age={person.Age},                  Address={person.Address}";
    // POST信息:Id=1,Name=YOUNG,Gender=女,Age=22,Address=重庆市渝北区
}
```

5、Header(请求头)

可以通过 [FromHeader] 限制参数来源

  • 举个栗子
csharp 复制代码
```

GET https://localhost:7165/api/person
```

-   Controller

```

[HttpGet]
public string GetAuthorization([FromHeader(Name = "MyAuthorization")] string MyAuthorization)
{
    return $"The Authorization is {MyAuthorization}";
    // The Authorization is this is MyAuthorization
}
```

3、数据注解 模型验证

定义:当客户端提交数据到 API 时,后端自动检查这个数据是否符合要求一般发生在 API层

最常见的验证方式 DataAnnotations (.NET自带的一套验证特性)命名空间:

arduino 复制代码
using System.ComponentModel.DataAnnotations;
  • 举个栗子 (使用数据注解 进行 单个 属性验证)

    csharp 复制代码
    using System.ComponentModel.DataAnnotations;
    ​
    namespace WebApiDemo1.Dtos;
    ​
    public class PersonDtos
    {
        public required int Id { get; set; }
    ​
        [Required(ErrorMessage ="name is required!!!")] // 必须填写
        //[StringLength(20)] // 限制字符串长度
        [StringLength(20, MinimumLength = 2, ErrorMessage = "长度最小必须为2")]
        public required string Name { get; set; }
    ​
        [Range(0, 100, ErrorMessage = "年龄范围在0-100")]
        public int Age { get; set; }
    ​
        [MinLength(5)] // 最短
        [MaxLength(50)] // 最长
        public string? Description { get; set; }
    ​
        [EmailAddress] // 邮箱验证格式
        public string? Email { get; set; }
    }
  • 注意

    区分 requied[Required]

    required 是 C# 语言特性,创建对象时必须初始化,否则编译器报警

    [Required] 其实是 [RequiredAttribute] 缩写

4、ValidationAttribute 模型验证

1、步骤

  • 1、创建一个自定义 Attribute 类,并继承 ValidationAttribute

    一般自定义验证类都会以 Attribute 结尾,实际使用的时候可以不写 Attribute

    • 举个栗子

    EducationValidatateAttribute : ValidationAttribute继承 ValidationAttribute 就说明 EducationValidatateAttribute 是一个验证属性

    arduino 复制代码
    using System.ComponentModel.DataAnnotations;
    ​
    namespace WebApiDemo1.Models.Validations;
    ​
    public class EducationValidatateAttribute : ValidationAttribute
    {
    }
  • 2、重写 IsValid 方法

    • ValidationResult 是方法的返回类型

      • 验证成功:return ValidationResult.Success;
      • 验证失败:return new ValidationResult("不能包含空格");
      • ValidationResult? = 返回一个验证结果,也可能表示没有错误
    • object? value 是当前正在验证的属性值

      • 假设将改验证属性写在 DTO 里面的 Name 字段上面,请求{"name":"Lily"}那么 ASP.NET Core 调用 IsValid 的时候,value就是 Lily
    • ValidationContext validationContext :当前正在验证哪个对象、哪个属性

    value

validationContext

csharp 复制代码
using System.ComponentModel.DataAnnotations;
​
namespace WebApiDemo1.Models.Validations;
​
public class TestValidateAttribute : ValidationAttribute
{
    protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
    {
      // 验证规则
    }
}
  • 3、加在 DTO 属性上

2、举个栗子

  • PersonDto.cs
csharp 复制代码
using System.ComponentModel.DataAnnotations;
using WebApiDemo1.Models.Validations;
​
namespace WebApiDemo1.Models;
​
public class PersonDto
{
    [Required(ErrorMessage = "Name is asked!")]
    public required string Name { get; set; }
​
    [Required(ErrorMessage = "Age is asked!")]
    [Range(0, 120, ErrorMessage = "Age's range is 0-120")]
    public int Age { get; set; }
​
    [Required]
    [EducationValidatate]
    public required string Education { get; set; }
​
    public string? Gender { get; set; }
}
  • EducationValidatateAttribute.cs
kotlin 复制代码
using System.ComponentModel.DataAnnotations;
​
namespace WebApiDemo1.Models.Validations;
​
public class EducationValidatateAttribute : ValidationAttribute
{
    protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
    {
        if (value is not (object?)"PRIMARY" and not (object?)"SECONDARY" and not (object?)"UNIVERSITY")
        {
            return new ValidationResult("Please enter the correct educational background:PRIMARY; SECONDARY; UNIVERSITY");
        }
​
        return ValidationResult.Success;
​
    }
}

  • TestDto.cs
csharp 复制代码
[TestValidate]
public class TestDto
{
    public int LeftNum { get; set; }
    public int RightNum { get; set; }
    public required string Symbol { get; set; }
}
  • TestValidateAttribute.cs
kotlin 复制代码
using System.ComponentModel.DataAnnotations;
​
namespace WebApiDemo1.Models.Validations;
​
public class TestValidateAttribute : ValidationAttribute
{
    protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
    {
        var test = validationContext.ObjectInstance as TestDto;
        if (test == null)
        {
            return new ValidationResult("No Test!!!");
        }
        if (test.LeftNum > test.RightNum && test.Symbol != ">")
        {
            return new ValidationResult("LeftNum IS BIGGER THAN RightNum");
        }
        else if (test.LeftNum < test.RightNum && test.Symbol != "<")
        {
            return new ValidationResult("LeftNum IS SMALLER THAN RightNum");
        }
        else if (test.LeftNum == test.RightNum && test.Symbol != "=")
        {
            return new ValidationResult("LeftNum  Equal RightNum");
        }
​
        return ValidationResult.Success;
​
    }
}

5、WebApi 返回类型

返回类型决定:Controller 方法能返回什么内容;最终HTTP响应长什么样

1、直接返回普通类型

直接返回 字符串、数字、布尔值这些,不需要考虑 HTTP 状态码

  • 举个栗子
csharp 复制代码
[ApiController]
[Route("api/[controller]")]
public class PersonController:ControllerBase
{
    [HttpGet]
    public string GetPerson()
    {
        return "Person" ;
    }
}

2、直接返回 DTO 对象

假设现在有一个 Dto对象 就可以 new 一个返回对象

ASP.NET Core 会自动序列化成 JSON,不需要自己写 JsonConvert.SerializeObject(...)

  • 举个栗子
csharp 复制代码
[ApiController]
[Route("api/[controller]")]
public class PersonController:ControllerBase
{
    // 返回单个 PersonDto 对象
    [HttpGet("{id}")]
    public PersonDto GetPerson(string id)
    {
        return new PersonDto
        {
            Name = $"YOUNG + {id}",
            Age = 23,
            Gender = "Female"
        };
    }
​
    // 返回 PersonDto 数组
    [HttpGet]
    public List<PersonDto> GetPersonList()
    {
        return new List<PersonDto>
        {
            new()
            {
                Name = "YOUNG",
                Age = 22,
                Gender = "Female"
            },
            new()
            {
                Name = "GNAY",
                Age = 24,
                Gender = "Male"
            }
        };
    }
}

3、IActionResult

可以灵活返回不同 HTTP 状态BadRequest()NotFound()Ok() 这些方法,它们都定义在 ControllerBase 中。

  • 举个栗子
ini 复制代码
[ApiController]
[Route("api/[controller]")]
public class PersonController: ControllerBase
{
    [HttpGet("{id}")]
    public IActionResult GetPerson(int id)
    {
        if (id < 0)
        {
            return BadRequest();
        };
​
        if(id ==0)
        {
            return Unauthorized();
        }
​
        if(id == 100)
        {
            return NotFound();
        }
​
        var person = new PersonDto
        {
            Name = "YOUNG",
            Age = 24,
            Gender = "Female"
        };
​
        return Ok(person);
    }
}

4、ActionResult<T>

控制器方法执行后返回的结果类型

ActionResult<T>IActionResult 差不多,实际上就是 具体数据类型 T + IActionResult可以返回一个对象 也可以 Ok(对象)

如果需要返回明确的 DTO 可以使用 ActionResult<T> 因为一眼就知道需要返回什么对象

  • 举个栗子
ini 复制代码
[ApiController]
[Route("api/[controller]")]
public class PersonController : ControllerBase
{
    [HttpGet("{id}")]
    public ActionResult<PersonDto> GetPerson(int id)
    {
        if (id < 0)
        {
            return BadRequest();
        }
​
        if (id == 0)
        {
            return Unauthorized();
        }
​
        if (id == 100)
        {
            return NotFound();
        }
​
        return new PersonDto
        {
            Name = "YOUNG",
            Age = 24,
            Gender = "Female"
        };
    }
}

1、异步接口 async Task

异步接口通常用 Task<T> 如果直接写 Task 相当于没有返回内容,就像一般的删除接口一样

async:告诉编译器 这个方法里面可能会用 await

Task:告诉调用方 这个方法是异步的,结果以后才完成

swift 复制代码
public async Task<返回类型> 方法名()
{
    var result = await 异步操作();
​
    return result;
}

2、常见的 HTTP 返回方法

  • 状态码:200 【成功】

    kotlin 复制代码
    return Ok();
  • 状态码:201 【创建成功】

    kotlin 复制代码
    return Created();
  • 状态码:400 【参数错误】

    vbscript 复制代码
    400 Bad Request
  • 状态码:404 【找不到数据】

    kotlin 复制代码
    return NotFound();
  • 状态码:401 【未登录】

    kotlin 复制代码
    return Unauthorized();
  • 状态码:403 【禁止访问】

    kotlin 复制代码
    return Forbid();
  • 状态码:204 【没有返回内容】

    kotlin 复制代码
    return NoContent();

6、内存存储库 Repository

Repository:存储库;专门负责数据访问

不用数据库,把数据暂时放在程序内存里的一个集合中,再通过一个 Repository 类统一增删改查

在模拟的时候,可以不用数据库,将数据写在 内存存储库里面。数据存在程序运行时的 RAM 里。数据只在程序运行期间存在,不会永久保存。

内存存储库的作用就是 不让Controller 自己处理输入如:Add(...)、Remove(...) 等数据处理操作数据处理操作都放在 Repository 文件里面

  • 举个栗子(静态类)存储库
csharp 复制代码
/** PersonRepository.cs */
namespace WebApiDemo1.Models.Repositories;
​
public static class PersonRepository
{
    private static List<PersonDto> _persons = new()
    {
        new PersonDto{Id = 1, Name = "Young", Age = 20 },
        new PersonDto{ Id = 2, Name = "Gnay", Age = 22 },
        new PersonDto{ Id = 3, Name = "Lily", Age = 20 },
        new PersonDto{ Id = 4, Name = "Harwon", Age = 22 },
    };
​
    public static List<PersonDto> GetAll()
    {
        return _persons;
    }
​
    public static PersonDto? GetById(int id)
    {
        return _persons.FirstOrDefault(x => x.Id == id);
    }
​
    public static void Add(PersonDto person)
    {
        _persons.Add(person);
    }
​
    public static void Delete(int id)
    {
        var person = _persons.FirstOrDefault(x => x.Id == id);
​
        if (person != null)
        {
            _persons.Remove(person);
        }
    }
​
    public static bool PersonExits(int id)
    {
        return _persons.Any(x => x.Id == id);
    }
}
scss 复制代码
/** PersonController.cs */ 
using Microsoft.AspNetCore.Mvc;
using WebApiDemo1.Models;
using WebApiDemo1.Models.Repositories;
​
namespace WebApiDemo1.Controllers;
​
[ApiController]
[Route("api/[controller]")]
public class PersonController : ControllerBase
{
    [HttpGet]
    public ActionResult<List<PersonDto>> GetPersonList()
    {
        return Ok(PersonRepository.GetAll());
    }
​
    [HttpGet("{id}")]
    public ActionResult<PersonDto> GetPerson(int id)
    {
        var person = PersonRepository.GetById(id);
        if (person == null)
        {
            return NotFound();
        }
        return Ok(person);
    }
​
    [HttpPost]
    public ActionResult<PersonDto> PostPerson(PersonDto person)
    {
        PersonRepository.Add(person);
        return Created();               
    }
​
    [HttpDelete("{id}")]
    public ActionResult DeletePerson(int id)
    {
        var person = PersonRepository.GetById(id);
        if (person == null)
        {
            return NotFound();
        }
        PersonRepository.Delete(person.Id);
        return Ok();
    }
}

7、操作过滤器 ActionFilter

可以把后端 Filter 类比成前端里的"请求拦截器 / 路由守卫 / axios interceptor"

处理"控制器方法执行前后都要做的公共逻辑";可以理解为:在 Controller 的 Action 方法执行前后,自动插入一段代码

通过使用 ASP.NET Core 中的筛选器,可在请求处理管道中的特定阶段之前或之后运行代码。

一个请求可能会经历:

复制代码
HTTP 请求
   ↓
路由匹配
   ↓
Controller
   ↓
过滤器
   ↓
Action 方法
   ↓
过滤器
   ↓
HTTP 响应
  • 作用

    • 日志记录:记录哪个接口被调用
    • 参数记录:检查参数是否合法
    • 性能统计:计算接口执行耗时
    • 修改参数:Action执行前修改参数
    • 修改返回值:Action执行后修改结果
    • 统一业务处理:做重复的公共逻辑

一个过滤器需要 继承 ActionFilterAttribute

ActionFilterAttributeASP.NET Core 给我们提供的一个基类

  • OnActionExecuting:Action 方法执行之前 触发
  • OnActionExecuted:Action 方法执行之后 触发
  • context:当前 Action 请求的上下文信息,可以通过context.ActionArguments["参数"]获取指定参数
  • context.Result:可以提前终止 Action
  • 举个栗子
csharp 复制代码
/** PersonValidateIdFilterAttribute.cs */
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using WebApiDemo1.Models.Repositories;
​
namespace WebApiDemo1.Filters;
​
/** 验证 id 是否正确 */
// 1、继承 ActionFilterAttribute
public class PersonValidateIdFilterAttribute : ActionFilterAttribute
{
    // 2、重写方法 OnActionExecuting(Action执行之前触发)
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        // 调用父类 ActionFilterAttribute 的 OnActionExecuting() 方法
        // 可以理解 为先让父类做它原本该做的事情
        // 然后再执行我们自己的代码
        base.OnActionExecuting(context);
​
        // 3、通过 context.ActionArguments 获取id
        var id = context.ActionArguments["id"] as int?;
​
        // 4、判断 id 值是否正确
        if (id.HasValue)
        {
            if (id.Value <= 0)
            {
                // 5、添加错误 给 id 加一个验证错误"Id is Invalid!"
                // AddModelError("字段","发生了什么错误")
                context.ModelState.AddModelError("id", "Id Is Invalid!");
​
                // 6、把 ModelState 中的验证错误整理成规范的 HTTP 错误响应。
                var problemDetails = new ValidationProblemDetails(context.ModelState)
                {
                    Title = "This Is A 400 Error Title",
                    Status = StatusCodes.Status400BadRequest
                };
​
                // 7、指定当前请求的响应结果
                // 注意:一旦在 OnActionExecuting 中给 context.Result 赋值,
                // Controller 的 Action 就不会继续执行。
                context.Result = new BadRequestObjectResult(problemDetails);
            }
            else if (!PersonRepository.PersonExits(id.Value))
            {
                context.ModelState.AddModelError("id", "Person Is Not Found!");
                var problemDetalis = new ValidationProblemDetails(context.ModelState)
                {
                    Title = "This Is A 404 Error Title",
                    Status = StatusCodes.Status404NotFound
                };
                context.Result = new BadRequestObjectResult(problemDetalis);
            }
        }
    }
}
csharp 复制代码
/** PersonController.cs */ 
using Microsoft.AspNetCore.Mvc;
using WebApiDemo1.Filters;
using WebApiDemo1.Models;
using WebApiDemo1.Models.Repositories;
​
namespace WebApiDemo1.Controllers;
​
[ApiController]
[Route("api/[controller]")]
public class PersonController : ControllerBase
{
    [HttpGet("{id}")]
    // 8、将操作过滤添加在接口位置
    [PersonValidateIdFilter]
    public ActionResult<PersonDto> GetPerson(int id)
    {
        var person = PersonRepository.GetById(id);
        if (person == null)
        {
            return NotFound();
        }
        return Ok(person);
    }
}

1、创建端点

Create()CreateAtAction() 的区别

  • Created() 只告诉客户端"创建成功了,并告诉你资源地址";

  • CreatedAtAction()ControllerBase 提供的方法,用于返回 201 Created ,并基于某个 Action 生成 Location 响应头

    • 作用:

      • 返回 HTTP 201 Created
      • 根据指定的 Action 自动生成新资源 URL
      • 可以把新创建的资源放进响应体
    • 使用时 有四个参数,四个参数都可以为空

      • actionName(string);指定用于生成 URL 的 Action 名称
      • controllerName(string);指定目标 Controller 名称
      • routeValues(object);提供生成 URL 所需的路由值
      • value(object);放入 HTTP 响应体的数据
    • CreatedAtAction(actionName)

    • CreatedAtAction(actionName, value)

    • CreatedAtAction(actionName, routeValues, value)

    • CreatedAtAction(actionName, controllerName, routeValues, value)

  • 举个栗子

    要求:新建 person ,名称不允许重复

csharp 复制代码
/** Controller */
[HttpPost]
public ActionResult<PersonDto> PostPerson(PersonDto person)
{
    PersonRepository.AddPerson(person);
    // nameOf:用于获取 某个变量、类型、方法、属性、参数等"名称"的字符串
    return CreatedAtAction(
        nameof(GetPerson),
        new { id = person.Id },
        person);
}
csharp 复制代码
/** Repository */
public static void AddPerson(PersonDto person)
{
    int maxId = _persons.Count != 0 ? _persons.Max(x => x.Id) : 1;
    person.Id = maxId + 1;
​
    _persons.Add(person);
}
​
public static bool IsPersonRepeat(PersonDto person)
{
    var exitPerson = _persons.Any(x => x.Name == person.Name);
    if (exitPerson)
    {
        return true;
    }
    return false;
}
ini 复制代码
/** ActionFilter */
public class PersonValidatePersonExitFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        base.OnActionExecuting(context);
        var person = context.ActionArguments["person"] as PersonDto;
        if(person == null)
        {
            context.ModelState.AddModelError("person", "Person Is Be Null");
            var problemDetails = new ValidationProblemDetails(context.ModelState)
            {
                Title = "This Is A 400 Error!",
                Status = StatusCodes.Status400BadRequest
            };
            context.Result = new BadRequestObjectResult(problemDetails);
        }
        else if(PersonRepository.IsPersonRepeat(person))
        {
            context.ModelState.AddModelError("name", "Person Is Exited!");
            var problemDetails = new ValidationProblemDetails(context.ModelState)
            {
                Title = "This Is A 400 Error!",
                Status = StatusCodes.Status400BadRequest
            };
            context.Result = new BadRequestObjectResult(problemDetails);
        }
    }
}

0、杂七杂八

1、静态修饰符 static

static可以修饰类,使类成为静态类。这样在此类中只能定义静态的方法和静态的变量

  • 修饰类

    静态类不能被实例化,也就是说不能使用new关键字创建静态类类型的变量

  • 修饰变量

    static可以修饰变量,注意:这个变量只能是成员变量,而不能是局部变量

  • 修饰方法

    static可以修饰方法,Main方法必须用static修饰, 因为它是与程序共存亡的,是程序的入口和程序的大门

    • 静态方法是不属于特定对象的方法
    • 静态方法可以直接访问静态成员(包括静态字段、静态方法)
    • 静态方法不可以直接访问实例成员,并且静态方法也不能直接调用实例方法
  • 修饰构造函数

    static可以修饰,使构造函数成为静态构造函数。 并且不能在再使用其他修饰符,并且不能是实例构造函数

    • 不能继承
    • 静态构造函数没有访问修饰符,没有参数,只有一个static修饰符
    • 静态构造函数只执行一次

2、构造函数和方法 区别

构造函数的名称 必须与 类名相同,并且没有返回值 类型等。使用 new 对象() 调用用于设置对象的初始状态

方法必须有返回类型。使用类名.方法()调用

  • 举个栗子

    csharp 复制代码
    public class Person
    {
        public string Name { get; set; }
    ​
        // 构造函数
        public Person(string name)
        {
            Name = name;
        }
    ​
        // 方法
        public void SayHello()
        {
            Console.WriteLine($"你好,我是{Name}");
        }
    }
    scss 复制代码
    class Person
          ↓
    new Person()
          ↓
    调用构造函数
          ↓
    Person 对象创建完成
          ↓
    person.SayHello()
          ↓
    调用普通方法
相关推荐
程序员cxuan1 小时前
Claude Code :如何最大化你的 Session 价值
人工智能·后端·程序员
李高钢1 小时前
WPF MVVM Light 入门:从零到Hello World
c#·wpf
雨落倾城夏未凉2 小时前
halcon核心-C# 联合编程(十一)
后端
曹牧3 小时前
C#:函数参数指定默认值
开发语言·c#
元界metalite3 小时前
SpringBoot整合Druid-连接池参数为什么不能照抄
后端
fatcoder3 小时前
玩转Nginx 09 — 运维排障:三场"火灾"实战演练
前端·后端·nginx
元界metalite3 小时前
MyBatis通用查询如何避免SELECT *?MetaLite如何按需返回字段
后端
Flynt3 小时前
Go 1.27 升了一波,泛型方法和 JSON v2 真香但有个坑
后端·go