ASP.NET Core 3.x 入门(七)Web API
此入门教程是记录下方参考资料视频的学习过程
开发工具:Visual Studio 2019
参考资料:https://www.bilibili.com/video/BV1c441167KQ
API文档:https://docs.microsoft.com/zh-cn/dotnet/api/?view=aspnetcore-3.1
目录
架构
- RESTful Web API:这次实现的
- Repository:就是之前用的 Service
- Controller:这里我们不返回完整的页面了,只返回数据,通常是 JSON
新建项目
还是 ASP.NET Core Web Application
我的命名是 My_ASP_NET_Core_Api
空模板,Https 和 Docker 都不勾选
将之前项目的 Models 和 Services 文件夹都复制到当前项目中,Services 目录下删除 Department 和 Employee 相关的接口和类
Repositories
项目下新建 Repositories 文件夹,新建六个类
IDepartmentRepository.cs
public interface IDepartmentRepository
{
Task<Department> Add(Department department);
Task<IEnumerable<Department>> GetAll();
Task<Department> GetById(int id);
}
DepartmentRepository.cs
public class DepartmentRepository : IDepartmentRepository
{
private readonly List<Department> _departments = new List<Department>();
public DepartmentRepository()
{
this._departments.Add(new Department
{
Id = 1,
Name = "HR",
EmployeeCount = 16,
Location = "Beijing"
});
this._departments.Add(new Department
{
Id = 2,
Name = "R&D",
EmployeeCount = 52,
Location = "Shanghai"
});
this._departments.Add(new Department
{
Id = 3,
Name = "Sales",
EmployeeCount = 200,
Location = "China"
});
}
public Task<IEnumerable<Department>> GetAll()
{
return Task.Run(() => this._departments.AsEnumerable());
}
public Task<Department> GetById(int id)
{
return Task.Run(() => this._departments.FirstOrDefault(x => x.Id == id));
}
public Task<Department> Add(Department department)
{
department.Id = this._departments.Max(x => x.Id) + 1;
this._departments.Add(department);
return Task.Run(() => department);
}
}
IEmployeeRepository.cs
public interface IEmployeeRepository
{
Task Add(Employee employee);
Task<IEnumerable<Employee>> GetByDepartmentId(int departmentId);
Task<Employee> Fire(int id);
}
EmployeeRepository.cs
public class EmployeeRepository : IEmployeeRepository
{
private readonly List<Employee> _employees = new List<Employee>();
public EmployeeRepository()
{
this._employees.Add(new Employee
{
Id = 1,
DepartmentId = 1,
FirstName = "Nick",
LastName = "Carter",
Gender = Gender.男
});
this._employees.Add(new Employee
{
Id = 2,
DepartmentId = 1,
FirstName = "Michael",
LastName = "Jackson",
Gender = Gender.男
});
this._employees.Add(new Employee
{
Id = 3,
DepartmentId = 1,
FirstName = "Mariah",
LastName = "Carey",
Gender = Gender.女
});
this._employees.Add(new Employee
{
Id = 4,
DepartmentId = 2,
FirstName = "Axl",
LastName = "Rose",
Gender = Gender.男
});
this._employees.Add(new Employee
{
Id = 5,
DepartmentId = 2,
FirstName = "Kate",
LastName = "Winslet",
Gender = Gender.女
});
this._employees.Add(new Employee
{
Id = 6,
DepartmentId = 3,
FirstName = "Rob",
LastName = "Thomas",
Gender = Gender.男
});
this._employees.Add(new Employee
{
Id = 7,
DepartmentId = 3,
FirstName = "Avril",
LastName = "Lavigne",
Gender = Gender.女
});
this._employees.Add(new Employee
{
Id = 8,
DepartmentId = 3,
FirstName = "Katy",
LastName = "Perry",
Gender = Gender.女
});
this._employees.Add(new Employee
{
Id = 9,
DepartmentId = 3,
FirstName = "Michelle",
LastName = "Monaghan",
Gender = Gender.女
});
}
public Task Add(Employee employee)
{
employee.Id = this._employees.Max(x => x.Id) + 1;
this._employees.Add(employee);
return Task.Run(() => employee);
}
public Task<IEnumerable<Employee>> GetByDepartmentId(int departmentId)
{
return Task.Run(() => this._employees.Where(x => x.DepartmentId == departmentId));
}
public Task<Employee> Fire(int id)
{
return Task.Run(() =>
{
var employee = this._employees.FirstOrDefault(e => e.Id == id);
if (employee != null)
{
employee.Fired = true;
return employee;
}
return null;
});
}
}
ISummaryRepository.cs
public interface ISummaryRepository
{
public Task<CompanySummary> GetCompanySummary();
}
SummaryRepository.cs
public class SummaryRepository : ISummaryRepository
{
private readonly IDepartmentRepository _departmentRepository;
public SummaryRepository(IDepartmentRepository departmentRepository)
{
this._departmentRepository = departmentRepository;
}
public Task<CompanySummary> GetCompanySummary()
{
return Task.Run(() =>
{
var all = this._departmentRepository.GetAll().Result;
return new CompanySummary
{
EmployeeCount = all.Sum(x => x.EmployeeCount),
AverageDepartmentEmployeeCount = (int)all.Average(x => x.EmployeeCount)
};
});
}
}
这六个文件和之前删除的 Service 差不多
写完别忘记注册,Startup.cs
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSingleton<IClock, UtcClock>();
services.AddSingleton<IDepartmentRepository, DepartmentRepository>();
services.AddSingleton<IEmployeeRepository, EmployeeRepository>();
services.AddSingleton<ISummaryRepository, SummaryRepository>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Controllers
新建 Controllers
ApiController 建议继承 ControllerBase ,Controller 就是继承自 ControllerBase
ControllerBase 没有对 View 的支持,因为是 Web Api ,所以不需要 View
DepartmentController.cs
[Route("v1/[controller]")]
[ApiController]
public class DepartmentController : ControllerBase
{
private readonly IDepartmentRepository _departmentRepository;
public DepartmentController(IDepartmentRepository departmentRepository)
{
this._departmentRepository = departmentRepository;
}
[HttpGet]// v1/Department verb:GET
//public async Task<IActionResult> GetAll() //也可以
public async Task<ActionResult<IEnumerable<Department>>> GetAll()
{
var departments = await this._departmentRepository.GetAll();
if (!departments.Any())
{
return NoContent();
}
return Ok(departments);
//return new ObjectResult(departments);
}
[HttpPost]// v1/Department verb:POST
public async Task<ActionResult<Department>> Add([FromBody]Department department)
{
var added = await this._departmentRepository.Add(department);
return Ok(added);
}
}
EmployeeController.cs
[Route("v1/[controller]")]
[ApiController]
public class EmployeeController : ControllerBase
{
private readonly IEmployeeRepository _employeeRepository;
public EmployeeController(IEmployeeRepository employeeRepository)
{
this._employeeRepository = employeeRepository;
}
[HttpGet("{departmentId}")]
public async Task<IActionResult> GetByDepartmentId(int departmentId)
{
var employees = await this._employeeRepository.GetByDepartmentId(departmentId);
if (!employees.Any())
{
return NoContent();
}
return Ok(employees);
}
[HttpGet("One/{id}", Name = "GetById")]
public async Task<IActionResult> GetById(int id)
{
var result = await this._employeeRepository.GetById(id);
if (null == result)
{
return NotFound();
}
return Ok(result);
}
[HttpPost]
public async Task<IActionResult> Add([FromBody] Employee employee)
{
var added = await this._employeeRepository.Add(employee);
return CreatedAtRoute("GetById", new { id = added.Id }, added);
//这里的 GetById 就是上面写的那个 GetById 方法,[HttpGet("One/{id}", Name = "GetById")]
}
[HttpPut("{id}")]//其实这里应该用 [HttpPatch]
public async Task<IActionResult> Fire(int id)
{
var result = await this._employeeRepository.Fire(id);
if (null != result)
{
return NoContent();
}
return NotFound();
}
}
SummaryController.cs
[Route("v1/[controller]")]
[ApiController]
public class SummaryController : ControllerBase
{
private readonly ISummaryRepository _summaryRepository;
public SummaryController(ISummaryRepository summaryRepository)
{
this._summaryRepository = summaryRepository;
}
public async Task<IActionResult> Get()
{
var result = await this._summaryRepository.GetCompanySummary();
return Ok(result);
}
}
ApiController Attribute
Controller 上标注的 [ApiController]
- Attribute 路由
- 对 Model 自动验证:比如
[FromBody]Department department
,会根据 Model 字段或属性的 Attribute 标注进行验证 - 推断绑定源:比如
[FromBody]Department department
显示的写出 department 是从请求的 Body 里来的,因为 Controller 有[ApiController]
标注,所以不加[FromBody]
也行- [FromBody] :Body
- [FromForm] :表单
- [FromRoute] :类似
[HttpGet("{departmentId}")]
,但是使用[HttpGet("")]
的方法的参数里就不需要再标注[FromRoute]
- [FromQuery]:QueryString:
?
的参数 - [FromHeader] :从 Http 请求的 Header 里取数据
- [FromServices]
- 具体内容去看文档
补充
[Route("v1/[controller]")]
路由,v1
版本控制,[controller]
是一个通配符,指的是控制器的名称,Department
RESTful Api 主要是通过 Http 动词来区分 Action ,所以默认不用加 Action ,就是说 url 访问 GET 的方法不需要管 Action 的名称
[ApiController]
[HttpGet]
HttpGet 默认可以不写,url 就是 版本/控制器名称 verb:GET
[HttpPost]
[FromBody]
CreatedAtRoute (string routeName, object routeValues, object content);
routeName
String
用于生成 URL 的路由的名称。
routeValues
Object
用于生成 URL 的路由数据。
content
Object
要在实体正文中设置格式的内容值。说白了就是返回给前端的数据
使用 Postman 查看结果
若使用 xml 去请求,还是会返回 json
因为默认情况下,ASP.NET Core Api 返回的是 json 格式的数据
若想支持 xml ,需要修改 Startup 类
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers().AddXmlSerializerFormatters();
}
ASP.NET Core Api 也支持一些常用的格式,以及自定义格式
Department Get 测试
http://localhost:5000/v1/Department
效果图
Department Post 测试
选择 POST ,url 不变,在 Body 里添加数据
效果图
状态码截图
EmployeeController Get 测试
http://localhost:5000/v1/Emoloyee/1
效果图
http://localhost:5000/v1/Employee/One/1
效果图
EmployeeController Post 测试
http://localhost:5000/v1/Employee
效果图
EmployeeController Put 测试
http://localhost:5000/v1/Employee/9
效果图
状态码 204 ,对应 NoContent()
,说明成功
Get 去验证一下
效果图
SummaryController Get 测试
http://localhost:5000/v1/Summary
效果图