SpringBoot @RequestBody 報錯 ('application/x-www-form-urlencoded;charset=UTF-8' not supported)


第一種:轉https://blog.csdn.net/chenfei2341/article/details/83652586

在Spring boot 中使用 @RequestBody 會報錯,提示錯誤 Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported,代碼如下:

    @RequestMapping(value = "/act/service/model/{modelId}/save", method = RequestMethod.POST)
    public void saveModel(@PathVariable String modelId, @RequestBody MultiValueMap<String, String> values) {
        // 具體代碼
    }

這個在傳統 spring MVC 中是有效的,但是在 Spring boot 中會報錯。
傳統是 Spring MVC 有效,是因為有 <mvc:annotation-driven> 注解,查資料,<mvc:annotation-driven> 注解配置了如下的內容
spring 3.1 版本:

<!-- 注解請求映射  -->
    <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">        
        <property name="interceptors">
            <list>  
                <ref bean="logNDCInteceptor"/>   <!-- 日志攔截器,這是你自定義的攔截器 -->
            </list>        
        </property>        
    </bean>      
    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <property name="messageConverters">  
            <list>  
                <ref bean="byteArray_hmc" />  
                <ref bean="string_hmc" />  
                <ref bean="resource_hmc" />  
                <ref bean="source_hmc" />  
                <ref bean="xmlAwareForm_hmc" />  
                <ref bean="jaxb2RootElement_hmc" />  
                <ref bean="jackson_hmc" />  
            </list>  
        </property>  
    </bean>  
    <bean id="byteArray_hmc" class="org.springframework.http.converter.ByteArrayHttpMessageConverter" /><!-- 處理.. -->
    <bean id="string_hmc" class="org.springframework.http.converter.StringHttpMessageConverter" /><!-- 處理.. -->
    <bean id="resource_hmc" class="org.springframework.http.converter.ResourceHttpMessageConverter" /><!-- 處理.. -->
    <bean id="source_hmc" class="org.springframework.http.converter.xml.SourceHttpMessageConverter" /><!-- 處理.. -->
    <bean id="xmlAwareForm_hmc" class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter" /><!-- 處理.. -->
    <bean id="jaxb2RootElement_hmc" class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter" /><!-- 處理.. -->
    <bean id="jackson_hmc" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" /><!-- 處理json-->

轉載:http://elf8848.iteye.com/blog/875830
這個找到的資料是 3.1 的,但是women可以看到,最后一個配置了 Jackson 的 json 處理程序,在更新的版本中,AnnotationMethodHandlerAdapter 已經廢棄,使用的是 RequestMappingHandlerAdapter,看下 RequestMappingHandlerAdapter 的源碼。

    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());
    }

這里面沒有了 json 的處理過程,我們把它加上

@EnableWebMvc
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
    
    @Bean
    public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
        RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
        
        List<HttpMessageConverter<?>> converters = adapter.getMessageConverters();

        MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
        List<MediaType> supportedMediaTypes = new ArrayList<MediaType>();
        MediaType textMedia = new MediaType(MediaType.TEXT_PLAIN, Charset.forName("UTF-8"));
        supportedMediaTypes.add(textMedia);
        MediaType jsonMedia = new MediaType(MediaType.APPLICATION_JSON, Charset.forName("UTF-8"));
        supportedMediaTypes.add(jsonMedia);jsonConverter.setSupportedMediaTypes(supportedMediaTypes);
        
        converters.add(jsonConverter);
        
        
        adapter.setMessageConverters(converters);
        
       return adapter;
    }
}

成功,報錯消除,正確獲取到了參數

第二種 轉自https://blog.csdn.net/justry_deng/article/details/99875548

SpringBoot自定義參數解析器,使被@RequestBody標注的參數能額外接收Content-Type為application/x-www-form-urlencoded的請求

