2010-04-30 94 views
4

我正在设计一个Web服务。该请求是幂等的,所以我选择了GET方法。响应计算起来相对昂贵而且不小,所以我想要在协议层面上进行缓存。 (不要担心我的回忆录,我已经覆盖了;我的问题实际上也是关注整个网络。)如何处理映射到相同响应的不同请求?

只有一个强制参数和一些可选参数与默认值值如果丢失。例如,以下两个映射到响应的相同表示。 (如果这是去它的界面,提出更好的东西一个愚蠢的方式。)

GET /service?mandatory_parameter=some_data HTTP/1.1 
GET /service?mandatory_parameter=some_data;optional_parameter=default1;another_optional_parameter=default2;yet_another_optional_parameter=default3 HTTP/1.1 

不过,我想客户不知道这一点,就会把它们分开,因此浪费缓存存储。我应该怎么做才能避免违反golden rule of caching

  1. 弥补规范形式,将其记录下来(例如是毕竟所需的所有参数,需要以特定的顺序进行排序),并返回一个client error除非所需的形式满足?
  2. 而不是一个错误,永久重定向到请求的规范形式?
  3. 或者只是不介意请求的外观如何,只是对相同的响应采用相同的ETag进行响应?
+0

共识似乎是用2.,我会这样。 – daxim 2010-05-09 09:21:58

回答

4

首先,在查询字符串中不要使用分号作为分隔符。您应该使用?开始查询字符串,并使用&来分隔变量/值对。 RFC 3986没有明确说你必须使用&,但绝大多数现有的代码使用这个分隔符,因为application/x-www-form-urlencoded的先例。

