欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring/SpringBoot?@RequestParam注解無法讀取application/json格式數(shù)據(jù)問題解決

 更新時(shí)間:2022年10月20日 10:06:37   作者:小菜雞cccc  
RequestParam用于將指定的請求參數(shù)賦值給方法中的形參,可以接受簡單類型屬性,也可以接受對象類型,一般用于GET請求,下面這篇文章主要給大家介紹了關(guān)于Spring/SpringBoot?@RequestParam注解無法讀取application/json格式數(shù)據(jù)問題解決的相關(guān)資料,需要的朋友可以參考下

前言

Emmmm…最近在做項(xiàng)目的途中,有遇到一個(gè)方法需要接收的參數(shù)只有一個(gè)或者較少的時(shí)候就懶得寫實(shí)體類去接收,使用spring框架都知道,接收單個(gè)參數(shù)就使用@RequestParam注解就好了,但是前端對應(yīng)的Content-type是需要改成application/x-www-form-urlencoded,所以在接口文檔上面特地標(biāo)記了。但是…不知道前端是格式改了但是參數(shù)還是用的json格式?jīng)]有改成鍵值對的方式傳遞還是什么原因,就一直說參數(shù)傳不過來,叫我改回json格式的。。我也實(shí)在是懶,另外一個(gè)也覺得沒必要,就一兩個(gè)參數(shù)就新建一個(gè)實(shí)體,太浪費(fèi),但是這個(gè)問題讓我覺得不靈活蠻久了,也一直沒找到辦法,所以借這個(gè)機(jī)會(huì),打開了我的開發(fā)神器,www.baidu.com…輸入我的問題,找了好久也沒找到有解決的方案,然后就想著看下Spring內(nèi)部是怎么處理的吧,就稍微跟了下源碼,下面就說下我解決的方案。

一、RequestMappingHandlerAdapter

RequestMappingHandlerAdapter實(shí)現(xiàn)了HandlerAdapter接口,顧名思義,表示handler的adapter,這里的handler指的是Spring處理具體請求的某個(gè)Controller的方法,也就是說HandlerAdapter指的是將當(dāng)前請求適配到某個(gè)Handler的處理器。

RequestMappingHandlerAdapter是HandlerAdapter的一個(gè)具體實(shí)現(xiàn),主要用于將某個(gè)請求適配給@RequestMapping類型的Handler處理,這里面就包含著請求數(shù)據(jù)和響應(yīng)數(shù)據(jù)的處理。

		// 這里可以獲取到處理程序方法參數(shù)解析器的一個(gè)列表
        List<HandlerMethodArgumentResolver> argumentResolvers =
                requestMappingHandlerAdapter.getArgumentResolvers()

如果是想處理響應(yīng)參數(shù)的話就使用

        //這里可以獲取到處理程序方法返回值的處理器
        List<HandlerMethodReturnValueHandler> originalHandlers = 
                 requestMappingHandlerAdapter.getReturnValueHandlers();

能獲取到這個(gè)列表了,那需要加入我們自己定義的處理器應(yīng)該不太麻煩了吧?(這里不講返回?cái)?shù)據(jù)的自定義策略處理,網(wǎng)上也有其他文章,如果需要可以找下)

二、HandlerMethodArgumentResolver

策略接口解決方法參數(shù)代入?yún)?shù)值在給定請求的上下文(翻譯的源碼注釋)

簡單的理解為:它負(fù)責(zé)處理你Handler方法里的所有入?yún)ⅲ喊ㄗ詣?dòng)封裝、自動(dòng)賦值、校驗(yàn)等等。

——————————————————————————————————————————

那么這個(gè)時(shí)候我已經(jīng)知道了第一步獲取到的那個(gè)列表中存放的類型是什么了,簡而言之,我們只需要實(shí)現(xiàn)這個(gè)策略類,編寫我們自己的算法或邏輯就行了

這個(gè)接口里面有兩個(gè)方法需要實(shí)現(xiàn):

