Java从Servlet读取未解码的URL

我们假设我有像’=&?/;#+%’这样的字符串作为我url的一部分,让我们这样说:

example.com/servletPath/someOtherPath/myString/something.html?a=b&c=d#asdf 

其中myString是上面的字符串。 我编写了关键部分,因此URL看起来像

 example.com/servletPath/someOtherPath/%3D%26%3F%2F%3B%23%2B%25/something.html?a=b&c=d#asdf 

到现在为止还挺好。

当我在servlet中并且我读取request.getRequestURI()request.getRequestURL()request.getPathInfo() ,返回的值已经被解码,所以我得到像strilng一样

 someOtherPath/=&?/;#+%/something.html?a=b&c=d#asdf 

我无法区分真正的特殊字符和编码字符。

我通过完全禁止上面的字符解决了特殊的问题,这在这种情况下有效,但我仍然想知道有没有办法在servlet类中获取未解码的URL。

另一个编辑:当我昨晚遇到这个问题时,我太累了,无法注意到真正发生的事情,这更奇怪! 我有servlet映射,比如/ servletPath / *之后,我可以放任何我想要的东西,并根据路径的其余部分让我的servlet响应, 除非路径中有%2F。 在那种情况下, 请求永远不会访问servlet ,我得到404! 如果我把’/’代替%2F它可以正常工作。 我正在Linux上运行Java 1.6.0-04上的Tomcat 6.0.14。

浏览器和服务器的’%2F’和’/’之间存在根本区别。

HttpServletRequest规范说(没有任何逻辑,AFAICT):

  • getContextPath:未解码
  • getPathInfo:已解码
  • getPathTranslated:未解码
  • getQueryString:未解码
  • getRequestURI:未解码
  • getServletPath:已解码

解码getPathInfo()的结果,但不得解码getRequestURI()的结果。 如果是这样,你的Servlet容器就违反了规范(正如Wouter Coekaerts和Francois Gravel正确指出的那样)。 您运行的是哪个Tomcat版本?

更令人困惑的是, 出于安全原因 ,当前的Tomcat版本会拒绝包含某些特殊字符编码的路径。

如果解码的url中存在%2F ,则表示编码的url包含%252F

由于%2F/为什么不拆分"\/"而不担心URL编码?

根据Javadoc ,getRequestURI不应解码字符串。 另一方面,getServletPath返回一个已解码的字符串。 我使用Jetty在本地测试它,它的行为与文档中描述的相同。

因此,您所描述的行为与Sun文档不匹配,因此您的情况可能还有其他因素在起作用。

看起来你正在尝试做一些RESTy(使用Jersey)。 您可以解析URL的前导和尾随部分以获取您要查找的数据吗?

url.substring(startLength,url.length – endLength);

更新:这个答案最初错误地指出路径中的’/’和’%2F’应始终被视为相同。 它们实际上是不同的,因为路径是/分隔段的列表。

您不必在URL 的路径部分中对已编码和未编码的字符进行区分。 路径中没有可以在URL中具有特殊含义的字符。 例如,’%2F’必须解释为与’/’相同,并且访问此类URL的浏览器可以在其认为合适时自由地替换另一个。 它们之间的区别正在打破URL编码的标准。

在完整的URL中,您必须出于不同的原因对转义字符和非转义字符进行区分,包括:

  • 查看路径部分的结束位置。 因为? 在路径中编码不应该被视为结束。
  • 在查询字符串内。 因为参数值的一部分可能包含’&’或’=’,…
  • 在路径内,’/’分隔两个段,而’%2F’可以包含在一个段中

Java在前两种情况下处理得很好:

  • getPathInfo()仅返回路径部分,已解码
  • getParameter(String)访问查询部分的部分

它与第三种情况不太一致。 如果你想在’/’作为两个路径段的分离和路径段(%2F)内的’/’之间做出区别,那么你不能一致地将路径表示为一个解码的字符串。 您可以将其表示为一个编码字符串(例如“foo / bar%2Fbaz”),或者将其表示为已解码段的列表(例如“foo”,“bar / baz”)。 但是因为getPathInfo()API承诺做到这一点(一个解码的字符串),它别无选择,只能将’/’和’%2F’视为相同。

对于通常的Web应用程序,这很好。 如果您在极少数情况下确实需要做出改变,那么您可以自己解析URL,使用getRequestURI()获取原始版本。 如果那个提供了您声明的URL解码,那么这意味着您正在使用的servlet实现中存在一个错误。