2016-08-24 98 views
0

数组在理想情况下,我想有以下格式的URL:宁静的网络API参数为INT

/api/categories/1,2,3...N/products 

,这将返回所有产品为指定的类别。使用多个类别ID进行一次API调用可以节省多次数据库调用,从而提高性能。

我可以通过以下方式轻松实现此目的。

public HttpResponseMessage GetProducts(string categoryIdsCsv) 
{ 
    // <1> Split and parse categoryIdsCsv 
    // <2> Get products 
} 

但是,这看起来不像干净整洁的解决方案,并可能违反SRP原则。我也尝试使用ModelBinder,但它将参数添加到查询字符串。

问题:

  1. 有没有实现这样的URL结构的清洁方法是什么?
  2. 还是有不同/更好的方法来检索所有产品的多个类别?

如果您需要进一步澄清,请让我知道。

+0

这有什么错'/ API /产品?类别[] = 1&类别[] = 2&...' ?这是这种事情的标准方式。此外,Web API 2会自动将这些查询参数解析到控制器方法参数'int [] categories'中,因此不需要定制'ModelBinder'。 –

回答

1

我刚刚找到了我的问题的答案。 Route属性在使用ModelBinder时缺少参数。

[Route("api/categories/{categoryIds}/products")] 
public HttpResponseMessage GetProducts([ModelBinder(typeof(CategoryIdsModelBinder))] CategoryIds categoryIds) 
{ 
    // <2> Get products using categoryIds.Ids 
} 

而且CategoryIds

public class CategoryIds 
{ 
    public List<int> Ids{ get; set; } 
} 

而且CategoryIdsModelBinder

public class CategoryIdsModelBinder : IModelBinder 
    { 
     public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext) 
     { 
      if (bindingContext.ModelType != typeof(CategoryIds)) 
      { 
       return false; 
      } 

      var val = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); 
      if (val == null) 
      { 
       return false; 
      } 

      var key = val.RawValue as string; 
      if (key == null) 
      { 
       bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Wrong value type"); 
       return false; 
      } 

      var values = val.AttemptedValue.Split(','); 
      var ids = new List<int>(); 
      foreach (var value in values) 
      { 
       int intValue; 
       int.TryParse(value.Trim(), out intValue); 
       if (intValue > 0) 
       { 
        ids.Add(intValue); 
       } 
      } 

      if (ids.Count > 0) 
      { 
       var result = new CategoryIds 
       { 
        Ids= ids 
       }; 

       bindingContext.Model = result; 
       return true; 
      } 

      bindingContext.ModelState.AddModelError(
       bindingContext.ModelName, "Cannot convert value to Location"); 
      return false; 
     } 
0

我们可以用邮方法

[RoutePrefix( “API /类”) 公共类的TestController { [HttpPost] [路线( “的getProducts”)

public HttpResponseMessage GetProducts (HttpRequestMessage request) 
    { 
     HttpResponseMessage message    = null; 
     string    input     = string.Empty; 

     input = request.Content.ReadAsStringAsync().Result; 
     var ids = Newtonsoft.Json.JsonConvert.DeserializeObject<List<string>> (input); 

    } 
} 
+0

谢谢,但我宁愿使用'GET'来保持API的安宁。我需要获取数据(只读)。 – Ignas

+0

POST不适用于检索数据。根据Http规范,POST方法用于将在服务器上创建新资源的操作,并且此新资源将具有新的唯一ID。 GET方法是当您执行服务器上的数据查询,并且不会更改该查询的任何资源的状态 – Andrew

0

不幸Web API无法将数据解析为数组或作为开箱即用的某种自定义对象。

如果要分析您的网址PARAM作为数组,你可以试着这样做:

  1. 写自己的路由约束,这将读取并从字符串转换您的参数去整数/字符串/数组什么;

  2. 编写您的自定义类型转换器并将其与您的数据模型一起使用;

  3. 写你的价值供应商,并与您的数据模型,用它

  4. 使用参数绑定

而且你总是可以使用查询参数这是从来没有将打破REST的原则:)

请参阅有关herehere的更多信息

H ope,帮助

+0

谢谢!我刚刚发布了一个解决方案,并发现了第二个解决方案 - 使用ActionFilterAttribute,我甚至可以使用List ID作为控制器中的参数。 – Ignas

+1

请注意此属性 - 它将在每个请求/响应流程上执行 – Andrew