Servlet规范系列 之 HttpServletResponseWrapper源码分析

欢迎大家关注本博,同时欢迎大家评论交流,可以给个赞哦!!!

  实际应用场景中,有时可能会有这样的需求:需要在响应到客户端之前,对响应进行统一处理,增加或减少到达应用的数据量等,这时就可以创建一个类继承HttpServletResponseWrapper,它可以对HttpServletResponse进行再封装,然后之后的请求都会经过这个类,应用接收到的请求对象也会是这个类。

  HttpServletResponseWrapper集成了ServletResponseWrapper,ServletResponseWrapper是对ServletResponse的包装,HttpServletResponseWrapper是对HttpServletResponse的包装,其实查看源码后会发现,ServletResponseWrapper和HttpServletResponseWrapper提供的API完全调用的ServletResponse和HttpServletResponse的API进行实现。在我们进行应用时,使用HttpServletResponseWrapper即可,它同时拥有ServletResponseWrapper和HttpServletResponseWrapper的功能。

  ServletResponseWrapper

package javax.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Locale;

/**
 * ServletResponseWrapper提供了一个简便实现类,在对ServletResponse有改动需求的时,可以重写此类,之后Response的操作都会经过重写的类.
 */
public class ServletResponseWrapper implements ServletResponse {
    
    
    
    /**
     * ServletResponse实例.
     */
    private ServletResponse response;

    /**
     * Constructor..
     */
    public ServletResponseWrapper(ServletResponse response) {
    
    
        if (response == null) {
    
    
            throw new IllegalArgumentException("Response cannot be null");
        }
        this.response = response;
    }

    /**
     * 获取ServletResponse实例.
     */
    public ServletResponse getResponse() {
    
    
        return this.response;
    }

    /**
     * 设置ServletResponse实例.
     */
    public void setResponse(ServletResponse response) {
    
    
        if (response == null) {
    
    
            throw new IllegalArgumentException("Response cannot be null");
        }
        this.response = response;
    }

    /**
     * 设置发送到客户端的响应的字符编码(MIME字符集),例如UTF-8.
     * 如果字符编码已经由setContentType或setLocale设置,则此方法将重写它.
     * 可以重复调用此方法来更改字符编码.如果在调用getWriter之后或在提交响应之后调用它,则此方法无效.
     * 如果协议提供了一种方法,容器必须将用于servlet响应的编写器的字符编码传递给客户机.
     * 在HTTP的情况下,字符编码作为文本媒体类型的Content-Type报头的一部分进行通信.
     * 请注意,如果Servlet没有指定内容类型,则字符编码不能通过HTTP头进行通信.但是,它仍然用于对通过Servlet响应的编写器写入的文本进行编码.
     * @param 编码字符串.
     */
    public void setCharacterEncoding(String charset) {
    
    
        this.response.setCharacterEncoding(charset);
    }

    /**
     * 返回用于此响应中发送的正文的字符编码(MIME字符集)的名称.
     * 字符编码可能是使用setCharacterEncoding或setContentType方法显式指定的,也可能是隐式使用setLocale方法指定的.显式规范优先于隐式规范.
     * 在调用getWriter或在提交响应之后对这些方法的调用对字符编码没有影响.如果未指定字符编码,则返回ISO-8859-1.
     * @return Character Encoding,例如:UTF-8.
     */
    public String getCharacterEncoding() {
    
    
        return this.response.getCharacterEncoding();
    }

    /**
     * 返回ServletOuputStream用于在response中写入二进制数据.
     * Servlet容器不编码二进制数据.
     * 调用ServletOutputStream的flush()方法提交response.
     * ServletOutputStream和PrintWriter不可同时使用.
     * @return ServletOutputStream实例.
     */
    public ServletOutputStream getOutputStream() throws IOException {
    
    
        return this.response.getOutputStream();
    }

