C# 属性将值路由到模型/FromBody参数中
在WebAPI(2)中使用属性路由时,我希望能够将路由参数从URL自动获取到模型参数中。这样做的原因是,我的验证在到达操作之前在过滤器中执行,没有它,附加信息就不容易获得 考虑以下简化示例:C# 属性将值路由到模型/FromBody参数中,c#,asp.net-web-api,asp.net-web-api2,model-binding,C#,Asp.net Web Api,Asp.net Web Api2,Model Binding,在WebAPI(2)中使用属性路由时,我希望能够将路由参数从URL自动获取到模型参数中。这样做的原因是,我的验证在到达操作之前在过滤器中执行,没有它,附加信息就不容易获得 考虑以下简化示例: public class UpdateProductModel { public int ProductId { get; set; } public string Name { get; set; } } public class ProductsController : ApiCont
public class UpdateProductModel
{
public int ProductId { get; set; }
public string Name { get; set; }
}
public class ProductsController : ApiController
{
[HttpPost, Route("/api/Products/{productId:int}")]
public void UpdateProduct(int productId, UpdateProductModel model)
{
// model.ProductId should == productId, but is default (0)
}
}
要发布到此的示例代码:
$.ajax({
url:“/api/Products/5”,
键入:“POST”,
数据:{
名称:“新名称”//NB:数据中没有ProductId
}
});
我希望模型中的ProductId
字段在输入操作方法之前从路由参数填充(即,它将可供我的验证器使用)
我不确定我需要在这里尝试和覆盖模型绑定过程的哪一部分-我认为是处理[FromBody]
部分的位(在本例中是模型参数)
在操作本身(例如,
model.ProductId=ProductId
)中设置此选项是不可接受的,因为我需要在它到达操作之前设置此选项。我没有看到任何问题。我能够从Uri中看到productId
我在Postman中使用POST-to-Uri:和json请求尝试了它:
{
"name": "Testing Prodcu"
}
参考本文
模型活页夹
比类型转换器更灵活的选项是创建自定义
模型活页夹。使用模型活页夹,您可以访问
HTTP请求、操作描述和来自
路线数据
要创建模型绑定器,请实现IModelBinder
界面
这里是一个用于UpdateProductModel
对象的模型绑定器,该对象将尝试提取路由值,并使用找到的任何匹配属性对模型进行水合物化
public类UpdateProductModelBinder:IModelBinder{
public bool BindModel(System.Web.Http.Controllers.HttpActionContext actionContext,ModelBindingContext bindingContext){
如果(!typeof(UpdateProductModel).IsAssignableFrom(bindingContext.ModelType)){
返回false;
}
//获取主体的内容并将其转换为模型
对象模型=null;
if(actionContext.Request.Content!=null)
model=actionContext.Request.Content.ReadAsAsync(bindingContext.ModelType).Result;
模型=模型??bindingContext.model
??Activator.CreateInstance(bindingContext.ModelType);
//检查路由或查询字符串中提供的值
//用于匹配属性,并在模型上设置它们。
//注意:这将覆盖已设置的任何现有值。
foreach(bindingContext.PropertyMetadata中的var属性){
var valueProvider=bindingContext.valueProvider.GetValue(property.Key);
if(valueProvider!=null){
var value=valueProvider.ConvertTo(property.value.ModelType);
var pInfo=bindingContext.ModelType.GetProperty(property.Key);
SetValue(模型、值、新对象[]{});
}
}
bindingContext.Model=Model;
返回true;
}
}
设置模型活页夹
有几种方法可以设置模型绑定器。首先,您可以添加一个
[ModelBinder]
参数的属性
public httpresponsemessageupdateproduct(int-productId,[ModelBinder(typeof(UpdateProductModelBinder))]UpdateProductModel模型)
您还可以向类型添加[ModelBinder]
属性。Web API将
对该类型的所有参数使用指定的模型绑定器
[ModelBinder(typeof(UpdateProductModelBinder))]
公共类UpdateProductModel{
public int ProductId{get;set;}
公共字符串名称{get;set;}
}
给出了以下使用上述模型和ModelBinder的简化示例
公共类产品控制器:ApiController{
[HttpPost,路由(“api/Products/{productId:int}”)]
公共IHttpActionResult UpdateProduct(int-productId,UpdateProductModel){
if(model==null)返回NotFound();
if(model.ProductId!=ProductId)返回NotFound();
返回Ok();
}
}
以下集成测试用于确认所需的功能
[TestClass]
公共类AttributeRoutingValuesTests{
[测试方法]
公共异步任务属性\u路由\u Url中的值\u应\u绑定\u参数\u FromBody(){
var config=新的HttpConfiguration();
config.maphttpAttribute路由();
使用(var server=newhttptestserver(config)){
var client=server.CreateClient();
字符串url=”http://localhost/api/Products/5";
var数据=新的UpdateProductModel{
Name=“New Name”//NB:数据中没有ProductId
};
使用(var response=wait client.PostAsJsonAsync(url,数据)){
AreEqual(HttpStatusCode.OK,response.StatusCode);
}
}
}
}
如果不想创建自定义参数绑定器,可能需要考虑不要将FromBody与FromUrl混合。而是完全使用FromUrl
[HttpPost, Route("/api/Products/{productId:int}/{name:string}")]
public void UpdateProduct([FromUri]UpdateProductModel model)
{
}
[HttpPost, Route("/api/Products")]
public void UpdateProduct([FromBody]UpdateProductModel model)
{
}
或者完全使用FromBody
[HttpPost, Route("/api/Products/{productId:int}/{name:string}")]
public void UpdateProduct([FromUri]UpdateProductModel model)
{
}
[HttpPost, Route("/api/Products")]
public void UpdateProduct([FromBody]UpdateProductModel model)
{
}
并相应地更新javascript,因为这是一个更新,所以应该是HttpPut。PUT动词是幂等的,因此对API的任何后续请求(相同json请求)都应该具有相同的响应/效果(服务器端不创建任何资源)。应在调用客户端中设置模型productId
public class ProductsController : ApiController
{
[HttpPut, Route("/api/Products/{productId:int}")]
public void UpdateProduct(UpdateProductModel model)
{
if (ModelState.IsValid)
{
//
}
else
{
BadRequest();
}
}
}
您没有注意到这一点-我的模型中有一个ProductId字段,我希望从路由值填充该字段-验证器在操作方法之前运行,并且只能访问该模型。您可以通过使用自定义
IModelBinder
检查路由数据以匹配模型上的属性来实现这一点。我希望