第一個(gè)方法的作用:是否與給定方法的參數(shù)是由該解析器的支持。(如果返回true,那么就使用該類進(jìn)行參數(shù)轉(zhuǎn)換,如果返回false,那么繼續(xù)找下一個(gè)策略類)

第二個(gè)方法的作用:解決方法參數(shù)成從給定請求的自變量值。 由WebDataBinderFactory提供了一個(gè)方法來創(chuàng)建一個(gè)WebDataBinder所需數(shù)據(jù)綁定和類型轉(zhuǎn)換目的時(shí)實(shí)例。(簡單來講,就是轉(zhuǎn)換參數(shù)值的,返回的就是解析的參數(shù)值)

三、RequestParamMethodArgumentResolver

這個(gè)類就是用來處理Controller的方法上有加@RequestParam注解的具體處理器。

首先會(huì)調(diào)用這個(gè)方法來確定是否使用這個(gè)處理器解析參數(shù),那么我們也看到了,如果參數(shù)有RequestParam注解,那么則會(huì)使用該類進(jìn)行處理,那么我們能不能效仿呢?

四、MyHandlerMethodArgumentResolver

這個(gè)沒啥好說,就自己定義的參數(shù)解析器。

直接上代碼吧

/**
 * @BelongsProject: 
 * @BelongsPackage: 
 * @Author: hef
 * @CreateTime: 2020-06-20 18:49
 * @Description: 描述
 */
public class MyHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {


    /**
     * 這個(gè)是處理@RequestParam注解的原本策略類
     */
    private RequestParamMethodArgumentResolver requestParamMethodArgumentResolver;

    /**
     * 全參構(gòu)造
     */
    public MyHandlerMethodArgumentResolver(RequestParamMethodArgumentResolver requestParamMethodArgumentResolver) {
        this.requestParamMethodArgumentResolver = requestParamMethodArgumentResolver;
    }

