spring mvc messageConverters及406等问题

来源:互联网 发布:淘宝店铺代运营广告 编辑:程序博客网 时间:2024/05/19 12:38

提要:spring mvc 在@ResponseBody或者response.getWriter().print()的时候,默认会通过其内置的系列converters进行自动转换,接口如下:

org.springframework.http.converter.HttpMessageConverter<T>

其中,比较常用的有以下几个:

org.springframework.http.converter.StringHttpMessageConverter,实现string类型转换org.springframework.http.converter.ByteArrayHttpMessageConverter,二进制类型转换org.springframework.http.converter.MappingJackson2HttpMessageConverter,json类型转换

以上转换器,是通过对request里面的Accept属性进行逐个解析并匹配实现(逗号分隔),如果相关converter配置出现错误,就会导致异常发生,以及可能导致响应406出现。

一般出现这样的情况多半是直接配置了RequestMappingHandlerAdapter这个类(比如请求为json类型时,就会无法自动转换,返回结果响应406 not accepted),因为它默认只会注册以下几个转换器:

public RequestMappingHandlerAdapter() {        StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();        stringHttpMessageConverter.setWriteAcceptCharset(false);  // see SPR-7316        this.messageConverters = new ArrayList<HttpMessageConverter<?>>(4);        this.messageConverters.add(new ByteArrayHttpMessageConverter());        this.messageConverters.add(stringHttpMessageConverter);        this.messageConverters.add(new SourceHttpMessageConverter<Source>());        this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());    }

为了解决这个问题,我们可以将需要的转换器配置到Adapter类上,但是,配置后会覆盖原有的converters,所以,配置的时候需要配置全面。更为简单的做法是不去配置HandlerAdapter这个类,而是直接配置<mvc:annotation-driven />,这个配置会默认配置HandlerAdapter以及HandlerMapping,并且会自动注册大部分的converters:

AnnotationDrivenBeanDefinitionParser类下的parse方法:@Override    public BeanDefinition parse(Element element, ParserContext parserContext) {        Object source = parserContext.extractSource(element);        CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);        parserContext.pushContainingComponent(compDefinition);        RuntimeBeanReference contentNegotiationManager = getContentNegotiationManager(element, source, parserContext);        RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);        handlerMappingDef.setSource(source);        handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);        handlerMappingDef.getPropertyValues().add("order", 0);        handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);        String methodMappingName = parserContext.getReaderContext().registerWithGeneratedName(handlerMappingDef);        if (element.hasAttribute("enable-matrix-variables")) {            Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enable-matrix-variables"));            handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);        }        else if (element.hasAttribute("enableMatrixVariables")) {            Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enableMatrixVariables"));            handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);        }        configurePathMatchingProperties(handlerMappingDef, element, parserContext);        RuntimeBeanReference conversionService = getConversionService(element, source, parserContext);        RuntimeBeanReference validator = getValidator(element, source, parserContext);        RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element);        RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);        bindingDef.setSource(source);        bindingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);        bindingDef.getPropertyValues().add("conversionService", conversionService);        bindingDef.getPropertyValues().add("validator", validator);        bindingDef.getPropertyValues().add("messageCodesResolver", messageCodesResolver);        ManagedList<?> messageConverters = getMessageConverters(element, source, parserContext);        ManagedList<?> argumentResolvers = getArgumentResolvers(element, parserContext);        ManagedList<?> returnValueHandlers = getReturnValueHandlers(element, parserContext);        String asyncTimeout = getAsyncTimeout(element);        RuntimeBeanReference asyncExecutor = getAsyncExecutor(element);        ManagedList<?> callableInterceptors = getCallableInterceptors(element, source, parserContext);        ManagedList<?> deferredResultInterceptors = getDeferredResultInterceptors(element, source, parserContext);        RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);        handlerAdapterDef.setSource(source);        handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);        handlerAdapterDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);        handlerAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);        handlerAdapterDef.getPropertyValues().add("messageConverters", messageConverters);        addResponseBodyAdvice(handlerAdapterDef);        if (element.hasAttribute("ignore-default-model-on-redirect")) {            Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignore-default-model-on-redirect"));            handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);        }        else if (element.hasAttribute("ignoreDefaultModelOnRedirect")) {            // "ignoreDefaultModelOnRedirect" spelling is deprecated            Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignoreDefaultModelOnRedirect"));            handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);        }        if (argumentResolvers != null) {            handlerAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers);        }        if (returnValueHandlers != null) {            handlerAdapterDef.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);        }        if (asyncTimeout != null) {            handlerAdapterDef.getPropertyValues().add("asyncRequestTimeout", asyncTimeout);        }        if (asyncExecutor != null) {            handlerAdapterDef.getPropertyValues().add("taskExecutor", asyncExecutor);        }        handlerAdapterDef.getPropertyValues().add("callableInterceptors", callableInterceptors);        handlerAdapterDef.getPropertyValues().add("deferredResultInterceptors", deferredResultInterceptors);        String handlerAdapterName = parserContext.getReaderContext().registerWithGeneratedName(handlerAdapterDef);        String uriCompContribName = MvcUriComponentsBuilder.MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME;        RootBeanDefinition uriCompContribDef = new RootBeanDefinition(CompositeUriComponentsContributorFactoryBean.class);        uriCompContribDef.setSource(source);        uriCompContribDef.getPropertyValues().addPropertyValue("handlerAdapter", handlerAdapterDef);        uriCompContribDef.getPropertyValues().addPropertyValue("conversionService", conversionService);        parserContext.getReaderContext().getRegistry().registerBeanDefinition(uriCompContribName, uriCompContribDef);        RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);        csInterceptorDef.setSource(source);        csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService);        RootBeanDefinition mappedCsInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);        mappedCsInterceptorDef.setSource(source);        mappedCsInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);        mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, (Object) null);        mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, csInterceptorDef);        String mappedInterceptorName = parserContext.getReaderContext().registerWithGeneratedName(mappedCsInterceptorDef);        RootBeanDefinition exceptionHandlerExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);        exceptionHandlerExceptionResolver.setSource(source);        exceptionHandlerExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);        exceptionHandlerExceptionResolver.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);        exceptionHandlerExceptionResolver.getPropertyValues().add("messageConverters", messageConverters);        exceptionHandlerExceptionResolver.getPropertyValues().add("order", 0);        addResponseBodyAdvice(exceptionHandlerExceptionResolver);        String methodExceptionResolverName =                parserContext.getReaderContext().registerWithGeneratedName(exceptionHandlerExceptionResolver);        RootBeanDefinition responseStatusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);        responseStatusExceptionResolver.setSource(source);        responseStatusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);        responseStatusExceptionResolver.getPropertyValues().add("order", 1);        String responseStatusExceptionResolverName =                parserContext.getReaderContext().registerWithGeneratedName(responseStatusExceptionResolver);        RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);        defaultExceptionResolver.setSource(source);        defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);        defaultExceptionResolver.getPropertyValues().add("order", 2);        String defaultExceptionResolverName =                parserContext.getReaderContext().registerWithGeneratedName(defaultExceptionResolver);        parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, methodMappingName));        parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, handlerAdapterName));        parserContext.registerComponent(new BeanComponentDefinition(uriCompContribDef, uriCompContribName));        parserContext.registerComponent(new BeanComponentDefinition(exceptionHandlerExceptionResolver, methodExceptionResolverName));        parserContext.registerComponent(new BeanComponentDefinition(responseStatusExceptionResolver, responseStatusExceptionResolverName));        parserContext.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExceptionResolverName));        parserContext.registerComponent(new BeanComponentDefinition(mappedCsInterceptorDef, mappedInterceptorName));        // Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"        MvcNamespaceUtils.registerDefaultComponents(parserContext, source);        parserContext.popAndRegisterContainingComponent();        return null;    }

