WebAPI-02-2 实现WebAPI+CRUD示例

一、GetPlayerById  通过主键获取详细

1、IPlayerRepository仓储定义接口

Player? GetPlayerById(Guid playerId);

2、实现类PlayerRepository中实现,条件查询,返回可空实体类

        public Player? GetPlayerById(Guid playerId)
        {
            return FindByCondition(player => player.Id == playerId)
                .FirstOrDefault();
        }

 3、控制器,

{id}是请求传过来的/1
加Name = "PlayerById",生成路由路径
        [HttpGet("{id}", Name = "PlayerById")]
        public IActionResult GetPlayerById(Guid id)
        {
            try
            {
                var player = repository.Player.GetPlayerById(id);
                if (player is null)
                {
                    return NotFound();
                }

                var result = mapper.Map<PlayerDto>(player);
                return Ok(result);
            }
            catch (Exception ex)
            {
                logger.LogError($"{ex.Message}");
                return StatusCode(500);
            }
        }

返回实体默认是不包括导航属性的,一个玩家有多个角色

 

 

二、GetPlayerWithCharacters   要包含返回子集,要创建两个DTO

  1)创建子集角色的DTO,不包含导航

public class CharacterDto
{
    public Guid Id { get; set; }
    public string Nickname { get; set; }
    public string Classes { get; set; }
    public int Level { get; set; }
    public DateTime DateCreated { get; set; }
}

  2)创建关系WithDto,继承PlayerDto,不需要额外的外键信息,只在控制器里面转换使用

public class PlayerWithCharactersDto : PlayerDto
{
    public IEnumerable<CharacterDto> Characters { get; set; }
}

  3)映射

            CreateMap<Player, PlayerWithCharactersDto>();
            CreateMap<Character, CharacterDto>();

  4)仓储接口

        Player? GetPlayerWithCharacters(Guid playerId);

  5)实现

    Include包含查询,返回的实体还是Player,这次包含了导航子集ICollection<Character>
        public Player? GetPlayerWithCharacters(Guid playerId)
        {
            return FindByCondition(player => player.Id == playerId)
                .Include(player => player.Characters)
                .FirstOrDefault();
        }

  6)控制器,id之后又加了个导航属性 character

        [HttpGet("{id}/character")]
        public IActionResult GetPlayerWithCharacters(Guid id)
        {
            try
            {
                var player = repository.Player.GetPlayerWithCharacters(id);
                if (player is null)
                {
                    return NotFound();
                }

                var result = mapper.Map<PlayerWithCharactersDto>(player);
                return Ok(result);
            }
            catch (Exception ex)
            {
                logger.LogError(ex.Message);
                return StatusCode(500);
            }
        }

 

 

三、创建

  1、接收DTO

  id和时间自动生成

public class PlayerForCreationDto
{
    [Required(ErrorMessage = "账号不能为空")]
    [StringLength(20, ErrorMessage = "账号长度不能大于50")]
    public string Account { get; set; }

    [Required(ErrorMessage = "账号类型不能为空")]
    [StringLength(10, ErrorMessage = "账号类型不能大于10")]
    public string AccountType { get; set; }
}

  2、映射,接收Dto--->实体

CreateMap<PlayerForCreationDto, Player>();

  3、仓储接口

void CreatePlayer(Player player);

  4、实现,也可以不写,直接调用基类Create

        public void CreatePlayer(Player player)
        {
            Create(player);
        }

  5、控制器

[HttpPost]请求
请求体是复杂类型,需要使用[FromBody]来修饰,参数自动绑定,对参数进行基本验证,是否传参
ModelState.IsValid验证,控制器对传入的模型验证的结果,规则就是传入DTO里面添加的特性Required、StringLength等,同MVC视图模型
两个映射操作
1、必须使用实体才能数据库创建
2、返回创建好的完整数据
  CreatedAtRoute:返回状态码为201的响应,代表已经创建的意思
  响应体response body:中返回新创建的对象

  响应头response headers:通过前面的获取PlayerById生成一个url,再加上id参数,一起组成资源地址location参数返回到响应头里,可以直接通过地址直接打开返回的资源
[HttpPost]
public IActionResult CreatePlayer([FromBody]PlayerForCreationDto player)
{
    try
    {
        if (!ModelState.IsValid)
        {
            return BadRequest("无效的请求数据");
        }

        var playerEntity = mapper.Map<Player>(player);

        repository.Player.CreatePlayer(playerEntity);
        repository.SaveChangesAsync();

        var createdPlayer = mapper.Map<PlayerDto>(playerEntity);
        return CreatedAtRoute("PlayerById", new { id = createdPlayer.Id }, createdPlayer);
    }
    catch (Exception ex)
    {
        logger.LogError(ex.Message);
        return StatusCode(500);
    }
}

 

四、更新

1、更新DTO

  虽然现在内容和创建DTO一样,但是

  1)语义区别,让代码具有扩展性

  2)有可能存在差异,有些字段可以创建后只读,修改不支持编辑

public class PlayerForUpdateDto
{
    [Required(ErrorMessage = "账号不能为空")]
    [StringLength(20, ErrorMessage = "账号长度不能大于50")]
    public string Account { get; set; }

    [Required(ErrorMessage = "账号类型不能为空")]
    [StringLength(10, ErrorMessage = "账号类型不能大于10")]
    public string AccountType { get; set; }
}

  2、映射,接收Dto--->实体

        CreateMap<PlayerForUpdateDto, Player>();

  3、仓储接口

  4、实现

  另外一种,不创建仓储方法

  5、控制器

    1)HttpPut:代表更新

    2)参数:要更新的ID,最新的实体

    3)mapper.Map(player, playerEntity);
    直接赋值给实体,相同的值会覆盖,直接修改了实体里面的值
    然后可以直接调用基类里面的方法repository.Player.Update(playerEntity);
    可以不用创建仓储接口

    4)NoContent();  

      修改成功返回204无内容状态码  

        [HttpPut("{id}")]
        public IActionResult UpdatePlayer(Guid id, [FromBody] PlayerForUpdateDto player)
        {
            try
            {
                if (!ModelState.IsValid)
                {
                    return BadRequest("无效的请求对象");
                }

                var playerEntity = repository.Player.GetPlayerById(id);
                if (playerEntity is null)
                {
                    return NotFound("待修改的玩家不存在");
                }

                mapper.Map(player, playerEntity);

                repository.Player.UpdatePlayer(playerEntity);
                repository.SaveChangesAsync();

                return NoContent();
            }
            catch (Exception ex)
            {
                logger.LogError(ex.Message);
                return StatusCode(500);
            }
        }

 

 

 

 

 

五、删除

1、控制器

GetPlayerWithCharacters获取id详情
player.Characters.Any()判断是否有关联角色
BadRequest返回400状态码
repository.Player.Delete(player);也可以直接调用基类方法,不创建仓储方法
        [HttpDelete("{id}")]
        public IActionResult DeletePlayer(Guid id)
        {
            try
            {
                var player = repository.Player.GetPlayerWithCharacters(id);
                if (player is null)
                {
                    return BadRequest("该玩家不存在");
                }

                if (player.Characters.Any())
                {
                    return BadRequest("该玩家有关联人物角色,不能删除!");
                }

                repository.Player.DeletePlayer(player);
                repository.SaveChangesAsync();

                return NoContent();
            }
            catch (Exception ex)
            {
                logger.LogError(ex.Message);
                return StatusCode(500);
            }
        }

 

swagger/index.html

结果

 

 

posted on 2025-03-17 22:18  张彦山  阅读(39)  评论(0)    收藏  举报