其次,你是对的,因为查询字符串中的参数导致不同的URI,因此,就缓存而言,是不同的资源。假设你想获得最佳的缓存性能,如果你知道已经指定了一个可选参数,并且它的包含是不必要的,并且不会影响将被传输的表示,那么应该重定向到忽略该参数的规范表示。 (例如,可选参数的值设置为默认值,例如,如果您有http://example.com:80/,则可以规范化为http://example.com/,因为80是具有HTTP的端口的默认值。您可以执行相同的操作查询参数,因为您控制的是URI空间。)如果您有参数(可选或其他)出现在规范顺序以外的顺序,您也应该重定向。如果知道URI之间的关系稳定,则301重定向将是首选。否则,请根据情况进行302/307重定向。我建议按照与OAuth相同的方式来定义规范形式:按字母顺序排序每个参数,首先按键,然后按值排序。其他规范化操作也将在这里帮助。 RFC 3986有关于URI normalization的全部内容,这些内容将与您相关。这种技术只能用于GET,并且通常不建议在PUT/POST/DELETE上重定向。第三,ETags非常好,如果客户端和服务器都能很好地实现,它们将提供巨大的性能提升。但是,很遗憾,双方都做得很对。同上Last-Modified。你应该追求这些,因为CPU和带宽节省在工作时很重要,但它们本身并不足够。像Cache-Control这样的其他头文件也是经常需要的。如果您打算详细介绍这些东西,那么值得熟悉Section 13 of RFC 2616

最后,警告一句话 - 这些重定向有问题需要注意:试图访问资源的客户端可能经常被重定向到其他位置。这会引入开销,如果客户端针对同一资源发出后续请求,则会为您节省总体费用,并维护状态以避免后续重定向。除非您已经开源了一个参考客户端实现,该实现可以利用缓存优化,否则您可能永远不会从这些调整中受益。

+0

由于[HTML4§B.2.2](http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.2),我拒绝了&。 +1从OAuth指出规范化,我会偷这个。我不认为需要参考实现方面存在任何问题;毕竟它只是HTTP。正如另一个回答所说,永久重定向既便宜又可以缓存。 – daxim 2010-05-09 09:22:18

+0

该建议只适用于您的URI直接出现在HTML或XML中的情况。但是,即使在这些情况下,如果您在将这些URI插入到内容之前还没有逃脱这些URI,那么您就错了。本规范中的建议与手动编码的输出无关。 – 2010-05-09 22:36:11

+0

关于参考实现的主题,我认为您可能会过度相信将使用您的API的人。如果它在没有设置缓存的情况下工作,人们将不会设置缓存。 – 2010-05-09 22:43:49

1

我会选择列表中的选项(2) - 我会使请求RESTful,而不是RPC像。

I.e.在这种情况下,如果你把所有的请求路径的参数部分:

/服务/ mandatory_parameter/some_data/optional_parameter /缺省1/another_optional_parameter /默认设置2/yet_another_optional_parameter/default3

在该情况下,不是所有的指定了可选参数,并返回301(永久重定向)到完整的资源名称,并填充默认值。这将(或应该)由客户端和Web缓存适当地缓存,即使它到达后端,然后使301对你来说应该很便宜。

在这一点上,你有一个规范的URI形式,并且缓存将按正常/预期工作。

这确实意味着每个参数组合都将被单独缓存(作为301),但是这很好,因为非规范请求将对完整请求具有独立的缓存策略,并且担心额外的客户端往返旅程可以自行填写所有参数。

您的选项(3)无法按预期工作 - 每个表单将独立缓存,因为它们是不同的URI。

还应当指出的是,很多下游缓存/软件不会在所有缓存你的回应由于查询参数,这就是为什么我建议把它变成一个“适当的”资源..

+0

您建议的URI不是RESTful,并且使用查询参数不会使URI成为“RPC-like”。如果你真的有这么多参数,我强烈建议不要这样做。这是最好的货运方式。请阅读Roy Fielding关于REST架构风格的实际定义的论文。 – 2010-05-06 22:02:15

+0

t0m,鲍勃答是对的。我不知道是什么让你觉得我的设计违反了建筑风格的标准,但它是一致的。你作为REST for Catalyst的维护者应该认识到这一点。大量的斜杠意味着没有一个等级。即参数是平等的。 – daxim 2010-05-09 09:22:34

+0

您不能仅更改URL结构并使服务成为RESTful,您建议不是RESTful。 – EralpB 2017-01-02 09:58:18

0

首先,您选择GET是一件好事,因为其他方法没有良好的缓存支持。据我所知,浏览器会根据参数来缓存URI,所以我不认为使用规范形式是个好主意。
你在这里没有说明的一件事是如何使用这项服务。如果这些请求是通过浏览器完成的(而且它看起来这些请求可能是从脚本发出的),那么即使请求多次请求,请求也可能看起来相同。因此,请确保无论生成URI的URI是否等于输入数据的相同URI(删除默认参数或始终包含它们)。
说到ETag,我建议你有这个,但我想澄清它是如何工作的;你得到请求,处理所有“昂贵的计算”,然后如果有一个与你的处理响应相同的散列(ETag)的If-None-Match头,你可能会返回304 Not-Modified。因此,如果客户端已经拥有它,则使用ETag来避免传输响应。 (当然,你可以在服务器端实现缓存,但是这最好根据输入参数进行)。
为了进一步改善客户端的缓存命中,您可能需要在响应中设置正确的缓存标题。

+0

这个答案没有提供新的见解。 – daxim 2010-05-09 09:23:01

+0

当它是它发布的第二个答案时。 – MyGGaN 2010-05-09 09:41:17

0

一个月前我问了几乎同样的问题。我的回答我描述了一个我的实现的例子。

在服务器端,我有在下列形式

GET /Service/RequestedData?param1=data1&param2=data2… 
GET /Service/RequestedData/IdOfData?param1=data1&param2=data2… 
PUT /Service/RequestedData/IdOfData // with param1=data1&param2=data2… in body 
POST /Service/RequestedData/IdOfData // with param1=data1&param2=data2… in body 
DELETE /Service/RequestedData/IdOfData 

所以请求在休息一个接收请求WFC服务,但GET请求有一些可选参数。特别是这部分是您感兴趣的端口。

因为WFC支持URL模板的功能,其中回复客户端请求的原型看起来像

[WebGet (UriTemplate = "RequestedData?param1={myParam1}&param2={myParam2}", 
     ResponseFormat = WebMessageFormat.Json)] 
[OperationContract] 
MyResult GetData (string myParam1, int myParam2); 

GET /Service/RequestedData?param1=&param2=data2 
GET /Service/RequestedData?param2=data2&param1= 
GET /Service/RequestedData?param2=data2 

所有的请求都将被映射到从侧面同一呼叫我的WCF服务。所以我少了一个问题。

现在,在每一个实施响应于HTTP请求GET我在HTTP报头“缓存控制:最大年龄= 0”设置该方法的开始。这意味着客户端总是尝试验证客户端浏览器缓存,没有ajax请求将不容易从本地缓存响应,就像它可以做Internet Explorer。

接下来我根据我的数据总是计算一个ETag。确切的算法是一个单独讨论的主题,但重要的是,在HTTP标头中,对HTTP GET请求的所有响应都存在ETag

因此,客户端每次验证他的本地缓存并发送GET请求到服务器。他们发送来自其本地缓存的ETag,位于“If-None-Match”HTTP标头内。服务器计算具有数据的ETag,该数据将发送回此GET请求。它的数据的ETag与客户端请求服务器发回的空主体和代码为“304 Not Modified”的回应相同。在这种情况下,浏览器从本地缓存中提供数据。

如果从一个未知的原因,同一个客户端创建的URL请求的新版本,它将从Web浏览器解释为 URL,那么Web浏览器将无法找到在本地缓存旧的服务器响应,并发送再向服务器请求相同的请求。这是一个真正的问题吗?服务器再次发送数据。如果你有一个服务器端缓存,你可以做更多的优化。在大多数情况下,GET请求的URL将由客户端JavaScript生成,因此您不会有这种情况。

ETag计算和“Cache-Control: max-age=0”和Etag头以及设置的设置“304 Not Modified”的代码应该做的WFC服务,但它是很容易的。

最重要的是我的ETag计算的实现并不像从数据库服务器和计算MD5高速缓存中获取整个数据那样广泛。我在SQL Server数据库的每一行数据中永久使用rowversion数据类型。这rowversion没什么别的作为数据库变化的计数器。如果其中一行数据rowversion改变了相应行的值就会增加。因此,如果从rowversion值的最大值开始SELECT声明,并且此值与先前的请求相比没有变化,则可以确定数据在该时间段内未被更改。ETa g的计算算法应该只对从表格中删除数据敏感。但这也是一个解决的问题。有关这方面的更多信息,请参阅Concurrency handling of Sql transactrion

我不希望建议我的ETag计算作为最佳选择,我只想说,计算ETag可以便宜得多,因为从整个数据计算MD5。

出现错误时服务器抛出一个异常,该异常将映射到我在throw语句中定义的HTTP代码。作为一个主体WFC发送一个标准的JSON对象{"description":"My error text"}。自定义错误对象也是可能的(见Is WebProtocolException included in .net 4.0?)。在客户端,我使用jQuery,并在错误事件处理程序中的相应的jQuery.ajax错误消息将被解码并显示给用户。

所以我的推荐:对所有的HTTP GET请求使用ETag和“Cache-Control: max-age=0”。对于所有其他请求,我会推荐您执行RESTfull服务。对于错误实现,您应该查看用于服务器和客户端实现的软件支持的最原始方式,并使用它。

UPDATED:要清除URL结构,我应该添加以下内容。在我的服务中,主要部分如GET /Service/RequestedData/IdOfData描述了请求的数据对象。参数param1=data1&param2=data2主要对应于关于分类的信息,分页过滤的数据。我为jQuery使用了主动的jqGrid插件,并且如果最终用户在网格中滚动到下一页,单击列标题(数据排序),或者如果他设置了关于搜索功能的过滤器,则所有这些遵循不同的可选参数附加了主要的URL。

+0

尽一切努力分享您关于Web服务的发现,但在您自己的博客上做。你问:“这是一个真正的问题吗?”是的,这就是为什么我问这个具体问题的原因! -1不注意它。 – daxim 2010-05-09 09:23:33

+0

对不起,如果我的回答太长,看起来像一个博客给你。我只想帮助你,所以不仅要给出简短的答案,而且要以一个例子来解释我的答案。您的问题的简短答案是:1)不需要规范形式如果客户端请求将由客户端软件产生 2)在错误的情况下不重定向,显示额外的div而不是错误消息 3)使用Etags,但一个好的,不介意请求的外观如何 – Oleg 2010-05-09 10:04:10

+0

如果我的回答对你没有帮助,你认为这是我的博客,我可以删除我的答案。我等待你的回复。 – Oleg 2010-05-09 10:12:41

相关问题