修改请求
考察这样一种需求:我们在Web应用中经常需要处理用户上传文件,例如,一个UploadServlet可以简单地编写如下:
但是要保证文件上传的完整性怎么办?在哈希算法一节中,我们知道,如果在上传文件的同时,把文件的哈希也传过来,服务器端做一个验证,就可以确保用户上传的文件一定是完整的。
这个验证逻辑非常适合写在中,因为它可以复用。
我们先写一个简单的版本,快速实现ValidateUploadFilter
的逻辑:
ValidateUploadFilter
对签名进行验证的逻辑是没有问题的,但是,细心的童鞋注意到,UploadServlet
并未读取到任何数据!
这里的原因是对HttpServletRequest
进行读取时,只能读取一次。如果Filter调用getInputStream()
读取了一次数据,后续Servlet处理时,再次读取,将无法读到任何数据。怎么办?
这个时候,我们需要一个“伪造”的HttpServletRequest
,具体做法是使用,对getInputStream()
和getReader()
返回一个新的流:
注意观察ReReadableHttpServletRequest
的构造方法,它保存了ValidateUploadFilter
读取的内容,并在调用getInputStream()
时通过byte[]
构造了一个新的ServletInputStream
。
再注意到我们编写ReReadableHttpServletRequest
时,是从HttpServletRequestWrapper
继承,而不是直接实现HttpServletRequest
接口。这是因为,Servlet的每个新版本都会对接口增加一些新方法,从HttpServletRequestWrapper
继承可以确保新方法被正确地覆写了,因为是由Servlet的jar包提供的,目的就是为了让我们方便地实现对HttpServletRequest
接口的代理。
我们总结一下对HttpServletRequest
接口进行代理的步骤:
- 覆写某些方法,使得新的
XxxHttpServletRequest
实例看上去“改变”了原始的HttpServletRequest
实例;
虽然整个Filter的代码比较复杂,但它的好处在于:这个Filter在整个处理链中实现了灵活的“可插拔”特性,即是否启用对Web应用程序的其他组件(Filter、Servlet)完全没有影响。
从下载练习:使用Filter修改请求 (推荐使用快速下载)