netCore WebAPI基础3

netCore WebAPI基础3

资源更新

  • put:对所有资源进行更新
  • patch:对部分资源进行更新
    put使用方法和post相同,但是put是幂等的。

patch使用

数据样式

patch的6个操作

  1. add:添加字段
  2. remove:删除字段
  3. replace:替换字段数据
  4. move:转移
  5. copy:复制
  6. test:测试

使用步骤

  1. nuget 安装Microsoft.AspNetCore.JsonPatchMicrosoft.AspNetCore.Mvc.NewtonsoftJson
  2. 更新数据的操作方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    [HttpPatch("{touristRouteId}")]
    public IActionResult PartiallyUpdateTouristRoute([FromRoute] Guid touristRouteId, [FromBody] JsonPatchDocument<TouristRouteForUpdateDto> patchDocument)
    {
    //原始数据
    var touristRouteFromRepo = _tourisTouteRepository.GetTourisRoute(touristRouteId);
    //原始数据转创建dto 需要在profile中设定mapper转换
    var touristRouteToPatch = _mapper.Map<TouristRouteForUpdateDto>(touristRouteFromRepo);
    //JsonPatch修改数据更新到dto上,打补丁,并绑定ModelState
    patchDocument.ApplyTo(touristRouteToPatch,ModelState);
    if (!TryValidateModel(touristRouteToPatch))//验证数据
    {
    return ValidationProblem(ModelState);
    }
    //JsonPatch已经修改了的更新数据映射到原始数据上
    //从 touristRouteToPatch到touristRouteFromRepo
    _mapper.Map(touristRouteToPatch, touristRouteFromRepo);//此时context中的数据已经被mapper更改
    _tourisTouteRepository.Save();
    return NoContent();
    }
  3. program
    1
    2
    3
    4
    5
    builder.Services.AddControllers(opt => {
    opt.ReturnHttpNotAcceptable = true;
    }).AddNewtonsoftJson(opt => {//注册
    opt.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
    })
  4. 前端的body数据
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    [
    {
    "op": "replace",
    "path": "/title",
    "value": "AAAAAAAAA"
    },
    {
    "op": "replace",
    "path": "/description",
    "value": "BBBB"
    }
    ]

删除

有选择的删除形式DELETE api/touristRoutes/(1,3,4,5)

  1. 控制器的操作方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    [HttpDelete("({touristIds})")]
    public IActionResult DeleteTouristRoutes([ModelBinder(BinderType = typeof(ArrayModelBinder))][FromRoute]IEnumerable<Guid> touristIds)
    {//收到touristIds(1,3,4,5)自动转换
    if (touristIds == null)
    {
    return BadRequest();
    }
    //仓库服务收到的字符串是已经处理好的
    var toruistRoutesFromRepo = _tourisTouteRepository.GetTouristRouteByIdList(touristIds);
    _tourisTouteRepository.DeleteTouristRoutes(toruistRoutesFromRepo);
    _tourisTouteRepository.Save();
    return NoContent();
    }
    2.模型绑定
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    public class ArrayModelBinder : IModelBinder
    {
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
    // 确定是否是Enumerable类型
    if (!bindingContext.ModelMetadata.IsEnumerableType)
    {
    bindingContext.Result = ModelBindingResult.Failed();
    return Task.CompletedTask;
    }

    //通过ValueProvider获得输入的值
    var value = bindingContext.ValueProvider
    .GetValue(bindingContext.ModelName).ToString();

    // 如果值为空
    if (string.IsNullOrWhiteSpace(value))
    {
    bindingContext.Result = ModelBindingResult.Success(null);
    return Task.CompletedTask;
    }

    //通过反射获得Enumerable的类型并得到一个转换器
    var elementType = bindingContext.ModelType.GetTypeInfo().GenericTypeArguments[0];
    var converter = TypeDescriptor.GetConverter(elementType);

    //使用逗号分割,并将所有的值使用转换器进行转换到一个数组中
    var values = value.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)
    .Select(x => converter.ConvertFromString(x.Trim()))
    .ToArray();

    //通过反射创建具体的数组,并赋值
    var typedValues = Array.CreateInstance(elementType, values.Length);
    values.CopyTo(typedValues, 0);
    bindingContext.Model = typedValues;

    //成功,并传递Model
    bindingContext.Result = ModelBindingResult.Success(bindingContext.Model);
    return Task.CompletedTask;
    }
    }

Post

在head中携带新插入数据的链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//控制器中获得信息的方法,定义name以供post使用
[HttpGet("{pictureId}", Name = "GetPictureForTouristRoute")]
public async Task <IActionResult> GetPictureForTouristRoute(Guid touristRouteId, int pictureId)
{
if (!await _tourisTouteRepository.TouristRouteExistsAsync(touristRouteId))
{
return NotFound("旅游线路不存在");
}
var picture = await _tourisTouteRepository.GetPictureAsync(pictureId);
if (picture == null)
{
return NotFound("照片不存在");
}
var pictureDto = _mapper.Map<TouristRoutePictureDto>(picture);
return Ok(pictureDto);
}
//post方法
[HttpPost]
public async Task<IActionResult> CreateTouristRoutePicture([FromRoute] Guid touristRouteId, [FromBody] TouristRoutePictureForCreateDto touristRoutePictureForCreationDto)
{
if (!await _tourisTouteRepository.TouristRouteExistsAsync(touristRouteId))
{
return NotFound("旅游线路不存在");
}
//从createDto转换为模型数据
var pictureModel = _mapper.Map<TouristRoutePicture>(touristRoutePictureForCreationDto);
_tourisTouteRepository.AddTouristRoutePicture(touristRouteId, pictureModel);//插入模型数据
await _tourisTouteRepository.SaveAsync();
//从模型数据转换为视图Dto
var pictureToReturn = _mapper.Map<TouristRoutePictureDto>(pictureModel);
//目的是在返回的head中携带新插入数据的链接,第一个参数为上面的name,第二个参数是上面方法需要的参数,第三个则是需要返回的信息
return CreatedAtRoute("GetPictureForTouristRoute", new { touristRouteId = pictureModel.TouristRouteId, pictureId = pictureModel.Id }, pictureToReturn);
}

作者

步步为营

发布于

2024-06-03

更新于

2025-03-15

许可协议