国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

spring中,為什么前端明明傳了值,后端卻接收不到

這篇具有很好參考價(jià)值的文章主要介紹了spring中,為什么前端明明傳了值,后端卻接收不到。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

問題場(chǎng)景

在進(jìn)行前后端的聯(lián)調(diào)時(shí),有時(shí)候會(huì)出現(xiàn),前端明明傳了值,后端接口卻接收不到的情況,這種情況常常讓人很苦惱,然后就會(huì)去仔細(xì)對(duì)比前后端的參數(shù)單詞是不是對(duì)應(yīng)上了,也會(huì)去檢查是不是前端的請(qǐng)求參數(shù)格式有問題,又或者是后端接口接收的參數(shù)格式有問題,一通檢查對(duì)比下來,發(fā)現(xiàn)都沒問題。那究竟是為什么呢?那就繼續(xù)往下看吧。


問題重現(xiàn)

控制層代碼:

    @PostMapping(value = "/test")
    public void test(@RequestBody UserVO userVO) {

        System.out.println("用戶代碼:" + userVO.getUCode());
        System.out.println("用戶名稱:" + userVO.getUName());

    }

參數(shù)實(shí)體類:UserVO

@Data
public class UserVO {

    /**
     * 用戶代碼
     */
    private Long uCode;

    /**
     * 用戶名稱
     */
    private String uName;

}

用postman模擬前端調(diào)用:

后臺(tái)實(shí)體類接收不到前端傳的值,Java,開發(fā)常見問題,技術(shù)分享,java,spring boot,spring,前端
控制臺(tái)預(yù)期打印結(jié)果:

用戶代碼:12345
用戶名稱:小明

控制臺(tái)實(shí)際打印結(jié)果:
后臺(tái)實(shí)體類接收不到前端傳的值,Java,開發(fā)常見問題,技術(shù)分享,java,spring boot,spring,前端


解決方式

在實(shí)體類的屬性上方加@JsonProperty注解,如下圖:

后臺(tái)實(shí)體類接收不到前端傳的值,Java,開發(fā)常見問題,技術(shù)分享,java,spring boot,spring,前端

然后測(cè)試控制臺(tái)打印結(jié)果:
后臺(tái)實(shí)體類接收不到前端傳的值,Java,開發(fā)常見問題,技術(shù)分享,java,spring boot,spring,前端


原因分析

首先我們先把實(shí)體類復(fù)原,并且加上一個(gè)新的屬性loginType

@Data
public class UserVO {

    /**
     * 用戶代碼
     */
    private Long uCode;

    /**
     * 用戶名稱
     */
    private String uName;

    /**
     * 登錄類型
     */
    private String loginType;

}

眼尖的同學(xué)可能會(huì)發(fā)現(xiàn)了,我新加的屬性loginType長(zhǎng)得是不是跟原來兩個(gè)屬性u(píng)Code和uName不太一樣,不一樣的點(diǎn)在于uCode和uName都是首字母小寫,第二個(gè)字母大寫的單詞,而loginType則不然。但是它們?nèi)挤像劮迕ǖ囊?guī)范,對(duì)吧。這時(shí)候可以猜測(cè),難道是這個(gè)原因?qū)е碌模?/p>

在這里我們先來簡(jiǎn)單驗(yàn)證下uCode、uName、loginType的情況

后臺(tái)實(shí)體類接收不到前端傳的值,Java,開發(fā)常見問題,技術(shù)分享,java,spring boot,spring,前端

后臺(tái)實(shí)體類接收不到前端傳的值,Java,開發(fā)常見問題,技術(shù)分享,java,spring boot,spring,前端
通過斷點(diǎn)發(fā)現(xiàn),uCode、uName是空的,loginType卻不是空的
然后我們將uCode、uName分別改為userCode、userName后再進(jìn)行測(cè)試

@Data
public class UserVO {

    /**
     * 用戶代碼
     */
    private Long userCode;

    /**
     * 用戶名稱
     */
    private String userName;

    /**
     * 登錄類型
     */
    private String loginType;

}

后臺(tái)實(shí)體類接收不到前端傳的值,Java,開發(fā)常見問題,技術(shù)分享,java,spring boot,spring,前端

后臺(tái)實(shí)體類接收不到前端傳的值,Java,開發(fā)常見問題,技術(shù)分享,java,spring boot,spring,前端

這個(gè)時(shí)候我們就可以得出結(jié)論,原因就是首字母小寫,第二個(gè)字母大寫的單詞的屬性是有問題的。