    /**
     * 返回一个getWriter对象,该对象可以向客户端发送字符文本.
     * PrintWriter使用getCharacterEncoding返回的字符编码.
     * 如果响应的字符编码没有按照getCharacterEncoding中的描述指定(即,该方法只返回默认值ISO-8859-1),
     *  则getWriter将其更新为ISO-8859-1.
     * 调用PrintWriter的flush()方法提交response.
     * PrintWriter和ServletOutputStream不可同时使用.
     * @return PrintWriter实例.
     */
    public PrintWriter getWriter() throws IOException {
    
    
        return this.response.getWriter();
    }

    /**
     * 在HTTP响应头Content-Length中设置响应体内容的长度.
     * @param 指定返回给客户端的内容的长度.设置Content-Length头.
     */
    public void setContentLength(int len) {
    
    
        this.response.setContentLength(len);
    }

    /**
     * 如果尚未提交响应,则设置发送到客户端的响应的内容类型.
     * 给定的内容类型可以包括字符编码规范,例如,text/html;charset=UTF-8.
     * 如果在调用getWriter之前调用此方法,则仅从给定的内容类型设置响应的字符编码.
     * 可以重复调用此方法来更改内容类型和字符编码.如果在提交响应后调用此方法,则此方法无效.
     * 如果在调用getWriter之后或在提交响应之后调用它,则不会设置响应的字符编码.
     * @param MIME类型.
     */
    public void setContentType(String type) {
    
    
        this.response.setContentType(type);
    }

    /**
     * 返回用于此响应中发送的MIME正文的内容类型.
     * 在提交响应之前,必须使用setContentType指定正确的内容类型.
     * 如果未指定内容类型,则此方法返回null.
     * 如果已指定内容类型,并且已显式或隐式指定字符编码,如getCharacterEncoding或getWriter中所述,则返回的字符串中包含charset参数.如果未指定字符编码,则省略charset参数.
     * @return Content-Type,例如:text/html; charset=UTF-8
     */
    public String getContentType() {
    
    
        return this.response.getContentType();
    }

    /**
     * 设置响应正文的首选缓冲区大小.Servlet容器将使用至少与请求大小相同的缓冲区.实际使用的缓冲区大小可以使用getBufferSize.
     * 更大的缓冲区允许在实际发送任何内容之前写入更多内容,从而为Servlet提供更多时间来设置适当的状态代码和标头.
     * 较小的缓冲区减少了服务器内存负载,并允许客户端更快地开始接收数据.
     * 必须在写入任何响应正文内容之前调用此方法.如果已写入内容或已提交response,则此方法将抛出IllegalStateException.
     * @param 缓冲区大小.
     */
    public void setBufferSize(int size) {
    
    
        this.response.setBufferSize(size);
    }

    /**
     * 返回用于响应的实际缓冲区大小.
     * 如果不使用缓冲,则此方法返回0.
     * @return 使用实际缓冲区大小.
     */
    public int getBufferSize() {
    
    
        return this.response.getBufferSize();
    }

    /**
     * 强制将缓冲区中的任何内容写入客户端.对此方法的调用会自动提交响应,这意味着将写入状态代码和标头.
     * @throws IOException .
     */
    public void flushBuffer() throws IOException {
    
    
        this.response.flushBuffer();
    }

    /**
     * 返回一个布尔值,指示是否已提交响应.已提交的响应已写入其状态代码和标头.
     */
    public boolean isCommitted() {
    
    
        return this.response.isCommitted();
    }

    /**
     * 清除缓冲区中存在的所有数据以及状态代码和标头.如果响应已提交,此方法将抛出IlleglaStateException.
     */
    public void reset() {
    
    
        this.response.reset();
    }

    /**
     * 清除响应中基础缓冲区的内容,而不清除标头或状态码.如果响应已提交,此方法将抛出IlleglaStateException.
     */
    public void resetBuffer() {
    
    
        this.response.resetBuffer();
    }

