如何将整数列表发送到Web API 2的GET请求?

9

我正在尝试完成一个任务,需要向Web API 2发送id(整数)列表的Get请求。

所以我找到了一些示例这里,它甚至有一个示例项目,但它不起作用...

这是我的Web API方法代码:

[HttpGet]
[Route("api/NewHotelData/{ids}")]
public HttpResponseMessage Get([FromUri] List<int> ids)
{
    // ids.Count is 0
    // ids is empty...
}

以下是我在Fiddler中测试的URL:

http://192.168.9.43/api/NewHotelData/?ids=1,2,3,4

但是列表总是空的,没有任何ID传递到方法中。

我似乎无法理解问题是在方法中、URL中还是两者都有问题......

那么如何实现这个目标呢?


你需要使用自定义模型绑定器才能使其正常工作。 - Aleksey L.
这个回答怎么被标记为正确答案的?(参考链接:http://forums.asp.net/t/2010247.aspx?Send+string+array+to+web+api) - Liran Friedman
你能否演示一下如何在我的情况下使用这个模型绑定器? - Liran Friedman
可能是重复问题:http://stackoverflow.com/questions/32336719/binding-a-comma-separated-list-to-a-listint-in-asp-net-web-api - Jan Kalfus
一个解决方法是实际上将其作为POST而不是get-我知道这与它们的用途背道而驰。 - Flexicoder
5个回答

12

你需要自定义模型绑定程序才能使其正常工作。这是一个简化版本,供您开始使用:

您需要自定义模型绑定器才能让它起作用。这是一个简化版本,您可以开始使用:

public class CsvIntModelBinder : IModelBinder
{
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        var key = bindingContext.ModelName;
        var valueProviderResult = bindingContext.ValueProvider.GetValue(key);
        if (valueProviderResult == null)
        {
            return false;
        }

        var attemptedValue = valueProviderResult.AttemptedValue;
        if (attemptedValue != null)
        {
            var list = attemptedValue.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries).
                       Select(v => int.Parse(v.Trim())).ToList();

            bindingContext.Model = list;
        }
        else
        {
            bindingContext.Model = new List<int>();
        }
        return true;
    }
}

这是使用方法(从路由中删除{ids}):

[HttpGet]
[Route("api/NewHotelData")]
public HttpResponseMessage Get([ModelBinder(typeof(CsvIntModelBinder))] List<int> ids)

如果您想在路由中保留{ids},则应将客户端请求更改为:

如果你希望路由中保留 {ids} ,那么你需要更改客户端的请求为:

api/NewHotelData/1,2,3,4

另一个选项(不使用自定义模型绑定器)是将get请求更改为:


?ids=1&ids=2&ids=3

这是正确的,但不要从路由中删除 {ids}。否则它将无法工作。 - Liran Friedman
如果您按照请求?ids=1,2,3,4使用它,它就能正常工作。 - Aleksey L.

9

根据评论中的建议,使用自定义模型绑定器是正确的方法。然而,您也可以像下面这样快速且不太规范地完成:

[HttpGet]
[Route("api/NewHotelData")]
public HttpResponseMessage Get([FromUri] string ids)
{
    var separated = ids.Split(new char[] { ',' });
    List<int> parsed = separated.Select(s => int.Parse(s)).ToList();
}

首先,我将分割uri中的ids字符串,然后使用Linq将它们转换为整数列表。请注意,这里缺少合理性检查,如果参数格式不正确,则会抛出异常。

您可以像这样调用它:http://192.168.9.43/api/NewHotelData?ids=5,10,20

更新:个人认为,对于这样一个简单的事情,使用模型绑定器是过度设计。你需要很多代码才能让这个简单的东西工作。在模型绑定器中使用的代码实际上非常相似,你只需要获得更好的语法方法的参数。如果将整数解析包装在try-catch块中,并在格式错误的情况下返回适当的错误消息,我不认为有什么理由不使用这种方法。


谢谢,但我熟悉这种方法。如果您能提供一个使用模型绑定器解决此问题的示例,那就太好了 :-) - Liran Friedman
你说得没错,我也同意你的观点,但这是我的要求,我需要按照指示去做 :-) - Liran Friedman
1
当然,这是你的决定 :) - Jan Kalfus

1
[HttpGet]
[Route("api/getsomething")]
public HttpResponseMessage Get([FromUri] params int[] ids)
{
}

用法:http GET localhost: 9000 / api / get something?ids = 1&ids = 5&ids = 9


这个方法是可行的,但我的需求是不使用方法名称调用该方法,像这样:localhost:9000/api/?ids=1&ids=5&ids=9,但这种方式不起作用... - Liran Friedman

1
显然,这个可以直接使用:
http://192.168.9.43/api/NewHotelData/?ids=1&ids=2&ids=3&ids=4

重复参数名称并使用不同的值。但是,如果这些ID变得非常大,并且您必须包含许多ID,则可能会使URL过长,那么会发生什么?
我认为最好只需将其作为POST请求完成即可。您写道您需要该请求为GET而不是POST,但为什么?在AJAX请求的上下文中,使用POST来检索数据完全可以接受。

@LiranFriedman 好的,但是重复参数名称是否有效?这才是最重要的,其他都只是我的个人想法。 - s.m.
我不确定,因为到目前为止没有任何值传递到服务器。 - Liran Friedman
1
理论上,你不应该使用POST来获取数据作为你的主要目标。POST用于创建实体,PUT用于更新,而GET用于获取。当然,你可以用这种方式逃避,但最终你所做的是违反协议的设计。通常,PUT和POST的“获取”部分涉及获取新创建/更新的实体。我并不是HTTP协议的专家,但这是普遍共识(据我所知)。 - gEdringer

1
这可能有些俗气,但也可以这样做:
在你的.NET类(模型)中:
public class TackyList
{
     public IEnumerable<int> myIntList {get; set;}
}

在客户端,您需要执行一个post请求,例如:{myIntList: [4,2,0]}

现在,您的控制器上的操作/方法应该类似于:

public void myApiMethodThatDoesSomethingWith(TackyList tl)
{
   // here you should be able to do something like:
   if(tl != null && tl.Count > 0) // blah
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接