但是我們不禁要問,為啥呢?它這也符合駝峰命名法的規(guī)范啊。為什么它就有問題呢?感興趣的同學(xué)可以接著往下看。


原理分析

首先我們要知道,在Spring中,前后端之間數(shù)據(jù)傳輸會(huì)涉及到數(shù)據(jù)的序列化和反序列化的操作,并且SpringBoot默認(rèn)是使用Jackson作為JSON數(shù)據(jù)格式處理的類庫(kù)。

序列化:按照指定的格式、順序等將實(shí)體類對(duì)象轉(zhuǎn)換為JSON對(duì)象;
反序列化:將JSON對(duì)象中的字符串、數(shù)字等,將其轉(zhuǎn)換為實(shí)體對(duì)象;

那么現(xiàn)在咱們就來斷點(diǎn)調(diào)試Jackson的源碼來看看原因。為方便展示,我將實(shí)體類留下uName、loginType兩個(gè)屬性

@Data
public class UserVO {

    /**
     * 用戶名稱
     */
    private String uName;

    /**
     * 登錄類型
     */
    private String loginType;

}

開始調(diào)試:
Jackon主要是通過抽象類AbstractJackson2HttpMessageConverterreadJavaType方法將 HTTP 請(qǐng)求中的消息體轉(zhuǎn)換為對(duì)象,所以我們找到這部分代碼,對(duì)他進(jìn)行斷點(diǎn)調(diào)試:

后臺(tái)實(shí)體類接收不到前端傳的值,Java,開發(fā)常見問題,技術(shù)分享,java,spring boot,spring,前端
然后逐步斷點(diǎn),在上圖的第192行和第195行,它會(huì)調(diào)用ObjectMapper.readValue,然后斷點(diǎn)推進(jìn)到調(diào)用方法的核心地方ObjectMapper_readMapAndClose方法

后臺(tái)實(shí)體類接收不到前端傳的值,Java,開發(fā)常見問題,技術(shù)分享,java,spring boot,spring,前端
this._findRootDeserializer(ctxt, valueType);的大概意思就是根據(jù)類型找到反序列化器,注意在這邊是先從緩存中取,取到了的話就直接返回了。如果沒到下一步斷點(diǎn),在這邊你可以清除一下緩存。
后臺(tái)實(shí)體類接收不到前端傳的值,Java,開發(fā)常見問題,技術(shù)分享,java,spring boot,spring,前端
然后斷點(diǎn)繼續(xù)推進(jìn)到創(chuàng)建反序列化器的地方DeserializerCache._createDeserializer

如果你清除緩存或者重啟項(xiàng)目在調(diào)用時(shí)會(huì)直接進(jìn)入到這個(gè)創(chuàng)建反序列化器的地方,你直接在這個(gè)方法上打斷點(diǎn)就好了

后臺(tái)實(shí)體類接收不到前端傳的值,Java,開發(fā)常見問題,技術(shù)分享,java,spring boot,spring,前端
找到上圖中第164行的代碼,BeanDescription是類的描述的意思,所有的屬性都在這里被解析,然后我們斷點(diǎn)進(jìn)去看看。會(huì)進(jìn)入到POJOPropertiesCollector.collectAll方法,就是字面意思,收集所有。方法邏輯詳見下圖:
后臺(tái)實(shí)體類接收不到前端傳的值,Java,開發(fā)常見問題,技術(shù)分享,java,spring boot,spring,前端
執(zhí)行完this._addFields(props);props加入了uNameloginType

后臺(tái)實(shí)體類接收不到前端傳的值,Java,開發(fā)常見問題,技術(shù)分享,java,spring boot,spring,前端
執(zhí)行完this._addMethods(props);后發(fā)現(xiàn)props竟然多了一個(gè)uname

后臺(tái)實(shí)體類接收不到前端傳的值,Java,開發(fā)常見問題,技術(shù)分享,java,spring boot,spring,前端

在這里我們點(diǎn)開屬性詳細(xì)去看,會(huì)發(fā)現(xiàn)uName的get和set為空,但是loginType的是正常的,并且uname這個(gè)不知道哪里跑出來的屬性的get和set也是不為空的。

后臺(tái)實(shí)體類接收不到前端傳的值,Java,開發(fā)常見問題,技術(shù)分享,java,spring boot,spring,前端
后臺(tái)實(shí)體類接收不到前端傳的值,Java,開發(fā)常見問題,技術(shù)分享,java,spring boot,spring,前端

