第 11 章 Web API 开发

阿里云教程1个月前发布
10 0 0

11.1 RESTful API 设计原则

核心原则:资源为中心(如/api/users)、HTTP 方法语义(GET 查询 / POST 创建 / PUT 更新 / DELETE 删除)、无状态、返回标准状态码。

规范示例

需求

HTTP 方法

接口路径

状态码

查询所有用户

GET

/api/users

200 OK

查询单个用户

GET

/api/users/{id}

200 OK/404 Not Found

创建用户

POST

/api/users

201 Created

全量更新用户

PUT

/api/users/{id}

204 No Content

部分更新用户

PATCH

/api/users/{id}

204 No Content

删除用户

DELETE

/api/users/{id}

204 No Content

11.2 控制器设计与路由配置

控制器核心规则:继承ControllerBase(API 控制器无需视图)、用[ApiController]标记(自动启用模型验证、路由推断)。

路由配置方式

  • 特性路由(推荐 API 使用):[Route(“api/[controller]/[action]”)] 或 固定路由 [Route(“api/v1/users”)]。
  • 约定路由(全局配置,适合简单场景):app.MapControllerRoute(name: “default”, pattern: “api/{controller}/{action}/{id?}”);。

实战案例:用户管理 API 控制器

[ApiController]
[Route("api/v1/[controller]")] // 路由:/api/v1/users
public class UsersController : ControllerBase
{
    private readonly AppDbContext _dbContext;

    public UsersController(AppDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    // 1. 查询所有用户(支持分页)
    [HttpGet]
    public async Task<ActionResult<List<UserDto>>> GetUsers(
        [FromQuery] int page = 1, 
        [FromQuery] int pageSize = 10)
    {
        var total = await _dbContext.Users.CountAsync();
        var users = await _dbContext.Users
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .Select(u => new UserDto // 用DTO避免暴露实体完整属性
            {
                Id = u.Id,
                Name = u.Name,
                Email = u.Email,
                Age = u.Age
            })
            .ToListAsync();

        // 返回分页结果
        return Ok(new PagedResult<UserDto>
        {
            Total = total,
            Page = page,
            PageSize = pageSize,
            Data = users
        });
    }

    // 2. 查询单个用户(路由参数:id)
    [HttpGet("{id}")]
    public async Task<ActionResult<UserDto>> GetUserById(int id)
    {
        var user = await _dbContext.Users.FindAsync(id);
        if (user == null)
        {
            return NotFound(new { Message = "用户不存在" }); // 404状态码
        }

        var userDto = new UserDto
        {
            Id = user.Id,
            Name = user.Name,
            Email = user.Email,
            Age = user.Age
        };
        return Ok(userDto);
    }

    // 3. 创建用户(请求体:FromBody)
    [HttpPost]
    public async Task<ActionResult<UserDto>> CreateUser([FromBody] CreateUserRequest request)
    {
        // 模型验证(由[ApiController]自动触发,验证失败返回400)
        var user = new User
        {
            Name = request.Name,
            Email = request.Email,
            Age = request.Age,
            CreateTime = DateTime.Now
        };

        _dbContext.Users.Add(user);
        await _dbContext.SaveChangesAsync();

        // 返回201 Created,包含新用户ID
        var userDto = new UserDto
        {
            Id = user.Id,
            Name = user.Name,
            Email = user.Email,
            Age = user.Age
        };
        return CreatedAtAction(nameof(GetUserById), new { id = user.Id }, userDto);
    }

    // 4. 删除用户
    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteUser(int id)
    {
        var user = await _dbContext.Users.FindAsync(id);
        if (user == null)
        {
            return NotFound();
        }

        _dbContext.Users.Remove(user);
        await _dbContext.SaveChangesAsync();
        return NoContent(); // 204 No Content(无返回体)
    }
}

// 辅助类:DTO与请求模型
public class UserDto
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public int Age { get; set; }
}