    /**
     * 在response未提交时,设置response的locale.
     * 如果尚未使用setContentType或setCharacterEncoding设置字符集、getWriter尚未被调用、response尚未被提交,它还会为区域设置适当的响应字符编码.
     * 可以重复调用此方法来更改区域设置和字符编码.如果在提交响应后调用该方法,则该方法无效.
     * 如果在使用字符集规范调用setContentType之后、调用setCharacterEncoding之后、调用了getWriter之后,或者在响应被提交之后调用,它不会设置响应的字符编码.
     * 如果协议提供了一种方法,容器必须将用于servlet响应的编写器的区域设置和字符编码传递给客户端.
     * 在HTTP的情况下,语言环境通过Content-Language报头进行通信,字符编码作为文本媒体类型的Content-Type报头的一部分.
     * 请注意,如果Servlet没有指定内容类型,则字符编码不能通过HTTP头进行通信.但是,它仍然用于对通过Servlet响应的编写器写入的文本进行编码.
     * @param loc locale.
     */
    public void setLocale(Locale loc) {
    
    
        this.response.setLocale(loc);
    }

    /**
     * 返回使用setLocale指定的区域设置.setLocale在response提交之后不会生效.
     * 如果没有指定locale,返回容器默认的locale.
     * @return locale.
     */
    public Locale getLocale() {
    
    
        return this.response.getLocale();
    }

}

  HttpServletResponseWrapper

package javax.servlet.http;

import java.io.IOException;

import javax.servlet.ServletResponseWrapper;

/**
 * HttpServletResponseWrapper提供了一个简便实现类,在对HttpServletResponse有改动需求的时,可以重写此类,之后Response的操作都会经过重写的类.
 */

public class HttpServletResponseWrapper extends ServletResponseWrapper implements HttpServletResponse {
    
    

    /**
     * Constructs.
     */
    public HttpServletResponseWrapper(HttpServletResponse response) {
    
    
        super(response);
    }
    
    /**
     * 获取HttpServletResponse.
     * @return HttpServletResponse.
     */
    private HttpServletResponse _getHttpServletResponse() {
    
    
        return (HttpServletResponse) super.getResponse();
    }

    /**
     * 设置指定Cookie到response中.
     * 可以被多次调用,可以设置多个Cookie.
     * @param Cookie实例.
     */
    public void addCookie(Cookie cookie) {
    
    
        this._getHttpServletResponse().addCookie(cookie);
    }

    /**
     * 判断response中是否包含某个响应头.
     * @param 响应头名称.
     * @return 存在返回true,否则返回false.
     */
    public boolean containsHeader(String name) {
    
    
        return this._getHttpServletResponse().containsHeader(name);
    }

    /**
     * 对指定URL进行编码,编码后包含会话ID,若逻辑判断不需要编码,则返回原URL.
     * 此方法的实现需包括:确定会话ID是否需要包含在编码后的URL中,例如,当浏览器支持Cookie时,或者URL重写(会话跟踪)被关闭时,则不需要URL编码,直接返回原URL.
     * 对于健壮的会话跟踪,Servlet发出的所有URL都应该通过这个方法运行.否则,URL重写不能用于不支持Cookie的浏览器.
     * @param 待编码的URL.
     * @return 已编码的URL.
     */
    public String encodeURL(String url) {
    
    
        return this._getHttpServletResponse().encodeURL(url);
    }

    /**
     * 对指定URL进行编码,编码后包含会话ID,若逻辑判断不需要编码,则返回原URL.
     * HttpServletResponse.sendRedirect调用前,需调用此方法进行URL编码.
     * 此方法包括是否包含会话ID的逻辑.
     * 此方法包含会话ID的逻辑与encodeURL(String url)是不同的,所以进行了分离.
     * 所有发送到HttpServletResponse.sendRedirect方法应通过此方法运行,否则,URL重写不能用于不支持cookies的浏览器.
     * @param 待编码的URL.
     * @return 已编码的URL.
     */
    public String encodeRedirectURL(String url) {
    
    
        return this._getHttpServletResponse().encodeRedirectURL(url);
    }

    /**
     * @deprecated 
     * 已弃用,使用encodeURL(String url).
     */
    public String encodeUrl(String url) {
    
    
        return this._getHttpServletResponse().encodeUrl(url);
    }

    /**
     * @deprecated 
     * 已弃用,使用encodeRedirectURL(String url).
     */
    public String encodeRedirectUrl(String url) {
    
    
        return this._getHttpServletResponse().encodeRedirectUrl(url);
    }