再接著執(zhí)行this._removeUnwantedProperties(props);移除不想要的屬性之后,會(huì)發(fā)現(xiàn)就剩下loginTypeuname了,因?yàn)?code>uName沒有g(shù)et和set。為什么
后臺(tái)實(shí)體類接收不到前端傳的值,Java,開發(fā)常見問題,技術(shù)分享,java,spring boot,spring,前端
然后props中目前存儲(chǔ)的就是loginTypeuname

現(xiàn)在我們就要弄明白為什么有g(shù)et/set的是uname而不是uName

首先,在這個(gè)例子中我使用的是@Data注解,也就是使用的 Lombok,也就是說 getter 和 setter 是由 Lombok 生成的。使用注解的話會(huì)將get/set方法隱藏起來,然后我們可以通過IDEA的Structure來看,見下圖:

后臺(tái)實(shí)體類接收不到前端傳的值,Java,開發(fā)常見問題,技術(shù)分享,java,spring boot,spring,前端

那么Jackson 到底是如何解析的,使得解析出來的是uname,而不是uName。它解析的具體代碼在com.fasterxml.jackson.databind.util.BeanUtil類中的legacyManglePropertyName方法中
后臺(tái)實(shí)體類接收不到前端傳的值,Java,開發(fā)常見問題,技術(shù)分享,java,spring boot,spring,前端

從上圖為我們可以很明顯的看到,通過這個(gè)方法之后getLoginType被解析成loginType了。那我們?cè)賮砜纯?code>uName,見下圖:
后臺(tái)實(shí)體類接收不到前端傳的值,Java,開發(fā)常見問題,技術(shù)分享,java,spring boot,spring,前端
從上圖斷點(diǎn)我們可以清晰的看見getUName被解析成uname了,按照我們正常的思維邏輯的話,loginType和uName都符合駝峰命名法的規(guī)范,那么uName對(duì)應(yīng)的get方法解析出來應(yīng)該是uName啊,為什么變成了uname呢?原因就在于這個(gè)legacyManglePropertyName方法的處理邏輯,它的邏輯大概是:

1.根據(jù)入?yún)ffset去除get或者get,然后就剩下UName或者LoginType了
2.然后從第一個(gè)字母開始解析,如果第一個(gè)字母是大寫的,于是就將它轉(zhuǎn)成小寫,然后找下一個(gè),如果還是大寫,就繼續(xù)轉(zhuǎn)成小寫,直到找到一個(gè)小寫字母后,就把之后的字母(不管大小寫)一起拼接進(jìn)來。

這樣就能解釋了:

去除get之后的LoginType找到第一個(gè)字母是大寫,轉(zhuǎn)為小寫的l,下一個(gè)字母是小寫的了,就直接把后面的全拼接進(jìn)來,最終形成了loginType

去除get之后的UName找到第一個(gè)字母是大寫,轉(zhuǎn)為小寫的u,下一個(gè)字母又是大寫,轉(zhuǎn)為小寫的n,在下一個(gè)字母是小寫的了,就直接把后面的全拼接進(jìn)來,最終形成了uname

如果說這邊的getUName換成getuName,那么解析出來的就是正確的uName了。

結(jié)論

到這里,我們就可以得出結(jié)論了

因?yàn)?Lombok 生成 get、set 方法的語(yǔ)義規(guī)范與和Jackson 處理 get、set 方法之間的不一致,導(dǎo)致屬性名無(wú)法匹配上,最終也就導(dǎo)致了前端明明傳了參數(shù),后端卻接收不到的問題。

擴(kuò)展

我后面去github的 lombok社區(qū) 了解了相關(guān)內(nèi)容,lombook社區(qū)是這樣描述的:

后臺(tái)實(shí)體類接收不到前端傳的值,Java,開發(fā)常見問題,技術(shù)分享,java,spring boot,spring,前端
用網(wǎng)頁(yè)翻譯給他翻譯成中文,翻譯有些不對(duì),但是能看明白大概意思就行

后臺(tái)實(shí)體類接收不到前端傳的值,Java,開發(fā)常見問題,技術(shù)分享,java,spring boot,spring,前端

lombok的大概意思就是:我就是這樣的規(guī)范,即使其他的工具框架都改了,我也不改,但是建議你們不要使用首字母小寫第二個(gè)字母大寫的屬性名,避免出現(xiàn)問題,可能知名度比較高的框架都比較傲嬌吧哈哈。

