很多时候我们在接收到客户端提交过来的请求之前,要验证一下数据合法性再执行操作。
数据注解Data Annotations
在WepApi中可以使用System.ComponentModel.DataAnnotations命名空间的属性,在你的model类中设置属性,来实现验证。
考虑以下场景:
using System.ComponentModel.DataAnnotations;
namespace MyApi.Models
{
public class Product
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public decimal Price {get; set;}
[Range(0, 999)]
public double Weight { get; set; }
}
}
如果你在MVC中使用过模型验证,那么看起来很眼熟,[Required]特性表示Name不能为null,[Range]特性表示Weight必须在0-999范围内。
假设收到客户端POST过来的json数据:
{ "Id":4, "Price":2.99, "Weight":5 }
我们可以看到数据并没有包含Name,而Name我们是标记了[Required]特性的。当WebApi将其转换为Product实体的时候,就会发生验证冲突。
在你的Action中可以验证Model是否合法:
public class ProductsController : ApiController
{
public HttpResponseMessagePost(Product product)
{
if (ModelState.IsValid)
{
// Dosomething with the product (not shown).
returnnew HttpResponseMessage(HttpStatusCode.OK);
}
else
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
}
}
但是模型验证不能保证完全不会出现问题,所以你需要在程序的其他层中增加验证,比如在Model层增加外键关联等等(EF)。
如果你使用的是EF可以参照文章Using Web API withEntity Framework,其中介绍了很多类似的问题。
“Under-Posting”(参数缺失)
传过来的数据中参数缺失的情况,例如:
{"Id":4, "Name":"Gizmo"} 这里没有传Pirce,Weight参数,此时Json Formatter会指定默认的值;
"Over-Posting"(参数多余)
传过来的参数多
{"Id":4, "Name":"Gizmo", "Color":"Blue"}
可以看到传过来一个不存在Color属性,既然这样,formatter会忽略该值,XMLformatter也一样。
注意,当你打算将某个属性作为只读属性的时候可能有问题。
例如
public class UserProfile
{
public string Name { get; set; }
public UriBlog { get; set; }
public bool IsAdmin { get; set;} // uh-oh!
}
你不希望用户可以修改IsAdmin这个值,因为这样会篡改为管理员权限!
最好的办法就是使用DTO对象,说白了就是封装一层面向展示层的对象,将IsAdmin剔除,这样用户就不会修改该值了。
public class UserProfileDTO
{
public string Name { get; set; }
public UriBlog { get; set; }
// Leave out "IsAdmin"
}
处理验证错误
1.当验证错误的时候WebApi不会自动向客户端返回错误,而是有Action编写代码实现向客户端返回错误。
2.你也可以自己创建一个action filter去验证模型状态(model state),如果发生错误则会想客户端返回错误信息,action也不会执行。
namespace MyApi.Filters
{
public class ValidateModelAttribute: ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.ModelState.IsValid == false)
{
actionContext.Response = actionContext.Request.CreateErrorResponse(
HttpStatusCode.BadRequest,actionContext.ModelState);
}
}
}
}
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
Date: Tue, 16 Jul 2013 21:02:29 GMT
Content-Length: 331
{
"Message": "The request isinvalid.",
"ModelState": {
"product": [
"Required property'Name' not found in JSON. Path '', line 1, position 17."
],
"product.Name": [
"The Name field isrequired."
],
"product.Weight": [
"The field Weight mustbe between 0 and 999."
]
}
}
如何应用这个filter呢?需要在 HttpConfiguration.Filters 集合中添加刚才创建的filter
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new ValidateModelAttribute());
}
}
3.另外一个方式就是添加特性(简便省事儿)
public class ProductsController : ApiController
{
[ValidateModel]
public HttpResponseMessagePost(Product product)
{
// ...
}
}