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

目录

ASP.NET Core 3.x 入门(一)创建项目 和 部署

ASP.NET Core 3.x 入门(二)建立 Controller ,使用 Tag Helper

ASP.NET Core 3.x 入门(三)View Component

ASP.NET Core 3.x 入门(四)Razor Page

ASP.NET Core 3.x 入门(五)SignalR

ASP.NET Core 3.x 入门(六)Blazor

ASP.NET Core 3.x 入门(七)Web API

ASP.NET Core 3.x 入门(八)gRPC - Protocol Buffer

ASP.NET Core 3.x 入门(九)gRPC in ASP.NET Core 3.x

架构

  • 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

效果图

ASP.NET Core 3.x 入门(七)Web API 结束

posted @ 2021-05-13 20:12  .NET好耶  阅读(291)  评论(0编辑  收藏  举报