基礎知識介紹:
       在SpringBoot里,若Controller層里方法的形參前使用了@RequestBody注解,那么該參數將會被RequestResponseBodyMethodProcessor解析器進行解析,若此時Content-Type為application/x-www-form-urlencoded,那么會報Unsupported Media Type錯誤,這就要求:請求的Content-Type必須為application/json了。

       一般的,若Controller層里方法的形參前(不管有沒有其它注解,只要)沒使用@RequestBody注解,那么該參數幾乎都是符合ServletModelAttributeMethodProcessor解析器要求的,進而會使用ServletModelAttributeMethodProcessor解析器進行解析;當請求的Content-Type為application/x-www-form-urlencoded時,幾乎用的都是ServletModelAttributeMethodProcessor解析器。

注:參數符合RequestResponseBodyMethodProcessor解析器要求的條件是
       parameter.hasParameterAnnotation(RequestBody.class)。可詳見
       RequestResponseBodyMethodProcessor類源碼。

注:參數符合ServletModelAttributeMethodProcessor解析器要求的條件是
       parameter.hasParameterAnnotation(ModelAttribute.class) ||
       this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType())。
       可詳見ModelAttributeMethodProcessor類源碼。

 

需求介紹及實現方式說明:
聲明:本文通過實現下述需求,進行示例。

需求介紹:

       實現被@RequestBody注解的參數,既能接收Content-Type為application/json的請求的參數值,又能接收Content-Type為application/x-www-form-urlencoded的請求的參數值。

實現方式概述:

       自定義參數解析器,當參數前有@RequestBody時,使用該解析器;在該解析器的內部,判斷Content-Type,若Content-Type為application/x-www-form-urlencoded,那么采用ServletModelAttributeMethodProcessor解析器;否者采用RequestResponseBodyMethodProcessor解析器。

注:即相當於對RequestResponseBodyMethodProcessor解析器進行了簡單的封裝。

具體實現代碼示例:

自定義參數解析器:

import org.springframework.core.MethodParameter;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor;
 
import javax.servlet.http.HttpServletRequest;
 
/**
 * 自定義參數解析器
 *
 * 注: 自定義參數解析器, 實現對RequestResponseBodyMethodProcessor的擴展
 *
 * 提示: 此解析器要實現的功能是: 若controller方法的參數前, 使用了@RequestBody注解, 那么解析此參數時,
 *                           1、若Content-Type為application/x-www-form-urlencoded,
 *                              那么走ServletModelAttributeMethodProcessor解析器
 *                           2、若Content-Type不為application/x-www-form-urlencoded,
 *                              那么走本應該走的RequestResponseBodyMethodProcessor解析器
 *
 * @author JustryDeng
 * @date 2019/8/19 19:47
 */
public class MyHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
 
    /**
     * 解析Content-Type為application/json的默認解析器是RequestResponseBodyMethodProcessor
     */
    private RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor;
 
    /**
     * 解析Content-Type為application/x-www-form-urlencoded的默認解析器是ServletModelAttributeMethodProcessor
     */
    private ServletModelAttributeMethodProcessor servletModelAttributeMethodProcessor;
 
    /**
     * 全參構造
     */
    public MyHandlerMethodArgumentResolver(RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor,
                                           ServletModelAttributeMethodProcessor servletModelAttributeMethodProcessor) {
        this.requestResponseBodyMethodProcessor = requestResponseBodyMethodProcessor;
        this.servletModelAttributeMethodProcessor = servletModelAttributeMethodProcessor;
    }
 
    /**
     * 當參數前有@RequestBody注解時, 解析該參數 會使用此 解析器
     *
     * 注:此方法的返回值將決定:是否使用此解析器解析該參數
     */
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.hasParameterAnnotation(RequestBody.class);
    }
 
    /**
     * 解析參數
     */
    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer,
                                  NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory)
                                  throws Exception {
        final String applicationXwwwFormUrlencoded = "application/x-www-form-urlencoded";
        HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
 
        if (request == null) {
            throw  new RuntimeException(" request must not be null!");
        }
        String contentType = request.getContentType();
        /*
         * 如果ContentType是application/x-www-form-urlencoded,那么使用ServletModelAttributeMethodProcessor解析器
         *
         * 注:其實默認的,當系統識別到參數前有@RequestBody注解時,就會走RequestResponseBodyMethodProcessor解析器;這里就
         *    相當於在走默認的解析器前走了個判斷而已。
         */
        if (applicationXwwwFormUrlencoded.equals(contentType)) {
            return servletModelAttributeMethodProcessor.resolveArgument(methodParameter,
                    modelAndViewContainer, nativeWebRequest, webDataBinderFactory);
        }
        return requestResponseBodyMethodProcessor.resolveArgument(methodParameter,
                    modelAndViewContainer, nativeWebRequest, webDataBinderFactory);
    }
}

