response.setHeader(Content-Type)与response.setContentType()的区别

response.setHeader(Content-Type)与response.setContentType()的区别

在Java Web编程中,设置 Content-TypeContent-Length 头部是再经常不过的操作了,但是 HttpServletResponse 提供了两个相关的方法来设置头部,一个是 void setHeader(String name, String value) ,一个是 void setContentType(String type) 或者 void setContentLength(int len) 。这两者之间有什么区别吗?之前一直分不清楚,也在代码中看到两种方式都有被人使用,甚至是两个一起写的。

我们通过阅读Tomcat相关函数的代码,来看看这两者具体有什么区别。

javax.servlet.http.HttpServletResponse#setHeader 的实现是 org.apache.catalina.connector.ResponseFacade#setHeader ,内部会调用 org.apache.catalina.connector.Response#setHeader ,源码如下:

public void setHeader(String name, String value) {

    if (name == null || name.length() == 0 || value == null) {
        return;
    }

    if (isCommitted()) {
        return;
    }

    // Ignore any call from an included servlet
    if (included) {
        return;
    }

    // 
    char cc=name.charAt(0);
    if (cc=='C' || cc=='c') {
        if (checkSpecialHeader(name, value))
        return;
    }

    getCoyoteResponse().setHeader(name, value);
}

setHeader 函数中,如果发现设置的头部已 c 或者 C 开头,会调用 checkSpecialHeader

// org.apache.catalina.connector.Response#checkSpecialHeader
private boolean checkSpecialHeader(String name, String value) {
    if (name.equalsIgnoreCase("Content-Type")) {
        setContentType(value);
        return true;
    }
    return false;
}

setHeader 函数中,为了性能,只校验第一个字母,在 checkSpecialHeader 方法中进行完整的判断,如果header的名字是不区分大小写的 Content-Type ,则调用 setContentType 函数来设置,然后直接返回。到这里我们可以知道,使用setHeader设置头部时,如果设置 Content-Type ,其实内部使用的是 setContentType 函数来实现。

Content-Length 呢?不着急,继续往下看。在 setHeader 函数中,如果不是 c 或者 C 开头的情况,会执行 getCoyoteResponse().setHeader(name, value) 这句话,源码如下:

// org.apache.coyote.Response#setHeader
public void setHeader(String name, String value) {
    char cc=name.charAt(0);
    if( cc=='C' || cc=='c' ) {
        if( checkSpecialHeader(name, value) )
        return;
    }
    headers.setValue(name).setString( value);
}

是不是觉得眼熟,这里再一次地判断了header名称是不是以 c 或者 C 开头。但是这里的 checkSpecialHeader 实现是不一样的:

// org.apache.coyote.Response#checkSpecialHeader
private boolean checkSpecialHeader( String name, String value) {
    // XXX Eliminate redundant fields !!!
    // ( both header and in special fields )
    if( name.equalsIgnoreCase( "Content-Type" ) ) {
        setContentType( value );
        return true;
    }
    if( name.equalsIgnoreCase( "Content-Length" ) ) {
        try {
            long cL=Long.parseLong( value );
            setContentLength( cL );
            return true;
        } catch( NumberFormatException ex ) {
            // Do nothing - the spec doesn't have any "throws"
            // and the user might know what he's doing
            return false;
        }
    }
    return false;
}

如果header的名字是不区分大小写的 Content-Type ,则调用 setContentType 函数来设置。如果header的名字是不区分大小写的 Content-Length ,则调用 setContentLength 函数来设置。

至此我们得到了结论:通过 setHeader 来设置 Content-Type 或者 Content-Length 头部,内部是调用 setsetContentType / setContentLength 来实现的。所以两者功能上没有区别。但是推荐使用 setsetContentType / setContentLength 因为少了多余的判断,性能更高,函数名也更明义。


原文:http://imushan.com/2018/12/09/java/tomcat/response.setHeader(Content-Type)与response.setContentType()的区别/