    /**
     * 写入指定的状态码和错误描述信息,并清除缓冲区,将错误响应给客户端.
     * 如果响应已经提交,此方法将抛出一个IllegalStateException,使用此方法后,应将响应视为已提交,不应写入.
     * @param 错误状态码.
     * @param 错误描述信息.
     */
    public void sendError(int sc, String msg) throws IOException {
    
    
        this._getHttpServletResponse().sendError(sc, msg);
    }

    /**
     * 写入指定的状态码,并清除缓冲区,将错误响应给客户端.
     * 如果响应已经提交,此方法将抛出一个IllegalStateException,使用此方法后,应将响应视为已提交,不应写入.
     * @param 错误状态码.
     */
    public void sendError(int sc) throws IOException {
    
    
        this._getHttpServletResponse().sendError(sc);
    }

    /**
     * 使用指定的重定向位置URL向客户端发送临时重定向响应.
     * 此方法可以接受相对URL,Servlet容器必须在向客户机发送响应之前将相对URL转换为绝对URL.
     * 如果位置是相对的而没有前导"/",容器会将其解释为相对于当前请求URI.
     * 如果位置与前导"/"相关,则容器会将其解释为相对于servlet容器根.
     * 如果响应已经提交,此方法将抛出一个IllegalStateException,使用此方法后,应将响应视为已提交,不应写入.
     * @param 重定向地址.
     */
    public void sendRedirect(String location) throws IOException {
    
    
        this._getHttpServletResponse().sendRedirect(location);
    }

    /**
     * 设置Date类型头信息.若已经存在指定的头,则会覆盖.
     * @param 头名称.
     * @param 头值.
     */
    public void setDateHeader(String name, long date) {
    
    
        this._getHttpServletResponse().setDateHeader(name, date);
    }

    /**
     * 新增Date类型头信息.不管是否存在,都会追加,可允许存在多个同名的头.
     * @param 头名称.
     * @param 头值.
     */
    public void addDateHeader(String name, long date) {
    
    
        this._getHttpServletResponse().addDateHeader(name, date);
    }

    /**
     * 设置头信息.若已经存在指定的头,则会覆盖.
     * @param 头名称.
     * @param 头值.
     */
    public void setHeader(String name, String value) {
    
    
        this._getHttpServletResponse().setHeader(name, value);
    }

    /**
     * 新增头信息.不管是否存在,都会追加,可允许存在多个同名的头.
     * @param 头名称.
     * @param 头值.
     */
    public void addHeader(String name, String value) {
    
    
        this._getHttpServletResponse().addHeader(name, value);
    }

    /**
     * 设置int类型头信息.若已经存在指定的头,则会覆盖.
     * @param 头名称.
     * @param 头值.
     */
    public void setIntHeader(String name, int value) {
    
    
        this._getHttpServletResponse().setIntHeader(name, value);
    }

    /**
     * 新增int类型头信息.不管是否存在,都会追加,可允许存在多个同名的头.
     * @param 头名称.
     * @param 头值.
     */
    public void addIntHeader(String name, int value) {
    
    
        this._getHttpServletResponse().addIntHeader(name, value);
    }

    /**
     * 设置response的状态码.
     * 此方法被用来设置成功状态码,出现错误设置状态码使用sendError方法.
     * 例如:SC_OK、SC_MOVED_TEMPORARILY.
     * @param 状态码.
     */
    public void setStatus(int sc) {
    
    
        this._getHttpServletResponse().setStatus(sc);
    }

    /**
     * @deprecated 
     * 设置response的状态码.
     * 此方法被用来设置成功状态码,出现错误设置状态码使用sendError方法.
     * 例如:SC_OK、SC_MOVED_TEMPORARILY.
     * @param 状态码.
     * @param 状态描述.
     */
    public void setStatus(int sc, String sm) {
    
    
        this._getHttpServletResponse().setStatus(sc, sm);
    }

}

  若文中存在错误和不足,欢迎指正!

本博微信公众号“超哥说码”,欢迎大家订阅,公众号正在完善中,会及时将更优质的博文推送于您!
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/securitit/article/details/108046187
今日推荐