但是lombok還是給出了一個(gè)解決方案,加上這個(gè)配置項(xiàng)

lombok.accessors.capitalization = [basic | beanspec] (default: basic)

其中basic代表遵循lombok的規(guī)范(getUName);beanspec代表遵循Spring、Jackson 的規(guī)范(getuName)。默認(rèn)是basic。

看到這里,我就來總結(jié)一下能解決這個(gè)問題的三種方案吧

1. 加@JsonProperty注解強(qiáng)行指定屬性名

@Data
public class UserVO {

    /**
     * 用戶名稱
     */
    @JsonProperty(value = "uName")
    private String uName;

    /**
     * 登錄類型
     */
    private String loginType;

}

2.不使用lombok,使用IDEA默認(rèn)生成get/set方法

后臺(tái)實(shí)體類接收不到前端傳的值,Java,開發(fā)常見問題,技術(shù)分享,java,spring boot,spring,前端
后臺(tái)實(shí)體類接收不到前端傳的值,Java,開發(fā)常見問題,技術(shù)分享,java,spring boot,spring,前端
后臺(tái)實(shí)體類接收不到前端傳的值,Java,開發(fā)常見問題,技術(shù)分享,java,spring boot,spring,前端
后臺(tái)實(shí)體類接收不到前端傳的值,Java,開發(fā)常見問題,技術(shù)分享,java,spring boot,spring,前端

3.加上lombok配置項(xiàng)

lombok.accessors.capitalization = [basic | beanspec] (default: basic)

最后,博主的建議是,盡量不要用這種命名方式,如果非要用,那就加上@JsonProperty注解強(qiáng)行指定屬性名,這樣比較方便。文章來源地址http://www.zghlxwxcb.cn/news/detail-768147.html

