如何将多个参数传递给ASP.Net Web API GET方法?

145
我正在使用 .Net MVC4 Web API 来(希望能够)实现RESTful api。我需要传入一些参数到系统中,并让它执行一些操作,然后返回对象列表作为结果。具体来说,我正在传入两个日期,并返回在它们之间的记录。我还跟踪了返回的记录,以便后续调用不会在系统中重新处理。
我考虑了几种方法:
  1. 将参数序列化为一个JSON字符串,并在API中拆分它。 http://forums.asp.net/t/1807316.aspx/1

  2. 将参数传递到查询字符串中。
    What is best way to pass multiple query parameters to a restful api?

  3. 在路由中定义参数: api/controller/date1/date2

  4. 使用POST方法,可以传递包含参数的对象。

  5. 研究ODATA,因为Web API(目前)支持它。我还没有做过太多关于这个方面的事情,所以不是很熟悉。

看起来适当的REST实践表明,在获取数据时,应该使用GET。然而,GET也应该是无副作用的(不产生副作用),我想知道我的具体实现是否违反了这一点,因为我在API系统中标记记录,因此我正在产生副作用。

它也引出了支持可变参数的问题。如果输入参数列表发生变化,如果这种情况经常发生,重新定义Choice 3的路由将很繁琐。如果参数在运行时定义,可能会发生什么......

无论如何,对于我的具体实现,哪种选择(如果有)似乎最好?

12个回答

153

我认为最简单的方法是直接使用AttributeRouting

这在你的控制器中很明显,为什么要放在全局的WebApiConfig文件中呢?

例如:

    [Route("api/YOURCONTROLLER/{paramOne}/{paramTwo}")]
    public string Get(int paramOne, int paramTwo)
    {
        return "The [Route] with multiple params worked";
    }

“{}”的名称必须与您的参数相匹配。

就这么简单,现在您有一个单独的GET处理多个参数的实例。


12
太好了。大多数人建议在 WebApiConfig 文件中设置路由,但这确实更好。 - rhyek
4
实际上,我们(大多数人)建议为您的配置设置一个集中管理区域。在 Web API 的情况下(无论是 Microsoft 还是其他),REST 的集中式模式至关重要。属性路由很可爱,但它会让人产生例外的诱惑。 - David Betz
3
同意,实际上我需要更新我的答案。用GET方法处理多个参数有一种更好的方式。当我刚开始使用WebAPI时发布了这篇文章,现在除非我不想创建新的控制器,否则我不再使用AttributeRouting,并将所有参数传递到查询字符串中,它们会自动映射。我有机会时会进行更新,以便人们不再使用这种较旧的方法。 - Mark Pieszak - Trilon.io
1
如果需要更改操作方法名称,这可以进行修改以适应。[Route(“api / YOURCONTROLLER / Get / {paramOne} / {paramTwo}”)] public string Get(int paramOne,int paramTwo) { 返回“something”; } - Dash
字符串 ParamOne,字符串 ParamTwo @NetRevolutions - Mark Pieszak - Trilon.io
显示剩余5条评论

51

只需向WebApiConfig条目中添加新路由。

例如,要调用:

public IEnumerable<SampleObject> Get(int pageNumber, int pageSize) { ..

添加:

config.Routes.MapHttpRoute(
    name: "GetPagedData",
    routeTemplate: "api/{controller}/{pageNumber}/{pageSize}"
);

然后将参数添加到HTTP调用中:

GET //<service address>/Api/Data/2/10 

13
这似乎是唯一列出所有部件的答案。我希望有人能更好地描述如何使用“api/controller?start=date1&end=date2”样式的URI。 - Hot Licks
@Hot Licks Andrew Veriga的答案在处理查询字符串参数方面效果很好。基本上,您将查询字符串名称绑定到类属性并将它们传递到方法中。您的方法将采用一个带有[FromUri]属性标记的单个类参数,并将其查询字符串参数作为其属性。 - David Peterson
太棒了。谢谢! - Hugo Nava Kopp
嗨@HotLicks和GrahamWright,你们觉得你们能回答这个问题吗?谢谢。https://stackoverflow.com/questions/57565318/net-core-apis-implement-pagination-functionality-routing-for-all - user11915641

45

我刚刚需要实现一个RESTful API,其中需要传递参数。我通过在查询字符串中以与Mark的第一个示例相同的方式传递参数来实现这一点:"api/controller?start=date1&end=date2"

在控制器中,我使用了 C#中的URL分割技巧

// uri: /api/courses
public IEnumerable<Course> Get()
{
    NameValueCollection nvc = HttpUtility.ParseQueryString(Request.RequestUri.Query);
    var system = nvc["System"];
    // BL comes here
    return _courses;
}
在我的情况下,我是通过类似以下方式的Ajax调用WebApi:
$.ajax({
        url: '/api/DbMetaData',
        type: 'GET',
        data: { system : 'My System',
                searchString: '123' },
        dataType: 'json',
        success: function (data) {
                  $.each(data, function (index, v) {
                  alert(index + ': ' + v.name);
                  });
         },
         statusCode: {
                  404: function () {
                       alert('Failed');
                       }
        }
   });

我希望这可以帮助到你...


2
我猜你没用WebApi,因为ParameterBinding可以自动地将你的查询字符串映射到api方法参数。 - emp
1
是的,更好的方法是使用像 [Route("api/DbMetaData/{system}/{searchString}")] 这样的属性,然后将参数添加到 Get(string system, string searchString) 中,然后使用 "...api/DbMetaData/mysystem/mysearchstring" 进行调用。 - Nigel Findlater
我在我的C# MVC WebApi中使用了他的示例,它运行良好。对于这个示例点赞。 - Si8

40

我在http://habrahabr.ru/post/164945/上找到了优秀的解决方案。

public class ResourceQuery
{
   public string Param1 { get; set; }
   public int OptionalParam2 { get; set; }
}

public class SampleResourceController : ApiController
{
    public SampleResourceModel Get([FromUri] ResourceQuery query)
    {
        // action
    }
}

5
这里的关键在于 [FromUri]。 - tranceporter
2
尽管这篇文章是俄语的,@tranceporter是正确的。 "FromUri" 看起来是从 URL 获取参数的好方法。另一篇有用的文章是:http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api - Greg
这是我已经做了相当长一段时间的事情,而且效果非常好!我也推荐这个解决方案。 - David Peterson
如果您调用另一个辅助方法(而不是Get),您仍然可以使用[FromUri]吗?我似乎无法让它起作用。 - jocull

10

这个记录标记的含义是什么?如果仅用于日志记录,我建议使用GET,并禁用所有缓存,因为您想记录此资源的每个查询。如果记录标记有其他用途,那么应该使用POST。用户应该知道他的操作会影响系统,而POST方法则是一种警告。


所谓标记,就是简单地跟踪已处理并返回的记录,以便后续调用不重复处理它们。在我的情况下,我只是向另一个表中插入一条记录来跟踪哪些被处理了。 - sig606
现在我已经将其实现为POST,主要是因为你所说的原因——操作发生并且消费者知道它们。此外,相对于传递不同的数据,它似乎更容易和灵活。 - sig606
@sig606:对我来说,POST是最好的选择,但您的协议似乎不安全。如果发生了什么事情,并且由于错误而未能处理客户端检索的记录,该怎么办?您将不再返回它们,客户端将失去数据。 - LukLed
现在我的API只返回已处理的记录。因此,使用者传递给API两个日期。在这两个日期之间的记录将被处理并标记。然后数据将返回给调用者。我想,如果在处理过程中或在到达客户端之前处理完成后发生了什么问题,那么我就有麻烦了。 - sig606

9

使用GETPOST方法已经被@LukLed清楚地解释过了。关于传递参数的方式,我建议采用第二种方法(我对ODATA不是很了解)。

1.将参数序列化为一个 JSON 字符串,并在 API 中进行拆分。 http://forums.asp.net/t/1807316.aspx/1

这种方法不太用户友好,也不太SEO 友好

2.在查询字符串中传递参数。What is best way to pass multiple query parameters to a restful api?

这通常是首选的方法。

3.在路由中定义参数: api/controller/date1/date2

这绝对不是一个好方法。这会让人们误以为date2date1的子资源,实际上并不是这样。 date1date2都是查询参数,应该处于同一级别。

在简单的情况下,我建议使用这样的 URI:

api/controller?start=date1&end=date2

但我个人比较喜欢下面的URI模式,但在这种情况下,我们需要编写一些自定义代码来映射参数。

api/controller/date1,date2

实际上,那些是我的原始解释。我认为LukLed优化了我的标签和URL链接。 - sig606
就SEO而言,在这种情况下不适用。这段代码将是“服务器对服务器”的,因此我不关心外部世界是否发现它。事实上,我必须确保采取适当的安全措施以避免随机访问。我已经为系统的另一部分进行了JSON序列化(似乎在尝试POST大型对象列表时存在错误,因此我必须序列化为字符串),因此在这种情况下不会有太大的压力。 - sig606
1
希望你已经有了答案,那么为什么还要问问题呢? - VJAI
2
很抱歉回复晚了,马克。我尝试了几种解决方案,但不确定哪个是最好的,并且一直在努力遵循行业标准的方法,所以我在此向SO(Stack Overflow)发帖求助。 - sig606
@Mark,你能具体说明需要编写什么类型的代码来映射参数吗? - RredCat
1
@Mark 类似于这样的东西:https://dev59.com/n2kw5IYBdhLWcg3wu9Fg? - RredCat


4
 [Route("api/controller/{one}/{two}")]
    public string Get(int One, int Two)
    {
        return "both params of the root link({one},{two}) and Get function parameters (one, two)  should be same ";
    }

根链接的两个参数({one},{two})和Get函数的参数(one, two)应该相同。


2
现在,您可以通过简单地使用以下方法来实现这一点:
        public string Get(int id, int abc)
        {
            return "value: " + id + "  " + abc;
        }

这将返回:"value: 5 10"

如果您使用https://yourdomain/api/yourcontroller?id=5&abc=10进行调用


2

我知道这已经很老了,但最近我也想要同样的东西,下面是我找到的...

最初的回答:

    public HttpResponseMessage Get([FromUri] string var, [FromUri] string test) {
        var retStr = new HttpResponseMessage(HttpStatusCode.OK);
        if (var.ToLower() == "getnew" && test.ToLower() == "test") {
            retStr.Content = new StringContent("Found Test", System.Text.Encoding.UTF8, "text/plain");
        } else {
            retStr.Content = new StringContent("Couldn't Find that test", System.Text.Encoding.UTF8, "text/plain");
        }

        return retStr;
    }

现在在您的地址/URI/...

http(s)://myURL/api/myController/?var=getnew&test=test

结果: "找到测试"


http(s)://myURL/api/myController/?var=getnew&test=anything

结果: "无法找到该测试"


我个人喜欢C#中的这种风格,因为我可以更改原始方法的签名,并重载我想要完成的内容,而无需调整路由配置。希望能帮助那些习惯于使用这种(也许有点过时)方法来进行GET请求的人们。 - Rick Riggs
1
我不得不创建一个事件API,被第三方日历应用程序所使用,它使用了这种方法。我很高兴找到了这个答案! - clayRay

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