【.com快译】众所周知,我们API设计的目标往往是通过我们的服务为用户提供某些功能。虽然HTTP和URL资源都允许与数据流进行某种程度的基本交互,但当涉及到其他特定要求时,它们通常会使您的API不堪重负。在这里,我们以分页为例,即如果某个数据库中存储了数百万篇文章,那么我们很可能无法将每篇文章都在一次响应中发送给客户端。针对这样的需求,业界提出了参数化的解决方案。什么是参数化?一般来说,参数化是一种请求配置。在编程语言中,我们可以通过函数请求相应的返回值。如果一个函数没有参数,那么我们就不能直接影响它的返回值。API也是如此,尤其是那些无状态的RESTAPI。毕竟,所有REST交互都是无状态的。也就是说,每个请求都将包含连接器理解请求所需的所有信息,并且它们与之前的任何请求都无关。在实际应用中,我们有很多方式可以给HTTP请求添加参数,包括:查询字符串、POST、PUT消息、各种PATCH请求、header等。他们每个人都有自己的用例和规则。那么,添加所有参数数据的最简单方法是将所有内容放入消息中。在具体的应用中,每个端点都会使用POST方法,很多API也会将所有的参数放在消息中。随着时间的推移,传统API中积累的参数越来越多,以至于不再适合查询字符串等操作。显然,我们在设计API时应该避免这种极端情况。添加什么参数?就REST而言,GraphQL作为一种API查询语言,为用户提供了一个满足数据查询的运行时。那么,我们是不是应该把HTTP规范中已经标准化的那些header字段类作为参数加入呢(参见:http://www.rfcreader.com/#rfc2616_line4589)?如果我们使用Accept标头,我们可以定义响应所使用的格式或媒体类型。我们可以使用它来告诉API它需要什么JSON或XML,以及获取API的版本(参见:https://www.moesif.com/blog/technical/api-design/Best-Practices-for-Versioning-REST-and-GraphQL-APIs/?utm_source=dzone&utm_medium=paid&utm_campaign=placed%20article&utm_term=rest%20api%20design%20best%20practices%20for%20parameters%20and%20query%20string%20usage)。并且通过Cache-Control标头,我们可以防止API使用no-cache发送缓存的响应,而无需使用查询字符串作为缓存破坏者(?cb=)。当然,授权也可以看作是一个参数。根据API在实际授权过程中的详细信息,授权是否通过,会产生不同类型的响应。因此,HTTP也为此定义了授权类型标头(请参阅:http://www.rfcreader.com/#rfc2616_line4922)。现在我们已经了解了各种默认标头字段,让我们讨论一下您是应该为自己的参数创建一个自定义标头字段,还是将其放在URL的查询字符串中。什么时候应该使用查询字符串?如果我们了解到要添加的参数不是默认的头字段,并且不敏感,那么我们应该检查查询字符串以确认它是否合适。查询字符串时,有一个Normal07.8lbs02falsefalsefalseEN-USZH-CNX-NONE/*StyleDefinitions*/table.MsoNormalTable{mso-style-name:normaltable;mso-tstyle-rowband-size:0;mso-tstyle-colband-size:0;mso-style-noshow:yes;mso-style-priority:99;mso-style-parent:"";mso-padding-alt:0cm5.4pt0cm5.4pt;mso-para-margin:0cm;mso-para-margin-bottom:.0001pt;mso-pagination:widow-orphan;font-size:10.0pt;font-family:"Arial",sans-serif;mso-bidi-font-family:"TimesNewRoman";}HTML元素可用于向服务器发送一些关键字。服务器相应地响应与关键字匹配的页面列表。然后在Web表单中重复使用查询字符串,以通过GET请求将数据发送到服务器。因此,查询字符串的主要用例是过滤,重点关注过滤、搜索和分页这两个特例。有关此内容的详细讨论,请参阅:https://www.moesif.com/blog/technical/api-design/REST-API-Design-Filtering-Sorting-and-Pagination/?utm_source=dzone&utm_medium=paid&utm_campaign=placed%20article&utm_term=rest%20api%20design%20best%20practices%20for%20parameters%20and%20query%20string%20usage。但是,如Web表单所示,它可以与不同类型的参数一起使用。RESTfulAPI可以使用带有消息的POST或PUT请求将表单数据发送到服务器。让我们看一个嵌套表示的参数示例。默认情况下,我们需要返回文章的简单表示。当我们将?withComments查询字符串添加到端点时,?withComments以普通表示形式返回一篇文章的评论,并且只需要一个请求。至于这些参数应该放在自定义的header中还是放在querystring中,主要取决于开发者个人的经验和喜好。根据HTTP的规范声明(见:http://www.rfcreader.com/#rfc2616_line1761):header字段可以被认为是函数的参数。但是,将查询字符串添加到URL比创建客户端标头要快得多,也更引人注目。实际上,这些字段充当请求修饰符,其语义等同于方法调用中的编程语言参数。实际上,您会发现在所有端点上保留相同的参数更适合标头。例如:身份验证令牌可以随每个请求一起发送。高度动态的参数(尤其是那些只对少数端点有效的参数)应该放在查询字符串中。例如:每个端点的过滤器参数将不同。Array和map参数对于开发人员来说,他们可能经常遇到的一个问题是:如何处理查询字符串中的数组参数?比如我们可能会遇到需要查找多个姓名的需求场景。通常的解决方案是:使用方括号。如下代码所示:但是,HTTP规范规定,IPv6[RFC3513]或更高版本标识的主机通过IP方括号(“[”和“]”)括起来来区分。这是URI语法中唯一允许使用方括号字符的地方。在许多HTTP服务器和客户端实现场景中,我们应该牢记上述规范。当然,为了简单起见,我们可以采用“多次使用一个参数名”的另一种解决方案。如以下代码所示:这种方法可行,但会导致开发人员体验不佳。通常,客户端只会使用类似地图的数据结构。相反,该结构在添加到URL之前会经过简单的字符串转换。而这恰恰导致后续的值被覆盖。可以看出,在发送请求之前,我们需要进行更复杂的转换。同时,有些人会使用另一种方法:用“,”字符分隔值,未编码的字符直接出现在URL中。如下代码所示:对于那些类似map的数据结构,我们也可以使用“.”。没有编码的字符。如下代码所示:或者,您也可以对整个查询字符串进行URL编码(参见:https://en.wikipedia.org/wiki/Percent-encoding)以直接使用您想要的任何字符或格式。不过,值得一提的是,这也会大大降低开发者的体验。什么时候不应该使用查询字符串?实际上,查询字符串通常是URL的一部分。而那些隐藏在客户端和API之间的攻击者(比如:maninthemiddle,MIM)可以很容易的读取我们的URL,所以我们不应该简单的把密码等敏感数据直接放到查询字符串中。虽然大多数HTTP客户端都会允许URL中包含五位字符,但如果我们没有充分设计和考虑URL的整体长度,开发人员在调试此类字符串时往往会出现问题。也会有各种各样的麻烦和不便。当然,由于我们可以将任何内容定义为资源,所以有时我们可以使用POST端点调用并传递大量参数,然后将消息中的所有数据发送给API。可以看出,为了避免向查询字符串中有多个参数的资源发送GET请求,导致URL冗长且无法识别,我们可以设计搜索类型等资源,根据API要求缓存各种计算的结果。同时,我们也可以向/searches端点发出新的请求。而这个请求会在消息中保存我们搜索相关的配置和参数。通过返回一个搜索ID以便我们可以使用它来获取搜索结果。总结作为一个API设计者或者架构师,我们所追求的良好实践其实就是找出最理想的API使用方式和最简单的实现用例。综上所述,我们建议您关注以下两个方面:从一开始,您可以使用Moesif(人工智能API服务平台)等服务开始分析您自己的API使用模式。虽然嵌套资源可以提高URL的可读性,但是如果我们嵌套过多,会导致URL参数冗长。因此,如果您发现自己创建了一个带有非常大查询字符串的端点,我建议您从中提取一些子资源并将它们作为消息中的参数发送。原标题:RESTAPIDesignBestPracticesforParametersandQueryStringUsage,作者:KayPloesser