里面有个getMessageConverters():

private ManagedList<?> getMessageConverters(Element element, Object source, ParserContext parserContext) {        Element convertersElement = DomUtils.getChildElementByTagName(element, "message-converters");        ManagedList<? super Object> messageConverters = new ManagedList<Object>();        if (convertersElement != null) {            messageConverters.setSource(source);            for (Element beanElement : DomUtils.getChildElementsByTagName(convertersElement, "bean", "ref")) {                Object object = parserContext.getDelegate().parsePropertySubElement(beanElement, null);                messageConverters.add(object);            }        }        if (convertersElement == null || Boolean.valueOf(convertersElement.getAttribute("register-defaults"))) {            messageConverters.setSource(source);            messageConverters.add(createConverterDefinition(ByteArrayHttpMessageConverter.class, source));            RootBeanDefinition stringConverterDef = createConverterDefinition(StringHttpMessageConverter.class, source);            stringConverterDef.getPropertyValues().add("writeAcceptCharset", false);            messageConverters.add(stringConverterDef);            messageConverters.add(createConverterDefinition(ResourceHttpMessageConverter.class, source));            messageConverters.add(createConverterDefinition(SourceHttpMessageConverter.class, source));            messageConverters.add(createConverterDefinition(AllEncompassingFormHttpMessageConverter.class, source));            if (romePresent) {                messageConverters.add(createConverterDefinition(AtomFeedHttpMessageConverter.class, source));                messageConverters.add(createConverterDefinition(RssChannelHttpMessageConverter.class, source));            }            if (jackson2XmlPresent) {                RootBeanDefinition jacksonConverterDef = createConverterDefinition(MappingJackson2XmlHttpMessageConverter.class, source);                GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source);                jacksonFactoryDef.getPropertyValues().add("createXmlMapper", true);                jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef);                messageConverters.add(jacksonConverterDef);            }            else if (jaxb2Present) {                messageConverters.add(createConverterDefinition(Jaxb2RootElementHttpMessageConverter.class, source));            }            if (jackson2Present) {                RootBeanDefinition jacksonConverterDef = createConverterDefinition(MappingJackson2HttpMessageConverter.class, source);                GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source);                jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef);                messageConverters.add(jacksonConverterDef);            }            else if (gsonPresent) {                messageConverters.add(createConverterDefinition(GsonHttpMessageConverter.class, source));            }        }        return messageConverters;    }

我们可以通过配置定制需要的转换器,并配置register-defaults=true保证原有转换器不被覆盖.
另外,StringHttpMessageConverter默认字符集为”ISO-8859-1”,需要转换为”UTF-8”时,可以配置覆盖

public class StringHttpMessageConverter extends AbstractHttpMessageConverter<String> {    public static final Charset DEFAULT_CHARSET = Charset.forName("ISO-8859-1");
0 0
原创粉丝点击