public class CreateUserRequest
{
    [Required(ErrorMessage = "用户名不能为空")]
    [MaxLength(50, ErrorMessage = "用户名最长50个字符")]
    public string Name { get; set; }

    [Required(ErrorMessage = "邮箱不能为空")]
    [EmailAddress(ErrorMessage = "邮箱格式错误")]
    public string Email { get; set; }

    [Range(18, 100, ErrorMessage = "年龄必须在18-100之间")]
    public int Age { get; set; }
}

public class PagedResult<T>
{
    public int Total { get; set; } // 总条数
    public int Page { get; set; } // 当前页
    public int PageSize { get; set; } // 每页条数
    public List<T> Data { get; set; } // 数据列表
}

11.3 模型绑定与数据验证

模型绑定来源:[FromQuery](URL 查询参数)、[FromRoute](路由参数)、[FromBody](请求体,如 JSON)、[FromForm](表单数据)、[FromHeader](请求头)。

数据验证:通过DataAnnotations特性(如[Required]、[EmailAddress])定义规则,[ApiController]自动触发验证,失败返回400 Bad Request。

自定义验证案例

// 自定义验证特性:邮箱不能包含特定域名
public class NoInvalidDomainAttribute : ValidationAttribute
{
    private readonly string _invalidDomain;

    public NoInvalidDomainAttribute(string invalidDomain)
    {
        _invalidDomain = invalidDomain;
        ErrorMessage = $"邮箱不能包含域名:{invalidDomain}";
    }

    public override bool IsValid(object value)
    {
        var email = value as string;
        if (string.IsNullOrEmpty(email))
        {
            return true; // 非空验证由[Required]处理
        }
        return !email.EndsWith(_invalidDomain, StringComparison.OrdinalIgnoreCase);
    }
}

// 在请求模型中使用
public class CreateUserRequest
{
    // ... 其他属性
    [Required(ErrorMessage = "邮箱不能为空")]
    [EmailAddress(ErrorMessage = "邮箱格式错误")]
    [NoInvalidDomain("@test.com")] // 自定义验证:禁止test.com邮箱
    public string Email { get; set; }
}

11.4 内容协商与格式化器

内容协商:API 根据请求头Accept自动返回对应格式(如application/json返回 JSON,application/xml返回 XML)。

配置 XML 格式化器(默认仅支持 JSON):

// Program.cs中添加XML格式化器
builder.Services.AddControllers()
    .AddXmlSerializerFormatters(); // 启用XML序列化

// 测试:请求头添加Accept: application/xml,返回XML格式响应

11.5 API 版本控制策略

常用版本控制方式:URL 路径(如/api/v1/users)、查询参数(如/api/users?api-version=1)、请求头(如api-version: 1)。

实战案例:URL 路径版本控制

  1. 安装 NuGet 包:Microsoft.AspNetCore.Mvc.Versioning。
  2. 配置版本控制
// Program.cs
builder.Services.AddApiVersioning(options =>
{
    options.AssumeDefaultVersionWhenUnspecified = true; // 未指定版本时使用默认版本
    options.DefaultApiVersion = new ApiVersion(1, 0); // 默认版本1.0
    options.ReportApiVersions = true; // 响应头返回支持的版本
});
  • 定义多版本控制器
// V1版本控制器
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
public class UsersController : ControllerBase
{
    [HttpGet]
    public IActionResult GetUsersV1()
    {
        return Ok(new { Version = "1.0", Data = "V1版本用户列表" });
    }
}

// V2版本控制器(新增字段)
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("2.0")]
public class UsersController : ControllerBase
{
    [HttpGet]
    public IActionResult GetUsersV2()
    {
        return Ok(new { Version = "2.0", Data = "V2版本用户列表(含创建时间)" });
    }
}

访问/api/v1/users返回 V1 结果

/api/v2/users返回 V2 结果。

© 版权声明

相关文章

暂无评论

none
暂无评论...