到了這里,關(guān)于spring中,為什么前端明明傳了值,后端卻接收不到的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 為什么 OpenAI 團(tuán)隊(duì)采用 Python 開發(fā)他們的后端服務(wù)?

    為什么 OpenAI 團(tuán)隊(duì)采用 Python 開發(fā)他們的后端服務(wù)?

    Python,年齡可能比很多讀者都要大,但是它在更新快速的編程界卻一直表現(xiàn)出色,甚至有人把它比作是編程界的《葵花寶典》,只是Python的速成之法相較《葵花寶典》有過之而無(wú)不及。 Python簡(jiǎn)潔,高效的特點(diǎn),大大提升了程序員的編碼速度,極大的提高了程序員的辦公效率,

    2024年02月02日
    瀏覽(29)
  • Python爬蟲:從后端分析為什么你爬蟲爬取不到數(shù)據(jù)

    Python爬蟲:從后端分析為什么你爬蟲爬取不到數(shù)據(jù)

    僅僅是小編總結(jié)的三點(diǎn)而已,可能不是很全面,如果之后小編了解到新的知識(shí)點(diǎn),可能還會(huì)增加的哈! 1. 最簡(jiǎn)單的爬蟲代碼 也就是各位最常使用的,直接利用requests模塊訪問當(dāng)前網(wǎng)站鏈接,利用相關(guān)解析模塊從而獲取得到自己想要的數(shù)據(jù),如下(利用python爬蟲爬取自己csdn個(gè)人

    2024年02月09日
    瀏覽(28)
  • 什么是 Spring?為什么學(xué)它?

    歡迎來到本篇文章!在這里,我將帶領(lǐng)大家快速學(xué)習(xí) Spring 的基本概念,并解答兩個(gè)關(guān)鍵問題:什么是 Spring,以及為什么學(xué)習(xí) Spring。 廢話少說,下面,我們開始吧! Spring 官方文檔:https://docs.spring.io/spring-framework/docs/5.2.24.RELEASE/spring-framework-reference/ 簡(jiǎn)化我們的企業(yè)級(jí)應(yīng)用程

    2024年02月06日
    瀏覽(25)
  • 前端為什么發(fā)請(qǐng)求沒有攜帶cookie?

    前端為什么發(fā)請(qǐng)求沒有攜帶cookie?

    在前端發(fā)送請(qǐng)求時(shí),如果想要攜帶 cookie,通常只能攜帶存儲(chǔ)在與請(qǐng)求域名相同路徑的 cookie。這是由瀏覽器的同源策略所決定的。 同源策略要求請(qǐng)求的域名、協(xié)議和端口都必須一致,否則瀏覽器會(huì)限制跨域請(qǐng)求的權(quán)限。當(dāng)瀏覽器發(fā)送跨域請(qǐng)求時(shí),默認(rèn)情況下不會(huì)自動(dòng)攜帶 co

    2024年02月06日
    瀏覽(26)
  • 后端服務(wù)器的響應(yīng)為什么能夠精準(zhǔn)的返回給請(qǐng)求它的那個(gè)客戶端

    正向代理位于客戶端和目標(biāo)服務(wù)器之間,代表客戶端發(fā)送請(qǐng)求;–》代理客戶端 反向代理位于目標(biāo)服務(wù)器和客戶端之間,代表目標(biāo)服務(wù)器接收請(qǐng)求。–》代理服務(wù)端 當(dāng)客戶端發(fā)送請(qǐng)求到反向代理服務(wù)器時(shí),反向代理服務(wù)器會(huì)在轉(zhuǎn)發(fā)請(qǐng)求給后端服務(wù)器之前,記錄請(qǐng)求的源IP地址

    2024年02月12日
    瀏覽(24)
  • 《Go語(yǔ)言在微服務(wù)中的崛起:為什么Go是下一個(gè)后端之星?》

    《Go語(yǔ)言在微服務(wù)中的崛起:為什么Go是下一個(gè)后端之星?》

    ???? 博主貓頭虎???? 帶您進(jìn)入 Golang 語(yǔ)言的新世界???? ?? 博客首頁(yè) ——????貓頭虎的博客?? ?? 《面試題大全專欄》 ?? 文章圖文并茂??生動(dòng)形象??簡(jiǎn)單易學(xué)!歡迎大家來踩踩~?? ?? 《IDEA開發(fā)秘籍專欄》 ?? 學(xué)會(huì)IDEA常用操作,工作效率翻倍~?? ?? 《100天精通

    2024年02月09日
    瀏覽(35)
  • Spring為什么默認(rèn)是單例的?

    Spring為什么默認(rèn)是單例的?

    目錄 一、五種作用域 二、單例bean與原型bean的區(qū)別 ?三、單例Bean的優(yōu)勢(shì)與劣勢(shì) 一、五種作用域 1.singleton: singleton是Spring Bean的 默認(rèn)作用域 ,也就是單例模式。在整個(gè)應(yīng)用程序中,只會(huì)創(chuàng)建一個(gè)實(shí)例,Bean的所有請(qǐng)求都會(huì)共享這個(gè)實(shí)例。 2.prototype: prototype表示 原型模式 ,每次

    2024年02月07日
    瀏覽(23)
  • 【Spring】淺談spring為什么推薦使用構(gòu)造器注入

    因本人實(shí)力有限,該文章主要內(nèi)容(在文章基礎(chǔ)上加了點(diǎn)點(diǎn)東西)均來自: 原文鏈接:https://www.cnblogs.com/joemsu/p/7688307.html 作者:joemsu ? Spring框架對(duì)Java開發(fā)的重要性不言而喻,其核心特性就是IOC(Inversion of Control, 控制反轉(zhuǎn))和AOP,平時(shí)使用最多的就是其中的IOC,我們通過

    2024年02月13日
    瀏覽(24)
  • 有了Spring為什么還需要SpringBoot呢

    有了Spring為什么還需要SpringBoot呢

    目錄 一、Spring缺點(diǎn)分析 二、什么是Spring Boot 三、Spring Boot的核心功能 3.1 起步依賴 3.2 自動(dòng)裝配 1. 配置文件和依賴太多了!??! spring是一個(gè)非常優(yōu)秀的輕量級(jí)框架,以IOC(控制反轉(zhuǎn))和AOP(面向切面)為思想內(nèi)核,極大簡(jiǎn)化了JAVA企業(yè)級(jí)項(xiàng)目的開發(fā)。雖然Spring的組件代碼是輕

    2024年02月08日
    瀏覽(24)
  • 31、Spring容器啟動(dòng)時(shí),為什么先加載BeanFactoryPostProcess

    因?yàn)锽eanDefinition會(huì)在ioc容器加載的時(shí)候先注冊(cè), 而BeanFactoryPostProcess就是在所有的BeanDefinition注冊(cè)完后做擴(kuò)展的,所以要先加載BeanFactoryPostProcess 解析配置類的組件 它就實(shí)現(xiàn)BeanFactoryPostProcess, 所以要先去加載BeanFactoryPostProcess 方式一:通過BeanFactory獲取 方式二 :通過BeanFactor

    2024年02月04日
    瀏覽(27)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包