.NET 6入门笔记
1、ApiControllerAttribute
-
实际上
[ApiController]
是ApiControllerAttribute
类, 要在方括号外面写全, 方括号内不用写全
2、ApiController数据源推断
(0)参数特性(过时)
-
.net core 3.1以前, 如果方法内传递的参数未知, 需要使用如下特性修饰参数, 否则报错
-
.net core 3.1以后, 如果控制器上没有
[ApiController]
使用如下修饰 -
自从有了
[ApiController]
, 可以自动自动推断对象
特性 | 绑定源 |
---|---|
[FromBody] | 该参数来自请求正文 |
[FromForm] | 请求正文中的表单数据 |
[FromHeader] | 请求标头 |
[FromQuery] | 请求查询字符串参数 |
[FromRoute] | 当前请求中的路由参数 |
[FromServices] | 作为操作参数插入的服务请求 |
namespace SecondDemo.Controllers
{
//[ApiController]
[Route("/api/[controller]/[action]")]
public class TestPostController : ControllerBase
{
//使用Model对象接受前端请求的数据
[HttpPost]
public string Post([FromBody] TestPostViewModel? model)
{
return model.I + model.Name;
}
}
}
-
[ApiController]
属性可以用于控制器类, 以启用下述API特定固定行为-
属性路由要求
-
自动HTTP400响应
-
绑定源参数推断
-
Multipart / form-data
请求推理, 多文件、数据上传, 自动请求推理 -
错误状态代码的问题详细信息
-
做数据验证, 不符合规则将报错
namespace SecondDemo.Models { public class TestPostViewModel { [MaxLength(3)] //最大长度为3 [Required] //必填项 public int I { get; set; } public string? Name { get; set; } } }
-
(1)使用Model接受前端传来的json对象
<script>
const { createApp, ref } = Vue;
const app = createApp({
setup() {
const userName = ref("NiHao");
const onGetValue = () => {
axios.post("http://localhost:5203/api/HelloWorld/Post", {i:666, name:"asd"})
.then(res =>{
userName.value = res.data;
})
}
return {userName, onGetValue}
}
})
</script>
namespace SecondDemo.Models
{
public class TestPostViewModel
{
public int I { get; set; }
public string? Name { get; set; }
}
}
namespace SecondDemo.Controllers
{
[ApiController]
[Route("/api/[controller]/[action]")]
public class TestPostController : ControllerBase
{
//使用Model对象接受前端请求的数据
[HttpPost]
public string Post(TestPostViewModel? model)
{
return model.I + model.Name;
}
}
}
3、创建Restful风格API
API | 描述 | 请求正文 | 响应正文 |
---|---|---|---|
GET /api/todoitems |
获取所有待办事项 | None | 待办事项的数组 |
GET /api/todoitems/{id} |
按ID获取项 | None | 待办事项 |
POST /api/todoitems |
添加新项 | 待办事项(实体) | 待办事项(实体的ID) |
PUT /api/todoitems/{id} |
更新现有项 | 待办事项(实体) | None |
DELETE /api/todoitems/{id} |
删除项 | None | None |
4、一次WebApp完整过程
5、三个返回值类型
ASP.NET Core提供以下WebApi控制器操作返回类型选项
-
特定类型
-
IActionResult
-
ActionResult
(1)特定类型
-
最简单的操作返回基元或者复杂数据类型(如
sring
或自定义对象)
[HttpGet]
public List<Produch> Get()
{
return _repostiory.GetProducts();
}
(2)IActionResult
-
做微服务项目, 由于状态码信息少, 占带宽少, 项目并发访问资源占用少, 使用状态码作为返回值, 进行实例心跳监测
-
当操作中可能有多个
ActionResult
返回类型时, 时候用IActionResult
, 表示多种Http状态码, 此类别中的某些常见返回类型为BadRequestResult(400)、NotFound(404)、OkObjectResult(200)
-
Ok()
便利方法, 作为return new OkObjectResult(arg)
的简写, 可以传递一个参数, 但是文档里不知道返回值类型示例
namespace SecondDemo.Controllers
{
[ApiController]
[Route("/api/[controller]/[action]")]
public class TestIActionResultController : ControllerBase
{
//返回200不带参数
[HttpGet]
public IActionResult GetOkNoArgs()
{
//Ok()便利方法, 作为return new OkObjectResult(arg)的简写
return Ok();
}
//返回200带个参数
[HttpGet]
public IActionResult GetOk()
{
return Ok(new TestPostViewModel { I = 123, Name = "asd" });
}
}
}
(3)ActionResult
-
ASP.NET core
包括面向Web Api
控制器操作的ActionResult
类型-
这种返回值既能返回普通类型返回值
-
又能返回指定状态码
-
//使用ActionResult
[HttpGet("{id}")]
public ActionResult<Product> GetProductById(int id)
{
if (id != 1)
{
return NotFound();
}
return new Product { Id = 1, Name = "asda"};
}
//同样, 使用IActionResult也能完成任务
[HttpGet("{id}")]
public IActionResult GetProductById(int id)
{
if (id != 1)
{
return NotFound();
}
return Ok(new Product { Id = 1, Name = "dnisai" });
}
6、Minimal APIS
最小的API包括, 在Program.cs
中声明
-
新的承载API
-
WebApplication
和WebApplicationBuilder
-
新的路由API
-
MapXxx(上下文地址, 终结者后执行委托)
-
可以理解为请求的url
-
执行完需要返回/响应的东西
-
委托可以直接声明、或者lambda表达式, 或者静态方法
-
//注册服务
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
//创建webapp
var app = builder.Build();
//创建委托
//MapXxx(路由地址, 当执行到终结者路由时 需要执行的委托
// () => statement...; return...;)
app.MapGet("/MinimalTest", () => "hello get");
app.MapPost("/MinimalTest", (Product p) => { return new TestPostViewModel { I = p.Id, Name = p.Name }; });
//使用静态方法作为委托参数
app.MapPut("/MinimalTest", Hello.HelloPut);
//使用Lambda表达式声明非匿名方法委托
string HelloDelete() => "hello delete";
app.MapDelete("/MinimalTest", HelloDelete);
//开启webapp
app.Run();
public class Hello
{
public static string HelloPut()
{
return "hello";
}
}
(1)路由约束
-
限制路由的匹配行为
//路由约束, 约束路由参数
//只能传递, int类型的id作为参数
app.MapPost("MiniMalConsTest/{id:int}", (string id) => "符合传参规定 id:" + id);
//使用正则表达式, 只能传递a-z\0-9\下划线
app.MapPost("MiniMalConsTest/{password:regex(^[a-z0-9_-]+$)}", (string password) => "符合传参规定 pwd:" + password);
(2)传参形式
-
常见的请求的传参形式与Restful保持一致
-
MinimalAPIS有一种依赖注入的传参形式
7、控制反转、依赖注入
(1)IOC
-
逻辑层、数据库链接层, 在使用的时候, 不再是我们去请求他们, 而是他们主动注入服务
-
在ASP.NET Core 6中, IOC容器是
ServiceCollection
服务收集器-
是一个存放服务的容器
-
服务就是在开发中需要的各种类的统称
-
ServiceCollection
不仅能在Web项目中使用
-
(2)DI注入
-
注入方式
-
构造函数注入(基础)
-
属性注入(好用, 使用增强容器)
-
增强容器
AutoFac
, 可以进行属性注入, 也可以进行AOP面向切面编程, 但是打开就是全局AOP, 性能不高, 不如用原生AOP
-
-
方法注入(基本不用)
-
-
依赖注入是实现控制反转的一种手段或方法
-
谁依赖谁: 应用程序依赖IOC容器
-
为什么依赖: 应用程序需要IOC容器来提供对象需要的外部资源
-
谁注入谁: IOC容器注入应用程序某个对象, 应用程序所依赖的对象
-
注入了什么, 注入某个对象所需要的外部资源(对象、资源、常量数据)
(3)WebApi中使用例子
1.创建Service
namespace SecondDemo.Services
{
public class UserService
{
public string GetUserName()
{
return "my username";
}
}
}
2.在Program.cs中加装顺态服务注册, 将UserService放入IOC容器
builder.Services.AddTransient<UserService>();
3.在需要的Controller中, 使用属性+构造器注入, 并使用
namespace SecondDemo.Controllers
{
[ApiController]
[Route("/api/[controller]/[action]")]
public class TestIOCController : ControllerBase
{
//创建属性
private UserService UserService { get; }
//使用构造器注入UserService
public TestIOCController(UserService userService)
{
this.UserService = userService;
}
//测试IOC容器DI注入
[HttpGet]
public string GetUserName()
{
return UserService.GetUserName();
}
}
}
(5)在Minimal Apis中使用
//Minimal APIS使用依赖注入
app.MapPost("MiniMalIOCDITest", (UserService userService) => userService.GetUserName())
(6)依赖倒置
-
假如使用接口规范Service, 需要在注册IService, 和Service
-
使用如下两个泛型的方法去注册IService, 第一泛型是接口, 第二个泛型是实现类
//加装瞬态服务注册, 将 接口类型、实现类型 IUserService、UserService放入IOC容器
builder.Services.AddTransient<IUserService, UserService>();
builder.Services.AddTransient<UserService>();
(7)IOC生命周期
-
Transient
瞬态生命周期-
每次使用该属性的时候都要new一个实例
-
-
Singleton
单例生命周期-
使用该属性的时候new一个实例, 以后都用这个
-
-
Scoped
作用域生命周期 线程单例-
每次请求都会new一个实例, 在这次请求里都用这个
-
1.使用场景
-
Scoped
不同线程多次共享一个属性 -
Transient
无需共享同一个属性 -
Singleton
只要注册了这个属性就一直有某个属性, 相当于缓存了这个属性-
builder.Services.AddSingleton<IService>(new Service(1));
-
2.注册服务
-
注册瞬态
builder.Services.AddTransient<服务层接口类, 服务层接口实现类>();
builder.Services.AddTransient(typeof(服务层接口类), typeof(服务层接口实现类));
-
注册线程
builder.Services.AddScoped<服务层接口类, 服务层接口实现类>();
builder.Services.AddScoped<服务层接口类, 服务层接口实现类>(typeof(IService), typeof(IService));
-
注册单例
builder.Services.AddSingleton<服务层接口类, 服务层接口实现类>();
builder.Services.AddSingleton(typeof(服务层接口类), typeof(服务层接口实现类));
//特点, 传入一个参数
builder.Services.AddSingleton<服务层接口类>(service);