POST 一般用于表示创建资源,新增。
[Route("{id}", Name = "GetProduct")]
public IActionResult GetProduct(int id)
{
var product = ProductService.Current.Products.SingleOrDefault(x => x.Id == id);
if (product == null)
{
return NotFound();
}
return Ok(product);
}
[HttpPost]
public IActionResult Post([FromBody] ProductCreation product)
{
//若序列化失败,则product为null
if (product == null) {
return BadRequest();
}
//查询最大的序列号
var maxId = ProductService.Product.List.Max(x => x.Id);
//创建一个新product
var NewProduct = new Product
{
Id = ++maxId,
Name = product.Name,
Price = product.Price
};
//将新产品添加进列表
ProductService.Product.List.Add(NewProduct);
//返回响应使用CreatedAtRoute
//1.能查询到新创建产品的Route
//2.匿名对象,传入查询条件
//3.新创建的对象
return CreatedAtRoute("GetProduct", new { id = maxId }, NewProduct);
}
[HttpPost]:
表示请求的谓词是Post
访问这个action需要class上的Route前缀。
[HttpPost(“create”)]:
自定义访问方式和路径。
访问这个action需要class上的Route前缀+create
[FromBody]:
请求的body里面包含着方法需要的实体数据。
方法需要把这个数据 反序列化 成 我们需要的 实体对象!
客户端程序可能发送一个Bad的Request,导致数据不能被 反序列化,此时参数值==null,我们需要相应一个BadRequest的Status Code。
返回 CreatedAtRoute
对于POST,建议的返回Status Code 为 201(Created),
可以使用 CreatedAtRoute这个内置的Helper Method。
它可以返回一个带地址Header的Respone,这个Location Header 会包含一个URI。通过这个URI可以找到我们刚刚创建的实体数据,但是这个Action必须有一个路由名字才能使用,所以GetProduct上的Route加了个名字。
Validation 验证
针对上面的方法,如果请求没有Body,参数就会是null。
若Body里面所包含的属性在product中不存在,则该属性被忽略。
但是若body属性有问题,name未填写或太长,框架会抛出500异常,但此时是客户端异常,所以我们需要搞一个400 BadRequest。
使用asp.net core 内置可以使用Data Annotations经行:
(更可以前端校验数据格式)
public class ProductCreation
{
[Display(Name = "产品名称")]
[Required(ErrorMessage = "{0}是必填项")]
// [MinLength(2, ErrorMessage = "{0}的最小长度是{1}")]
// [MaxLength(10, ErrorMessage = "{0}的长度不可以超过{1}")]
[StringLength(10, MinimumLength = 2, ErrorMessage = "{0}的长度应该不小于{2}, 不大于{1}")]
public string Name { get; set; }
[Display(Name = "价格")]
[Range(0, Double.MaxValue, ErrorMessage = "{0}的值必须大于{1}")]
public float Price { get; set; }
}
这些Data Annotation(验证的注解)
[MinLength]最小长度
[StringLength]同时验证最小和最大长度
[Range]表示数值的范围
[Display(Name=“xxx”)]给属性起名字
{0}表示Display的Name属性, {1}表示当前注解的第一个变量, {2}表示当前注解的第二个变量.
//加入模型验证
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
ModelState:
是一个Dictionary,它里面是请求提交到Action的Name和Value,一个name对应着model的一个属性,也包含了错误信息的合集
ModelState.IsValid
true则通过,false则失败
如果有错误的话, 我们可以把ModelState当作Bad Request的参数一起返回到前台.
PUT
用于对model进行完整的更新。
//HttpPut一般用于更新资源
[HttpPut("{id}")]
public IActionResult Put(int id,[FromBody]ProductModifiaction product) {
//如果请求过来的数据不是我们希望的数据结构,则返回错误
if (product == null) {
return BadRequest();
}
//查询出响应的产品
var model = ProductService.Product.List.SingleOrDefault(x => x.Id == id);
if (model == null) {
//未查到
return NotFound();
}
model.Name = product.Name;
model.Price = product.Price;
//修改后返回NoContent
return NoContent();
}
按照Http Put 的约定,需要一个id这样的参数,用于查找现有的model,由于Put的更新是完整更新!!!,所以传入整个ProducetModification来修改整个对象.
Put的建议返回是NoContent()
也可使用Ok(xxx);
若更新不完整,则设置没更新的为null
Patch部分更新
Http Patch用于做部分更新的,他的Request Body应该包含需要更新的属性名和值,甚至可以包含针对这个属性要进行的操作。
//Http Patch用于部分更新
//使用JsonPatchDocument<Dto对象>
[HttpPatch]
public IActionResult Patch(int id, [FromBody]JsonPatchDocument<ProductModifiaction> model){
//模型失败返回错误
if (model == null) {
return BadRequest();
}
var product = ProductService.Product.List.SingleOrDefault(x => x.Id == id);
//查询失败报错NotFound
if (product == null) {
return NotFound();
}
//转换模型更新,创建一个新的ProductionModification
var modify = new ProductModifiaction
{
Name = product.Name,
Description = product.Description,
Price = product.Price
};
//应用转换
model.ApplyTo(modify);
//更新资源
product.Name = modify.Name;
product.Price = modify.Price;
product.Description = modify.Description;
return NoContent();
}
HttpPatch,按照约定方法有
一个参数id
一个JsonPatchDocument<>类型的参数
泛型类型是响应的dto
使用JsonPatchDocument对象的ApplyTo方法,将更新内容作用于参数对象。
DELETE 删除
[HttpDelete("{id}")]
public IActionResult Delete(int id)
{
var model = ProductService.Current.Products.SingleOrDefault(x => x.Id == id);
if (model == null)
{
return NotFound();
}
ProductService.Current.Products.Remove(model);
return NoContent();
}