dma*_*a_k 28 spring spring-mvc mime-types
假设我有一个服务器GET请求并返回要序列化为JSON的bean,并且还提供了一个异常处理程序IllegalArgumentException,可以在服务中引发:
@RequestMapping(value = "/meta/{itemId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public MetaInformation getMetaInformation(@PathVariable int itemId) {
    return myService.getMetaInformation(itemId);
}
@ExceptionHandler(IllegalArgumentException.class)
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
@ResponseBody
public String handleIllegalArgumentException(IllegalArgumentException ex) {
    return ExceptionUtils.getStackTrace(ex);
}
消息转换器是:
<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
        <bean class="org.springframework.http.converter.StringHttpMessageConverter" />
    </mvc:message-converters>
</mvc:annotation-driven>
现在,当我在浏览器中请求给定的URL时,我看到了正确的JSON回复.但是,如果引发异常,则字符串化的异常也会转换为JSON,但我希望它能被处理StringHttpMessageConverter(产生的text/plainmime类型).我该怎么办?
为了使图片更完整(和复杂),假设我还有以下处理程序:
@RequestMapping(value = "/version", method = RequestMethod.GET)
@ResponseBody
public String getApplicationVersion() {
    return "1.0.12";
}
此处理程序允许返回字符串由两者序列化MappingJackson2HttpMessageConverter并StringHttpMessageConverter依赖于Accept-type客户端传递.返回类型和值应如下所示:
+----+---------------------+-----------------------+------------------+-------------------------------------+ | NN | URL | Accept-type | Content-type | Message converter | | | | request header | response header | | +----+---------------------+-----------------------+------------------+-------------------------------------+ | 1. | /version | text/html; */* | text/plain | StringHttpMessageConverter | | 2. | /version | application/json; */* | application/json | MappingJackson2HttpMessageConverter | | 3. | /meta/1 | text/html; */* | application/json | MappingJackson2HttpMessageConverter | | 4. | /meta/1 | application/json; */* | application/json | MappingJackson2HttpMessageConverter | | 5. | /meta/0 (exception) | text/html; */* | text/plain | StringHttpMessageConverter | | 6. | /meta/0 (exception) | application/json; */* | text/plain | StringHttpMessageConverter | +----+---------------------+-----------------------+------------------+-------------------------------------+
oeh*_*che 20
我想移除produces = MediaType.APPLICATION_JSON_VALUE从@RequestMapping的getMetaInformation会给你想要的结果.
响应类型将根据Accept标头中的content-type值进行协商.
编辑
由于这不包括方案3,4,这里是一个ResponseEntity.class直接使用的解决方案:
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleIllegalArgumentException(Exception ex) {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.TEXT_PLAIN);
    return new ResponseEntity<String>(ex.getMessage(), headers, HttpStatus.BAD_REQUEST);
}
dma*_*a_k 12
这个问题有几个方面:
StringHttpMessageConverter将catch-all mime类型添加*/*到支持的媒体类型列表中,同时MappingJackson2HttpMessageConverter仅绑定application/json.@RequestMapping被提供produces = ...,该值被存储在HttpServletRequest(见RequestMappingInfoHandlerMapping.handleMatch()),并且当错误处理程序被调用时,这个mime类型被自动继承和使用.简单的解决方案是StringHttpMessageConverter在列表中排在第一位:
<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <property name="supportedMediaTypes">
                <array>
                    <util:constant static-field="org.springframework.http.MediaType.TEXT_PLAIN" />
                </array>
            </property>
        </bean>
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
    </mvc:message-converters>
</mvc:annotation-driven>
并produces从@RequestMapping注释中删除:
@RequestMapping(value = "/meta/{itemId}", method = RequestMethod.GET)
@ResponseBody
public MetaInformation getMetaInformation(@PathVariable int itemId) {
    return myService.getMetaInformation(itemId);
}
现在:
StringHttpMessageConverter将放弃所有类型,仅MappingJackson2HttpMessageConverter可以处理(MetaInformation,java.util.Collection,等),使他们进一步通过.StringHttpMessageConverter则优先.到目前为止这么好,但不幸的是事情变得更加复杂ObjectToStringHttpMessageConverter.对于处理程序返回类型,java.util.Collection<MetaInformation>此消息转换器将报告它可以将此类型转换为java.lang.String.限制来自这样的事实:集合元素类型被擦除并且AbstractHttpMessageConverter.canWrite(Class<?> clazz, MediaType mediaType)方法获得java.util.Collection<?>类以进行检查,但是当涉及到转换步骤ObjectToStringHttpMessageConverter失败时.为了解决我们produces为@RequestMapping应该使用JSON转换器的注释保留的问题,但为了强制异常处理程序的正确内容类型,我们将擦除以下HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE属性HttpServletRequest:
@ExceptionHandler(IllegalArgumentException.class)
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
@ResponseBody
public String handleIllegalArgumentException(HttpServletRequest request, IllegalArgumentException ex) {
    request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
    return ExceptionUtils.getStackTrace(ex);
}
@RequestMapping(value = "/meta", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public Collection<MetaInformation> getMetaInformations() {
    return myService.getMetaInformations();
}
上下文保持原样:
<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
        <bean class="org.springframework.http.converter.ObjectToStringHttpMessageConverter">
            <property name="conversionService">
                <bean class="org.springframework.context.support.ConversionServiceFactoryBean" />
            </property>
            <property name="supportedMediaTypes">
                <array>
                    <util:constant static-field="org.springframework.http.MediaType.TEXT_PLAIN" />
                </array>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>
现在由于内容类型协商而正确处理场景(1,2,3,4),并且在异常处理程序中处理场景(5,6).
或者,可以用数组替换集合返回类型,然后解决方案#1再次适用:
@RequestMapping(value = "/meta", method = RequestMethod.GET)
@ResponseBody
public MetaInformation[] getMetaInformations() {
    return myService.getMetaInformations().toArray();
}
讨论:
我认为不AbstractMessageConverterMethodProcessor.writeWithMessageConverters()应该从值继承类,而是从方法签名继承:
Type returnValueType = returnType.getGenericParameterType();
并HttpMessageConverter.canWrite(Class<?> clazz, MediaType mediaType)应改为:
canWrite(Type returnType, MediaType mediaType)
或者(如果它过于限制潜在的基于类的转换器)
canWrite(Class<?> valueClazz, Type returnType, MediaType mediaType)
然后可以正确处理参数化类型,并且解决方案#1将再次适用.
| 归档时间: | 
 | 
| 查看次数: | 27216 次 | 
| 最近记录: |