    /**
     * 當(dāng)參數(shù)前有@RequestParam注解時(shí),會(huì)使用此 解析器
     * <p>
     * 注:此方法的返回值將決定:是否使用此解析器解析該參數(shù)
     */
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
    	//很明顯,就是判斷是否有這個(gè)注解
        return methodParameter.hasParameterAnnotation(RequestParam.class);
    }


    /**
     * 解析參數(shù)
     */
    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer,
                                  NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory)
            throws Exception {
        final String applicationJson = "application/json";
        HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
        if (request == null) {
            throw new RuntimeException(" request must not be null!");
        }
        //獲取到內(nèi)容類型
        String contentType = request.getContentType();
        //如果類型是屬于json 那么則跑自己解析的方法
        if (null != contentType && contentType.contains(applicationJson )) {
        	//獲取參數(shù)名稱
            String parameterName = methodParameter.getParameterName();
            //獲取參數(shù)類型
            Class<?> parameterType = methodParameter.getParameterType();
			//因?yàn)閖son數(shù)據(jù)是放在流里面,所以要去讀取流,
			//但是ServletRequest的getReader()和getInputStream()兩個(gè)方法只能被調(diào)用一次,而且不能兩個(gè)都調(diào)用。
			//所以這里是需要寫個(gè)自定義的HttpServletRequestWrapper,主要功能就是需要重復(fù)讀取流數(shù)據(jù)
            String read = getRead(request.getReader());
            //轉(zhuǎn)換json
            JSONObject jsonObject = JSON.parseObject(read);
            Object o1;
            if (jsonObject == null) {
            	//這里有一個(gè)可能性就是比如get請求,參數(shù)是拼接在URL后面,但是如果我們還是去讀流里面的數(shù)據(jù)就會(huì)讀取不到
                Map<String, String[]> parameterMap = request.getParameterMap();
                o1 = parameterMap.get(parameterName);
            }else {
                o1 = jsonObject.get(parameterName);
            }
            Object arg = null;
            //如果已經(jīng)獲取到了值的話那么再做類型轉(zhuǎn)換
            if (o1 != null) {
                WebDataBinder binder = webDataBinderFactory.createBinder(nativeWebRequest, null, parameterName);
                arg = binder.convertIfNecessary(o1, parameterType, methodParameter);
            }
            return arg;
        }
		//否則跑原本的策略類.
        Object o = requestParamMethodArgumentResolver.resolveArgument(methodParameter,
                modelAndViewContainer, nativeWebRequest, webDataBinderFactory);
        return o;
    }

    /**
     * 流轉(zhuǎn)字符串
     *
     * @param bf
     * @return
     */
    private static String getRead(BufferedReader bf) {
        StringBuilder sb = new StringBuilder();
        try {
            char[] buff = new char[1024];
            int len;
            while ((len = bf.read(buff)) != -1) {
                sb.append(buff, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sb.toString();
    }
}

四、ConfigArgumentResolvers

自己的策略類已經(jīng)寫好了,那么怎么加入到配置中去呢?

/**
 * @BelongsProject: 
 * @BelongsPackage: 
 * @Author: hef
 * @CreateTime: 2020-06-20 18:49
 * @Description: 描述
 */
@Configuration
public class ConfigArgumentResolvers {
    private final RequestMappingHandlerAdapter requestMappingHandlerAdapter;

    public ConfigArgumentResolvers(RequestMappingHandlerAdapter requestMappingHandlerAdapter) {
        this.requestMappingHandlerAdapter = requestMappingHandlerAdapter;
    }

	//springBoot啟動(dòng)的時(shí)候執(zhí)行
    @PostConstruct
    private void addArgumentResolvers() {
        // 獲取到框架定義好的參數(shù)解析集合
        List<HandlerMethodArgumentResolver> argumentResolvers =
                requestMappingHandlerAdapter.getArgumentResolvers();
        MyHandlerMethodArgumentResolver myHandlerMethodArgumentResolver = getMyHandlerMethodArgumentResolver(argumentResolvers);
        // ha.getArgumentResolvers()獲取到的是不可變的集合,所以我們需要新建一個(gè)集合來放置參數(shù)解析器
        List<HandlerMethodArgumentResolver> myArgumentResolvers =
                new ArrayList<>(argumentResolvers.size() + 1);
        //這里有一個(gè)注意點(diǎn)就是自定義的處理器需要放在RequestParamMethodArgumentResolver前面
        //為什么呢?因?yàn)槿绻旁谒竺娴脑?那么它已經(jīng)處理掉了,就到不了我們自己定義的策略里面去了
        //所以直接把自定義的策略放在第一個(gè),穩(wěn)妥!
        // 將自定義的解析器,放置在第一個(gè); 并保留原來的解析器
        myArgumentResolvers.add(myHandlerMethodArgumentResolver);
        myArgumentResolvers.addAll(argumentResolvers);
        //再把新的集合設(shè)置進(jìn)去
        requestMappingHandlerAdapter.setArgumentResolvers(myArgumentResolvers);
    }

    /**
     * 獲取MyHandlerMethodArgumentResolver實(shí)例
     */
    private MyHandlerMethodArgumentResolver getMyHandlerMethodArgumentResolver(
            List<HandlerMethodArgumentResolver> argumentResolversList) {
        // 原本處理RequestParam的類
        RequestParamMethodArgumentResolver requestParamMethodArgumentResolver = null;

        if (argumentResolversList == null) {
            throw new RuntimeException("argumentResolverList must not be null!");
        }
        for (HandlerMethodArgumentResolver argumentResolver : argumentResolversList) {
            if (requestParamMethodArgumentResolver != null) {
                break;
            }
            if (argumentResolver instanceof RequestParamMethodArgumentResolver) {
            // 因?yàn)樵谖覀冏约翰呗岳锩媸沁€需要用到這個(gè)原本的類的,所以需要得到這個(gè)對象實(shí)例
                requestParamMethodArgumentResolver = (RequestParamMethodArgumentResolver) argumentResolver;
            }
        }
        if (requestParamMethodArgumentResolver == null) {
            throw new RuntimeException("RequestParamMethodArgumentResolver not be null!");
        }
        //實(shí)例化自定義參數(shù)解析器
        return new MyHandlerMethodArgumentResolver(requestParamMethodArgumentResolver);
    }
}

五、MyHttpServletRequestWrapper

這個(gè)就是自定義的HttpServletRequest,保證可以重復(fù)獲取到流數(shù)據(jù)

/**
 * @BelongsProject: 
 * @BelongsPackage: 
 * @Author: hef
 * @CreateTime: 2020-06-22 16:29
 * @Description: 描述
 */
public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {
    private final byte[] body;

    public MyHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        //在讀取流之前獲取一次這個(gè)parameterMap,否則讀取流后無法再解析出數(shù)據(jù),
        // 原因是org.apache.catalina.connector.Request里面有usingInputStream 和 usingReader兩個(gè)全局變量記錄流是否被讀取過
        //org.apache.catalina.connector.Request里面的parseParameters方法就是用來解析請求參數(shù)(Parse request parameters.)
        //在解析參數(shù)之前會(huì)有一個(gè)判斷,如果流被讀取過 則不再解析請求參數(shù) //
        // if (usingInputStream || usingReader) { 這是源碼里面的判斷
        //                success = true;
        //                return;
        //            }
        //如果先請求過一次后,那么org.apache.catalina.util.ParameterMap里面會(huì)有一個(gè)locked狀態(tài),如果讀過一次之后 會(huì)變成鎖定狀態(tài) 那么后面再讀都是讀取解析過后的map
        //    /**
        //     * The current lock state of this parameter map.
        //     */
        //    private boolean locked = false;
        request.getParameterMap();
        body = ReadAsChars(request).getBytes(Charset.forName("UTF-8"));
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {

        final ByteArrayInputStream bais = new ByteArrayInputStream(body);

        return new ServletInputStream() {

            @Override
            public int read() throws IOException {
                return bais.read();
            }

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {

            }
        };
    }

	/**
     * 解析流
     * @param request
     * @return
     */
	public static String ReadAsChars(ServletRequest request)
        {
            InputStream is = null;
            StringBuilder sb = new StringBuilder();
            try
            {
                is = request.getInputStream();

                byte[] b = new byte[4096];
                for (int n; (n = is.read(b)) != -1;)
                {
                    sb.append(new String(b, 0, n));
                }
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
            finally
            {
                if (null != is)
                {
                    try
                    {
                        is.close();
                    }
                    catch (IOException e)
                    {
                        e.printStackTrace();
                    }
                }
            }
            return sb.toString();
        }
}

六、HttpServletRequestReplacedFilter

替換掉原本的Request對象,使用自定義的

/**
 * @BelongsProject: 
 * @BelongsPackage: 
 * @Author: hef
 * @CreateTime: 2020-06-22 16:47
 * @Description: 描述
 */
@Component
public class HttpServletRequestReplacedFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        ServletRequest requestWrapper = null;
        if(request instanceof HttpServletRequest) {
            requestWrapper = new MyHttpServletRequestWrapper((HttpServletRequest) request);
        }
        if(null == requestWrapper) {
            chain.doFilter(request, response);
        } else {
            chain.doFilter(requestWrapper, response);
        }
    }
}

七、總結(jié)

如果是想@RequestBody接收表單形式的參數(shù)也可以用此方法,處理起來更簡單 ,只需要實(shí)例化自定義處理器的時(shí)候傳入另外兩個(gè)個(gè)處理器就可以了

    /**
     * 解析Content-Type為application/json的默認(rèn)解析器是RequestResponseBodyMethodProcessor
     */
    private RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor;

    /**
     * 解析Content-Type為application/x-www-form-urlencoded的默認(rèn)解析器是ServletModelAttributeMethodProcessor
     */
    private ServletModelAttributeMethodProcessor servletModelAttributeMethodProcessor;

到這一步就已經(jīng)實(shí)現(xiàn)了RequestParam注解也可以接受Json格式數(shù)據(jù)了,我也沒進(jìn)行更多的測試,具體還會(huì)出現(xiàn)什么關(guān)聯(lián)性的問題暫時(shí)是沒發(fā)現(xiàn),后續(xù)如果有碼友出現(xiàn)了什么問題可以留言一起討論,本人小菜雞一枚,希望寫的不好的地方大神多多指教,不勝感激!

總結(jié)

到此這篇關(guān)于Spring/SpringBoot @RequestParam注解無法讀取application/json格式數(shù)據(jù)問題解決的文章就介紹到這了,更多相關(guān)@RequestParam注解無法讀取application/json內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • MyBatis的mapper.xml文件中入?yún)⒑头祷刂档膶?shí)現(xiàn)

    MyBatis的mapper.xml文件中入?yún)⒑头祷刂档膶?shí)現(xiàn)

    這篇文章主要介紹了MyBatis的mapper.xml文件中入?yún)⒑头祷刂档膶?shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • JAVA開發(fā)環(huán)境Vs?code配置步驟詳解

    JAVA開發(fā)環(huán)境Vs?code配置步驟詳解

    這篇文章主要為大家介紹了JAVA開發(fā)環(huán)境Vs?code配置步驟詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-04-04
  • java通過isAccessAllowed方法實(shí)現(xiàn)訪問控制

    java通過isAccessAllowed方法實(shí)現(xiàn)訪問控制

    在Web應(yīng)用開發(fā)中,使用Apache Shiro框架的isAccessAllowed方法可以有效管理用戶的訪問權(quán)限,本文詳細(xì)解析了該方法的實(shí)現(xiàn)過程,包括用戶身份驗(yàn)證、權(quán)限判斷和安全性分析,下面就一起來了解一下
    2024-09-09
  • java8如何根據(jù)某一屬性條件快速篩選list中的集合

    java8如何根據(jù)某一屬性條件快速篩選list中的集合

    這篇文章主要介紹了java8如何根據(jù)某一屬性條件快速篩選list中的集合,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • springboot validator枚舉值校驗(yàn)功能實(shí)現(xiàn)

    springboot validator枚舉值校驗(yàn)功能實(shí)現(xiàn)

    這篇文章主要介紹了springboot validator枚舉值校驗(yàn)功能實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • java向上轉(zhuǎn)型與向下轉(zhuǎn)型詳解

    java向上轉(zhuǎn)型與向下轉(zhuǎn)型詳解

    這篇文章主要為大家詳細(xì)介紹了java向上轉(zhuǎn)型與向下轉(zhuǎn)型,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-09-09
  • Java?設(shè)計(jì)模式以虹貓藍(lán)兔的故事講解單例模式

    Java?設(shè)計(jì)模式以虹貓藍(lán)兔的故事講解單例模式

    單例模式(Singleton?Pattern)是?Java?中最簡單的設(shè)計(jì)模式之一。這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式
    2022-03-03
  • mybatisplus實(shí)現(xiàn)自動(dòng)創(chuàng)建/更新時(shí)間的項(xiàng)目實(shí)踐

    mybatisplus實(shí)現(xiàn)自動(dòng)創(chuàng)建/更新時(shí)間的項(xiàng)目實(shí)踐

    Mybatis-Plus提供了自動(dòng)填充功能,可以通過實(shí)現(xiàn)MetaObjectHandler接口來實(shí)現(xiàn)自動(dòng)更新時(shí)間的功能,本文就來介紹一下mybatisplus實(shí)現(xiàn)自動(dòng)創(chuàng)建/更新時(shí)間的項(xiàng)目實(shí)踐,感興趣的可以了解下
    2024-01-01
  • Netty粘包問題的常見解決方案

    Netty粘包問題的常見解決方案

    粘包和拆包問題也叫做粘包和半包問題,它是指在數(shù)據(jù)傳輸時(shí),接收方未能正常讀取到一條完整數(shù)據(jù)的情況(只讀取了部分?jǐn)?shù)據(jù),或多讀取到了另一條數(shù)據(jù)的情況)就叫做粘包或拆包問題,本文介紹了Netty如何解決粘包問題,需要的朋友可以參考下
    2024-06-06
  • java可變參數(shù)(不定向參數(shù))的作用與實(shí)例

    java可變參數(shù)(不定向參數(shù))的作用與實(shí)例

    這篇文章主要給大家介紹了關(guān)于java可變參數(shù)(不定向參數(shù))的作用與實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04

最新評論