注冊該參數解析器:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor;
 
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
 
/**
 * 配置注冊參數解析器
 *
 * @author JustryDeng
 * @date 2019/8/20 10:13
 */
@Configuration
public class ConfigArgumentResolvers {
 
    private final RequestMappingHandlerAdapter requestMappingHandlerAdapter;
 
    public ConfigArgumentResolvers(RequestMappingHandlerAdapter requestMappingHandlerAdapter) {
        this.requestMappingHandlerAdapter = requestMappingHandlerAdapter;
    }
 
    @PostConstruct
    private void addArgumentResolvers() {
        // 獲取到的是不可變的集合
        List<HandlerMethodArgumentResolver> argumentResolvers =
                                                    requestMappingHandlerAdapter.getArgumentResolvers();
        MyHandlerMethodArgumentResolver myHandlerMethodArgumentResolver =
                                                    getMyHandlerMethodArgumentResolver(argumentResolvers);
        // ha.getArgumentResolvers()獲取到的是不可變的集合,所以我們需要新建一個集合來放置參數解析器
        List<HandlerMethodArgumentResolver> myArgumentResolvers =
                                                    new ArrayList<>(argumentResolvers.size() + 1);
        // 將自定義的解析器,放置在第一個; 並保留原來的解析器
        myArgumentResolvers.add(myHandlerMethodArgumentResolver);
        myArgumentResolvers.addAll(argumentResolvers);
        requestMappingHandlerAdapter.setArgumentResolvers(myArgumentResolvers);
    }
 
    /**
     * 獲取MyHandlerMethodArgumentResolver實例
     */
    private MyHandlerMethodArgumentResolver getMyHandlerMethodArgumentResolver(
            List<HandlerMethodArgumentResolver> argumentResolversList) {
        // 解析Content-Type為application/json的默認解析器
        RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor = null;
        // 解析Content-Type為application/x-www-form-urlencoded的默認解析器
        ServletModelAttributeMethodProcessor servletModelAttributeMethodProcessor = null;
 
        if (argumentResolversList == null) {
            throw new RuntimeException("argumentResolverList must not be null!");
        }
        for (HandlerMethodArgumentResolver argumentResolver : argumentResolversList) {
            if (requestResponseBodyMethodProcessor != null && servletModelAttributeMethodProcessor != null) {
                break;
            }
            if (argumentResolver instanceof RequestResponseBodyMethodProcessor) {
                requestResponseBodyMethodProcessor = (RequestResponseBodyMethodProcessor)argumentResolver;
                continue;
            }
            if (argumentResolver instanceof ServletModelAttributeMethodProcessor) {
                servletModelAttributeMethodProcessor = (ServletModelAttributeMethodProcessor)argumentResolver;
            }
        }
        if (requestResponseBodyMethodProcessor == null || servletModelAttributeMethodProcessor == null) {
            throw new RuntimeException("requestResponseBodyMethodProcessor and "
                    + " servletModelAttributeMethodProcessor must not be null!");
        }
        return new MyHandlerMethodArgumentResolver(requestResponseBodyMethodProcessor,
                servletModelAttributeMethodProcessor);
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM