springboot设置Content-Type的踩坑记录

文章讲述了在Springboot应用中,由于返回值类型为String导致Content-Type默认为text/plain的问题,以及如何通过修改返回值类型为void或使用@GetMapping注解的produces属性来正确设置Content-Type。还提到客户端的Accept字段对响应的影响。
摘要由CSDN通过智能技术生成

最近遇到一个需求,需要根据上传文件的类型,设置response的Content-Type,代码的简单示例如下!

@RequestMapping("/test")
@ResponseBody
public String test(HttpServletRequest request, HttpServletResponse response) throws IOException {
    // ......
    switch (fileExt) {
        case "JPEG":
        case "JPG":
            response.setContentType(MediaType.IMAGE_JPEG_VALUE);
            break;
        case "GIF":
            response.setContentType(MediaType.IMAGE_GIF_VALUE);
            break;
        case "PNG":
            response.setContentType(MediaType.IMAGE_PNG_VALUE);
            break;
        default:
            break;
    }
    // .......
    return "OK";
}

但是,在进行测试时,发现接口响应的Content-Type始终是text/plain,导致浏览器渲染异常。

对Springboot源码一步一步调试,最终找到问题原因:Springboot会根据接口的返回值类型,推断设置response的Content-Type。

已上述代码为例,返回值为String类型,在 AbstractMessageConverterMethodProcessor#writeWithMessageConverters方法中,selectedMediaType为text/plain,最后通过消息转换器的 AbstractHttpMessageConverter#write方法,设置response的Content-Type

protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
    ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
    throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    // ...
    if (selectedMediaType != null) {
        selectedMediaType = selectedMediaType.removeQualityValue();
	for (HttpMessageConverter<?> converter : this.messageConverters) {
	    GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
	    if (genericConverter != null ? 
                ((GenericHttpMessageConverter)converter).canWrite(targetType, valueType, selectedMediaType) :
		converter.canWrite(valueType, selectedMediaType)) {
		body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
			(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
			inputMessage, outputMessage);
		if (body != null) {
		    Object theBody = body;
		    LogFormatUtils.traceDebug(logger, traceOn ->
			"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
		    addContentDispositionHeader(inputMessage, outputMessage);
		    if (genericConverter != null) {
			genericConverter.write(body, targetType, selectedMediaType, outputMessage);
		    } else {
			((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
		    }
		} else {
		    if (logger.isDebugEnabled()) {
			logger.debug("Nothing to write: null body");
		    }
		}
		return;
	    }
	}
    }
    // ...
}
public final void write(final T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
	    throws IOException, HttpMessageNotWritableException {
    final HttpHeaders headers = outputMessage.getHeaders();
    // 设置Content-Type
    addDefaultHeaders(headers, t, contentType);
    // ...
}

所以,出现上述问题的原因就在于接口返回了一个字符串,对这个接口来说,返回值根本就是多余的,修改为void,问题解决。

拓展:设置Content-Type的其他方式

通过 @GetMapping等注解的 produces属性,来指定响应的MIME类型。

@GetMapping(value = "/test", produces = MediaType.TEXT_HTML_VALUE)
@ResponseBody
public String test1(HttpServletResponse response) throws IOException {  
    return "<h1>html</h1>";
}

@GetMapping(value = "/test", produces = MediaType.TEXT_PLAIN_VALUE)
@ResponseBody
public String test2(HttpServletResponse response) throws IOException {   
    return "<h1>plain</h1>";
}

客户端发起HTTP请求时,会在请求头中包含一个Accept字段,指示它可以接受哪些媒体类型(MIME Type)。如果响应的Content-Type与Accept不匹配,则会返回406 Not Acceptable。

produces属性的作用就是让框架根据请求头里的Accept属性决定由哪个方法来处理请求。在上述示例中,第一个方法只响应Accept为text/html的GET请求,第二个方法则只响应Accept为text/plain的GET请求。
在这里插入图片描述

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值