《ASP.ENT Core 与 RESTful API 开发实战》-- (第4章)-- 读书笔记(下)
第 4 章 资源操作
4.5 创建资源
由于创建资源的 Id 会在服务端生成,因此在创建资源时,不建议使用与获取数据时相同的 DTO,而要单独创建一个新的 DTO 类,并通过数据注解特性对相应 的属性做限制
namespace Library.API.Models
{
public class AuthorForCreationDto
{
[Required(ErrorMessage = "必须提供姓名")]
[MaxLength(20, ErrorMessage = "姓名的最大长度为20个字符")]
public string Name { get; set; }
public int Age { get; set; }
[EmailAddress(ErrorMessage = "邮箱的格式不正确")]
public string Email { get; set; }
}
}
在 IAuthorRepository 添加用于资源添加的方法
void AddAuthor(AuthorDto author);
并在 AuthorMockRepository 中实现
public void AddAuthor(AuthorDto author)
{
author.Id = Guid.NewGuid();
LibraryMockData.Current.Authors.Add(author);
}
接着在 AuthorController 中添加用于创建 Author 的 Action
[HttpPost]
public IActionResult CreateAuthor(AuthorForCreationDto authorForCreationDto)
{
var authorDto = new AuthorDto
{
Name = authorForCreationDto.Name,
Age = authorForCreationDto.Age,
Email = authorForCreationDto.Email
};
AuthorRepository.AddAuthor(authorDto);
// 返回201 Created 状态码,并在响应消息头中包含 Location 项,它的值是新创建资源的 URL
// 第一个参数是要调用 Action 的路由名称
// 第二个参数是包含要调用 Action 所需要参数的匿名对象
// 最后一个参数是代表添加成功后的资源本身
return CreatedAtRoute(nameof(GetAuthor), new {authorId = authorDto.Id}, authorDto);
}
由于 CreatedAtRoute 方法要生成指向 GetAuthor 方法的 URL,因此还需要为这个 Action 定义一个路由名称
[HttpGet("{authorId}", Name = nameof(GetAuthor))]
public ActionResult<AuthorDto> GetAuthor(Guid authorId)
创建子级资源,创建 DTO,在 IBookRepository 中接口添加方法,在 BookMockRepository 中实现类实现接口方法
namespace Library.API.Models
{
public class BookForCreationDto
{
public string Title { get; set; }
public string Description { get; set; }
public int Pages { get; set; }
}
}
void AddBook(BookDto book);
public void AddBook(BookDto book)
{
LibraryMockData.Current.Books.Add(book);
}
在 BookController 添加 Action
[HttpPost]
public IActionResult AddBook(Guid authorId, BookForCreationDto bookForCreationDto)
{
if (!AuthorRepository.IsAuthorExists(authorId))
{
return NotFound();
}
var newBook = new BookDto
{
Id = Guid.NewGuid(),
Title = bookForCreationDto.Title,
Description = bookForCreationDto.Description,
Pages = bookForCreationDto.Pages,
AuthorId = authorId
};
BookRepository.AddBook(newBook);
return CreatedAtRoute(nameof(GetBook), new {authorId = authorId, bookId = newBook.Id}, newBook);
}
同样为 GetBook 方法指定路由名称
[HttpGet("{bookId}", Name = nameof(GetBook))]
public ActionResult<BookDto> GetBook(Guid authorId, Guid bookId)
4.6 删除资源
在 IBookRepository 中接口添加方法,在 BookMockRepository 中实现类实现接口方法
void DeleteBook(BookDto book);
public void DeleteBook(BookDto book)
{
LibraryMockData.Current.Books.Remove(book);
}
在 BookController 添加 Action
[HttpDelete("{bookID}")]
public IActionResult DeleteBook(Guid authorId, Guid bookId)
{
if (!AuthorRepository.IsAuthorExists(authorId))
{
return NotFound();
}
var book = BookRepository.GetBookForAuthor(authorId, bookId);
if (book == null)
{
return NotFound();
}
BookRepository.DeleteBook(book);
return NoContent();
}
删除父与子,当删除一个父级资源,所有相关子级资源也一同删除
在 IAuthorRepository 中接口添加方法,在 AuthorMockRepository 中实现类实现接口方法
void DeleteAuthor(AuthorDto author);
public void DeleteAuthor(AuthorDto author)
{
LibraryMockData.Current.Books.RemoveAll(book => book.AuthorId == author.Id);
LibraryMockData.Current.Authors.Remove(author);
}
4.7 更新资源
从 HTTP 方法的角度来看,更新资源有两种情况:
- 整体更新,PUT 方法完成
- 部分更新,PATCH 方法完成
整体更新,创建 DTO,在 IBookRepository 中接口添加方法,在 BookMockRepository 中实现类实现接口方法
namespace Library.API.Models
{
public class BookForUpdateDto
{
public string Title { get; set; }
public string Description { get; set; }
public int Pages { get; set; }
}
}
void UpdateBook(Guid authorId, Guid bookId, BookForUpdateDto book);
public void UpdateBook(Guid authorId, Guid bookId, BookForUpdateDto book)
{
var originalBook = GetBookForAuthor(authorId, bookId);
originalBook.Title = book.Title;
originalBook.Pages = book.Pages;
originalBook.Description = book.Description;
}
在 BookController 添加 Action
[HttpPut("{bookId}")]
public IActionResult UpdateBook(Guid authorId, Guid bookId, BookForUpdateDto updateBook)
{
if (!AuthorRepository.IsAuthorExists(authorId))
{
return NotFound();
}
var book = BookRepository.GetBookForAuthor(authorId, bookId);
if (book == null)
{
return NotFound();
}
BookRepository.UpdateBook(authorId, bookId, updateBook);
return NoContent();
}
部分更新,PATCH 方法的请求正文使用的是 JSON Patch 文档格式
文档由一个数组构成,数组中的每个元素代表一个更改项,每一项包括3项:
- op:操作类型
- path:对象的属性名
- value:对象的值
op 的值包括以下6种:
- add
- remove
- replace
- copy
- move
- test
因此以下内容会更新图书资源的 Title 属性,并清空 Description 属性
[
{
"op": "replace",
"path": "/title",
"value": "Book 1 - Updated"
},
{
"op": "remove",
"path": "/description"
}
]
在 BookController 添加 Action
[HttpPatch("{bookId}")]
public IActionResult PartiallyUpdateBook(Guid authorId, Guid bookId, JsonPatchDocument<BookForUpdateDto> patchDocument)
{
if (!AuthorRepository.IsAuthorExists(authorId))
{
return NotFound();
}
var book = BookRepository.GetBookForAuthor(authorId, bookId);
if (book == null)
{
return NotFound();
}
var bookToPatch = new BookForUpdateDto
{
Title = book.Title,
Description = book.Description,
Pages = book.Pages
};
patchDocument.ApplyTo(bookToPatch, ModelState);
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
BookRepository.UpdateBook(authorId, bookId, bookToPatch);
return NoContent();
}
第三个参数类型为 JsonPatchDocument,它的值会从请求信息的正文中获取
ApplyTo 方法将相应的修改操作应用到新建的对象上,并将可能出现的错误记录到 ModelStateDictionary 中,使用该方法需要添加引用
dotnet add package Microsoft.AspNetCore.Mvc.NewtonsoftJson
4.8 内容协商
RESTful API 应该根据客户端的需要返回不同格式的数据
客户端指明格式是在其请求消息的消息头中添加 Accept 项,它的值是一个 MIME 类型,如 application/xml
如果支持返回此格式数据则直接返回,不支持则返回 406 NotAcceptable 状态码
以上过程称为内容协商
ASP.NET Core MVC 中,对于不支持的 Accept 类型返回 406 NotAcceptable 这一配置项默认为 false,因此它会返回默认格式,可以在 ConfigureService 方法中添加 MVC 服务时配置
services.AddMvc(configure =>
{
configure.ReturnHttpNotAcceptable = true;
});
Formatter 是 ASP.NET Core 中用于处理数据输出或输入格式的组件,它分为两类:输出 Formatter 和输入 Formatter
前者满足 HTTP 请求消息头的 Accept 项,后者匹配 HTTP 请求消息头的 Content-Type 项
要使服务器能够返回 XML 格式的数据,只要将能够输出 XML 格式数据的 Formatter 添加到输出 Formatter 集合中即可
services.AddMvc(configure =>
{
configure.ReturnHttpNotAcceptable = true;
configure.OutputFormatters.Add(new XmlSerializerOutputFormatter());
});
services.AddMvc() 方法返回 IMvcBuilder 接口,可以直接调用扩展方法将 XML 格式数据输入输出都添加进来
services.AddMvc(configure =>
{
configure.ReturnHttpNotAcceptable = true;
//configure.OutputFormatters.Add(new XmlSerializerOutputFormatter());
}).AddXmlSerializerFormatters();
对于特殊格式数据,需要创建自定义 Formatter,继承自 TextOutputFormatter 类或 TextInputFormatter 类


本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
欢迎转载、使用、重新发布,但务必保留文章署名 郑子铭 (包含链接: http://www.cnblogs.com/MingsonZheng/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。
如有任何疑问,请与我联系 (MingsonZheng@outlook.com) 。
《ASP.ENT Core 与 RESTful API 开发实战》-- (第4章)-- 读书笔记(下)的更多相关文章
- 使用ASP.NET Core构建RESTful API的技术指南
译者荐语:利用周末的时间,本人拜读了长沙.NET技术社区翻译的技术标准<微软RESTFul API指南>,打算按照步骤写一个完整的教程,后来无意中看到了这篇文章,与我要写的主题有不少相似之 ...
- 4类Storage方案(AS开发实战第四章学习笔记)
4.1 共享参数SharedPreferences SharedPreferences按照key-value对的方式把数据保存在配置文件中,该配置文件符合XML规范,文件路径是/data/data/应 ...
- 菜单Menu(AS开发实战第四章学习笔记)
4.5 菜单Menu Android的菜单主要分两种,一种是选项菜单OptionMenu,通过按菜单键或点击事件触发,另一种是上下文菜单ContextMenu,通过长按事件触发.页面的布局文件放在re ...
- [Android]《Android艺术开发探索》第一章读书笔记
1. 典型情况下生命周期分析 (1)一般情况下,当当前Activity从不可见重新变为可见状态时,onRestart方法就会被调用. (2)当用户打开新的Activity或者切换到桌面的时候,回调如下 ...
- 温故知新,使用ASP.NET Core创建Web API,永远第一次
ASP.NET Core简介 ASP.NET Core是一个跨平台的高性能开源框架,用于生成启用云且连接Internet的新式应用. 使用ASP.NET Core,您可以: 生成Web应用和服务.物联 ...
- 快读《ASP.NET Core技术内幕与项目实战》WebApi3.1:WebApi最佳实践
本节内容,涉及到6.1-6.6(P155-182),以WebApi说明为主.主要NuGet包:无 一.创建WebApi的最佳实践,综合了RPC和Restful两种风格的特点 1 //定义Person类 ...
- 零基础ASP.NET Core WebAPI团队协作开发
零基础ASP.NET Core WebAPI团队协作开发 相信大家对“前后端分离”和“微服务”这两个词应该是耳熟能详了.网上也有很多介绍这方面的文章,写的都很好.我这里提这个是因为接下来我要分享的内容 ...
- ASP.NET Core WebApi构建API接口服务实战演练
一.ASP.NET Core WebApi课程介绍 人生苦短,我用.NET Core!提到Api接口,一般会想到以前用到的WebService和WCF服务,这三个技术都是用来创建服务接口,只不过Web ...
- 从 0 使用 SpringBoot MyBatis MySQL Redis Elasticsearch打造企业级 RESTful API 项目实战
大家好!这是一门付费视频课程.新课优惠价 699 元,折合每小时 9 元左右,需要朋友的联系爱学啊客服 QQ:3469271680:我们每课程是明码标价的,因为如果售价为现在的 2 倍,然后打 5 折 ...
- Asp.Net Core 5 REST API - Step by Step
翻译自 Mohamad Lawand 2021年1月19日的文章 <Asp.Net Core 5 Rest API Step by Step> [1] 在本文中,我们将创建一个简单的 As ...
随机推荐
- 3 分钟将免费无限制的 Claude 2.0 接入任意 GPT 套壳应用,太香了!
Claude 是 ChatGPT 的最强竞争对手,由 OpenAI 早期团队成员创建,目标就是打造出能 赶超 ChatGPT 的 AI.最新版的 Claude 2.0,能力已经开始领先 ChatGPT ...
- 如何在vim创建的脚本内添加固定的头部信息
编辑以下的文件: vim /etc/vimrc 在脚本的结尾添加如下内容: autocmd BufNewFile *.sh,*.script exec ":call WESTOS()&quo ...
- mysql too many connections 解决
本文为博主原创,转载请注明出处: 由于在开发过程中,很多人连接共同一个数据库,在工具连接到mysql, 并执行sql时,提示 too many connections ,这是由于数据库连接太多,以致于 ...
- @JsonIgnore 失效没起作用及 @JSONField(serialize = false)
项目中需要对接口返回的某一个字段进行屏蔽,返回给前端响应的时候,不显示某个字段. 第一时间想到在实体类屏蔽的属性字段上添加@JsonIgnore注解,但添加之后并没有起作用. 在网上搜索了下,使用 @ ...
- [转帖]Nginx内置变量以及日志格式变量参数详解
https://www.cnblogs.com/wajika/p/6426270.html $args #请求中的参数值 $query_string #同 $args $arg_NAME #GET请求 ...
- [转帖]SMEMBERS:获取集合包含的所有元素
https://www.bookstack.cn/read/redisguide/spilt.4.291fab46a3b4f05c.md SMEMBERS set 以下代码展示了如何使用 SMEMBE ...
- [转帖]Shell脚本数组(实现冒泡排序,直接选择排序,反转排序)
目录 数组 数组定义方法 数组包括的数据类型 获取数组长度 读取某下标赋值 数组遍历 数组切片 数组替换 删除数组 追加数组中的元素 从函数返回数组 加法传参运算 乘法传参运算 数组排序算法 冒泡排序 ...
- Python学习之三: 编译二进制
Python学习之三: 编译二进制 摘要 每次使用python 执行py文件其实是比较麻烦的 主要是还得安装python的虚拟机,以及安装对应的pip包. 感觉比较繁杂 理论上最快捷的方式是编译成 二 ...
- 每日一库:GORM简介
GORM(Go Object-Relational Mapping)是一个用于Go语言的ORM库,它提供了一种简单.优雅的方式来操作数据库.GORM支持多种数据库,包括MySQL.PostgreSQL ...
- 自定义httpServletRequestWrapper导致上传文件请求参数丢失
问题背景 项目是 SpringBoot 单体式,在项目中,为了实现调用 controller 请求的日志记录功能.因此做了以下配置: 创建自定义拦截器 LogInterceptor; 因为需要使用到流 ...