剝開比原看代碼09:通過dashboard創建密鑰時,前端的數據是如何傳到后端的?

2019獨角獸企業重金招聘Python工程師標準>>> hot3.png

作者:freewind

比原項目倉庫:

Github地址:https://github.com/Bytom/bytom

Gitee地址:https://gitee.com/BytomBlockchain/bytom

在前面一篇文章,我們粗略的研究了一下比原的dashboard是如何做出來的,但是對里面提到的各種細節功能,并沒有深入的去研究。那么從本文開始,我們將在這一段時間,分別研究里面提到的每一項功能。

在前一篇文章中,當我們第一次在瀏覽器中打開dashboard時,因為還沒有創建過密鑰,所以比原會提示我們輸入一些別名和密碼,為我們創建一個密鑰和相應的帳戶。就是下面這張圖所對應的: 那么本文就將研究一下,當我們點擊了"Register"按鈕以后,我們在前端頁面上填寫的參數,到底是如何一步步的傳到比原的后端的。

跟之前一樣,我們將對這個問題進行細分,然后各個擊破:

  1. 前端:當我們填完表單,點了提交以后,比原在前端是如何發送數據的?
  2. 后端:比原的后端是如何接收到數據的?

前端:當我們填完表單,點了提交以后,數據會發送到后端的哪個接口?

當我們點擊了"Register"按鈕,在前端頁面中,一定會在某個地方觸發一個向比原節點webapi接口發出請求的操作。究竟是訪問的哪個web api?提交的數據又是什么樣的呢?讓我們先從前端代碼中尋找一下。

注意,比原的前端代碼位于另一個項目倉庫bytom/dashboard中。為了能與我們在本系列文章中使用的比原v1.0.1的代碼相匹配,我找到了dashboard中的v1.0.0的代碼,并且提交到了一個單獨的項目中:freewind/bytom-dashboard-v1.0.0。注意該項目代碼未做任何修改,其master分支對應于官方代碼倉庫的v1.0.0分支。之所以要弄一個單獨的出來,這是因為我們在文章中,每次引用一段代碼的時候,都會給出相應的github上的鏈接,方便讀者跳過去查看全貌,使用一個獨立項目,會讓這個過程更簡便一些。

由于比原的前端頁面是使用React為主的,所以我猜想在代碼中,也該會有一個名為Register的組件,或者某個表單中有一個名為Register的按鈕。經過搜索,我們幸運的發現了Register.jsx 這個組件文件,它正好是我們需要的。

經過高度簡化后的代碼如下:

src/features/app/components/Register/Register.jsx#L9-L148

class Register extends React.Component {// ...// 4. submitWithErrors(data) {return new Promise((resolve, reject) => {// 5. this.props.registerKey(data).catch((err) => reject({_error: err.message}))})}// ...render() {// ...return (// ...// 3.<form className={styles.form} onSubmit={handleSubmit(this.submitWithErrors)}>// 1.<TextFieldtitle={lang === 'zh' ? '賬戶別名' : 'Account Alias'}placeholder={lang === 'zh' ? '請輸入賬戶別名...' : 'Please enter the account alias...'}fieldProps={accountAlias} /><TextFieldtitle={lang === 'zh' ? '密鑰別名' : 'Key Alias'}placeholder={lang === 'zh' ? '請輸入密鑰別名...' : 'Please enter the key alias...'}fieldProps={keyAlias}/><TextFieldtitle={lang === 'zh' ? '密鑰密碼' : 'Key Password'}placeholder={lang === 'zh' ? '請輸入密鑰密碼...' : 'Please enter the key password...'}fieldProps={password}type='password'/><TextFieldtitle={lang === 'zh' ? '重復輸入密鑰密碼' : 'Repeat your key password'}placeholder={lang === 'zh' ? '請重復輸入密鑰密碼...' : 'Please repeat the key password...'}fieldProps={repeatPassword}type='password'/>// 2. <button type='submit' className='btn btn-primary' disabled={submitting}>{lang === 'zh' ? '注冊' : 'Register'}</button>// ....</form>// ...)}
}

上面的代碼,共有5個地方需要注意,被我用數字標示出來了。注意這5個數字并不是從上到下標注,而是按照我們關注的順序來的:

  1. 表單上的各個輸入框,就是我們填寫別名和密碼的地方。這里需要關注的是每個TextFieldfieldProps屬性,它對應我們提交到后臺的數據的name
  2. 就是那個“Register”按鈕了。需要注意的是,它的typesubmit,也就是說,點擊它以后,將會觸發所在formonSubmit方法
  3. 回到了form的開頭。注意它的onSubmit里面,調用的是handleSubmit(this.submitWithErrors)。其中的handleSubmit是從該表單所使用的第三方redux-form中傳入的,用來處理表單提交,我們在這里不關注它,只需要知道我們需要把自己的處理函數this.submitWithErrors傳給它。而在后者中,我們將會調用比原節點提供的web api
  4. 第3步中的this.submitWithErrors最終將走到這里定義的submitWithErrors函數
  5. submitWithErrors將會發起一個異步請求,最終調用由外部傳進來的registerKey函數

從這里我們還看不到調用的是哪個api,所以我們必須繼續去尋找registerKey。很快就在同文件中找到了registerKey

src/features/app/components/Register/Register.jsx#L176-L180

(dispatch) => ({registerKey: (token) => dispatch(actions.core.registerKey(token)),// ...})

它又將會調用actions.core.registerKey這個函數:

src/features/core/actions.js#L44-L87

const registerKey = (data) => {return (dispatch) => {// ...// 1.1const keyData = {'alias': data.keyAlias,'password': data.password}// 1.2return chainClient().mockHsm.keys.create(keyData).then((resp) => {// ...// 2.1const accountData = {'root_xpubs':[resp.data.xpub],'quorum':1,'alias': data.accountAlias}// 2.2dispatch({type: 'CREATE_REGISTER_KEY', data})// 2.3chainClient().accounts.create(accountData).then((resp) => {// ...// 2.4if(resp.status === 'success') {dispatch({type: 'CREATE_REGISTER_ACCOUNT', resp})}})// ...})// ...}
}

可以看到,在這個函數中,做的事情還是很多的。而且并不是我一開始預料的調用一次后臺接口就行了,而是調用了兩次(分別是創建密鑰和創建帳戶)。下面進行分析:

  1. 1.1是為了讓后臺創建密鑰而需要準備的參數,一個是alias,一個是password,它們都是用戶填寫的
  2. 1.2是調用后臺用于創建密鑰的接口,把keyData傳過去,并且拿到返回的resp后,進行后續的處理
  3. 2.1是為了讓后臺創建帳戶而需要準備的參數,分別是root_xpubs, quorumalias,其中root_xpubs是創建密鑰后返回的公鑰,quorum目前不知道(TODO),alias是用戶填寫的帳戶別名
  4. 2.2這一句沒有作用(經過官方確認了),因為我在代碼中沒有找到處理CREATE_REGISTER_KEY的代碼。可以看這個issue#28
  5. 2.3調用后臺創建帳戶,把accountData傳過去,可以拿到返回的resp
  6. 2.4調用成功后,再使用redux的dispatch函數分發一個CREATE_REGISTER_ACCOUNT信息。不過這個信息好像也沒有太大用處。

關于CREATE_REGISTER_ACCOUNT,我在代碼中找到了兩處相關:

  1. src/features/core/reducers.js#L229-L234
const accountInit = (state = false, action) => {if (action.type == 'CREATE_REGISTER_ACCOUNT') {return true}return state
}
  1. src/features/app/reducers.js#L10-L115
export const flashMessages = (state = {}, action) => {switch (action.type) {// ...case 'CREATE_REGISTER_ACCOUNT': {return newSuccess(state, 'CREATE_REGISTER_ACCOUNT')}// ...}
}

第一個看起來沒什么用,第二個應該是用來在操作完成后,顯示相關的錯誤信息。

那就讓我們把關注點放在1.22.3這兩個后臺調用的地方吧。

  1. chainClient().mockHsm.keys.create(keyData)對應的是:

src/sdk/api/mockHsmKeys.js#L3-L31

const mockHsmKeysAPI = (client) => {return {create: (params, cb) => {let body = Object.assign({}, params)const uri = body.xprv ? '/import-private-key' : '/create-key'return shared.tryCallback(client.request(uri, body).then(data => data),cb)},// ...}
}

可以看到在create方法中,如果找不到body.xprv(就是本文對應的情況),則會調用后臺的/create-key接口。經過一長串的跟蹤,我們終于找到了一個。

  1. chainClient().accounts.create(accountData)對應的是:

src/sdk/api/accounts.js#L3-L30

const accountsAPI = (client) => {return {create: (params, cb) => shared.create(client, '/create-account', params, {cb, skipArray: true}),// ...}
}

很快我們在這邊,也找到了創建帳戶時調用的接口為/create-account

前端這邊,我們終于分析完了。下一步,將進入比原的節點(也就是后端)。

后端:比原的后端是如何接收到數據的?

如果我們對前一篇文章還有印象的話,會記得比原在啟動之后,會在Node.initAndstartApiServer方法中啟動web api對應的http服務,并且在API.buildHandler()方法中會配置很多的功能點,其中一定會有我們這里調用的接口。

讓我們看看API.buildHandler方法:

api/api.go#L164-L244

func (a *API) buildHandler() {walletEnable := falsem := http.NewServeMux()if a.wallet != nil {walletEnable = true// ...m.Handle("/create-account", jsonHandler(a.createAccount))// ...m.Handle("/create-key", jsonHandler(a.pseudohsmCreateKey))// ...

很快,我們就發現了:

  1. /create-account: 對應a.createAccount
  2. /create-key: 對應a.pseudohsmCreateKey

讓我們先看一下a.pseudohsmCreateKey

api/hsm.go#L23-L32

func (a *API) pseudohsmCreateKey(ctx context.Context, in struct {Alias    string `json:"alias"`Password string `json:"password"`
}) Response {// ...
}

可以看到,pseudohsmCreateKey的第二個參數,是一個struct,它有兩個字段,分別是AliasPassword,這正好和前面從前端傳過來的參數keyData對應。那么這個參數的值是怎么由提交的JSON數據轉換過來的呢?上次我們說到,主要是由a.pseudohsmCreateKey外面套著的那個jsonHandler進行的,它會處理與http協議相關的操作,以及把JSON數據轉換成這里需要的Go類型的參數,pseudohsmCreateKey就可以直接用了。

由于在這個小問題中,我們問題的邊界是比原后臺是如何拿到數據的,所以我們到這里就可以停止對這個方法的分析了。它具體是怎么創建密鑰的,這在以后的文章中將詳細討論。

再看a.createAccount

api/accounts.go#L15-L30

// POST /create-account
func (a *API) createAccount(ctx context.Context, ins struct {RootXPubs []chainkd.XPub `json:"root_xpubs"`Quorum    int            `json:"quorum"`Alias     string         `json:"alias"`
}) Response {// ...
}

與前面一樣,這個方法的參數RootXPubsQuorumAlias也是由前端提交,并且由jsonHandler自動轉換好的。

當我們清楚了在本文中,前后端數據是如何交互的,就很容易推廣到更多的情景。在前端還在很多的頁面和表單,在很多地方都需要調用后端的接口,我相信按照本文的思路,應該都可以快速的找到。如果有比較特殊的情況,我們以后會再專門寫文章講解。

轉載于:https://my.oschina.net/u/3886279/blog/1861147

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/390077.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/390077.shtml
英文地址,請注明出處:http://en.pswp.cn/news/390077.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

面試題 17.24. 最大子矩陣

面試題 17.24. 最大子矩陣 給定一個正整數、負整數和 0 組成的 N M 矩陣&#xff0c;編寫代碼找出元素總和最大的子矩陣。 返回一個數組 [r1, c1, r2, c2]&#xff0c;其中 r1, c1 分別代表子矩陣左上角的行號和列號&#xff0c;r2, c2 分別代表右下角的行號和列號。若有多個…

js模擬form表單提交數據, js模擬a標簽點擊跳轉,避開使用window.open引起來的瀏覽器阻止問題...

js模擬form表單提交數據, js模擬a標簽點擊跳轉&#xff0c;避開使用window.open引起來的瀏覽器阻止問題 js模擬form表單提交數據源碼&#xff1a; /** * js模擬form表單提交 * param {object} 參數對象 * url 必填 提交地址 * methond 選填 默認post 提交方…

004. ES6之函數的擴展

2019獨角獸企業重金招聘Python工程師標準>>> 1. 函數參數的默認值 ES6 允許為函數的參數設置默認值&#xff0c; function log(x, y World) {console.log(x, y); }log(Hello) // Hello World log(Hello, China) // Hello China log(Hello, ) // Hello// 1. 參數變量…

數據結構 | 鏈表:1097 刪除重復元素

代碼提交之后一直說段錯誤。我以為是數組開的不夠大&#xff0c;但是隨著數組一點一點開大&#xff0c;還是有一個case沒有AC。最終我發現&#xff1a;是有個邊界條件沒有考慮到 void printList(const vector<Node>& a){if(!a.size()) return;FF(i,a.size()-1){print…

算法之美 : 位運算

上一小節我們用三道題了解一下面試過程中棧和隊列的常見面試題。本小節筆者將通過幾個 位運算 的題目來帶大家熟悉下常用的位運算知識。 相比于棧和隊列來講&#xff0c;筆者自身認為位運算需要掌握的知識就要多一些&#xff0c;包括對于數字的二進制表示&#xff0c;二進制的反…

447. 回旋鏢的數量

447. 回旋鏢的數量 給定平面上 n 對 互不相同 的點 points &#xff0c;其中 points[i] [xi, yi] 。回旋鏢 是由點 (i, j, k) 表示的元組 &#xff0c;其中 i 和 j 之間的距離和 i 和 k 之間的距離相等&#xff08;需要考慮元組的順序&#xff09;。 返回平面上所有回旋鏢的…

一名3年工作經驗的程序員應該具備的技能

本文轉自:https://m.imooc.com/article/details?article_id7557 前言 因為和同事有約定再加上LZ自己也喜歡做完一件事之后進行總結&#xff0c;因此有了這篇文章。這篇文章大部分內容都是面向整個程序員群體的&#xff0c;當然因為LZ本身是做Java開發的&#xff0c;因此有一部…

js 排序算法總結

1.冒泡排序 平均時間復雜度O(N2) 最好情況O(N)最壞情況O(N2) 空間復雜度O(1) function bubbleSort(arr){if(arr.length < 1)return arr;var flag 1; // 標識是否進行交換for(var i0; i < arr.length; i){if(i !0 && flag) break;for(var j0; j <…

524. 通過刪除字母匹配到字典里最長單詞

524. 通過刪除字母匹配到字典里最長單詞 給你一個字符串 s 和一個字符串數組 dictionary 作為字典&#xff0c;找出并返回字典中最長的字符串&#xff0c;該字符串可以通過刪除 s 中的某些字符得到。 如果答案不止一個&#xff0c;返回長度最長且字典序最小的字符串。如果答案…

django開發商城(提供初始數據,商城首頁及購物車)

1.爬取數據 2.json數據轉化為sql語句 3.新建輪播圖模型(模型名與sql語句對應表名相同) class Wheel(models.Model):imgmodels.CharField(max_length150)namemodels.CharField(max_length20)trackidmodels.CharField(max_length20) 4.終端打開mysql,執行插入語句 5.在首頁進行展…

多語言版希爾排序

2019獨角獸企業重金招聘Python工程師標準>>> 簡介 希爾排序(Shells Sort)是插入排序的一種又稱“縮小增量排序”&#xff08;Diminishing Increment Sort&#xff09;&#xff0c;是直接插入排序算法的一種更高效的改進版本。希爾排序是非穩定排序算法。該方法因D.L…

UML 中extend和include的區別

在UML用例圖中有兩種關系——包含和擴展&#xff0c;容易混淆&#xff0c;下面通過一張表來區別一下這兩種關系。 轉載于:https://www.cnblogs.com/yonyong/p/8555547.html

hdu 6301 Distinct Values(貪心)題解

題意&#xff1a;長為n的串&#xff0c;給你m個區間&#xff0c;這些區間內元素不重復&#xff0c;問這樣的串字典序最小為&#xff1f; 思路&#xff1a;用set保存當前能插入的元素&#xff0c;這樣就能直接插入最小元素了。對操作按l排序&#xff0c;因為排過的不用排&#x…

瀏覽器兼容CSS漸進增強 VS 優雅降級如何選擇

由于低級瀏覽器不支持 CSS3&#xff0c;但是 CSS3 特效太優秀不忍放棄&#xff0c;所以在高級瀏覽器中使用CSS3&#xff0c;而在低級瀏覽器只保證最基本的功能。二者的目的都是關注不同瀏覽器下的不同體驗&#xff0c;但是它們側重點不同&#xff0c;所以導致了工作流程上的不同…

細數sass安裝中遇到的坑

前言&#xff1a; 前兩天打算清理電腦的時候&#xff0c;遇到了一點特殊的問題&#xff0c;打算重裝一些東西&#xff0c;其中就有我一直用的順手的SASS預編譯工具。 但是在重裝的時候&#xff0c;我發現我居然不會用了&#xff1f;&#xff1f;&#xff1f; 靠&#xff0c;要不…

442. 數組中重復的數據

442. 數組中重復的數據 給定一個整數數組 a&#xff0c;其中1 ≤ a[i] ≤ n &#xff08;n為數組長度&#xff09;, 其中有些元素出現兩次而其他元素出現一次。 找到所有出現兩次的元素。 你可以不用到任何額外空間并在O(n)時間復雜度內解決這個問題嗎&#xff1f; 示例&am…

[bzoj 2726] 任務安排 (斜率優化 線性dp)

3月14日第三題&#xff01;&#xff01;&#xff01;&#xff08;雖然是15號發的qwq&#xff09; Description 機器上有N個需要處理的任務&#xff0c;它們構成了一個序列。這些任務被標號為1到N&#xff0c;因此序列的排列為1,2,3…N。這N個任務被分成若干批&#xff0c;每批…

2018年,牛客網小白月賽5

第一次啊&#xff0c;補題&#xff0c;希望大佬批評。 題目按我補題順序來的。 https://www.nowcoder.com/acm/contest/135#question H 題 最大公倍數 題意:給出兩個數&#xff0c;求最大公倍數 歐幾里德算法算出最大公約數k; 然后算出。最大公倍數即可 代碼如下&#xff1a; …

292. Nim 游戲

292. Nim 游戲 你和你的朋友&#xff0c;兩個人一起玩 Nim 游戲&#xff1a; 桌子上有一堆石頭。你們輪流進行自己的回合&#xff0c;你作為先手。每一回合&#xff0c;輪到的人拿掉 1 - 3 塊石頭。拿掉最后一塊石頭的人就是獲勝者。 假設你們每一步都是最優解。請編寫一個函…

0710 mux協議的作用(ppp撥號時如何和gprs進行at指令交互)

ppp撥號使gprs上網的同時如何和gprs模塊進行at指令的交互&#xff0c;這是一個問題。 在linux中&#xff0c;ppp撥號上網是內核中支持的&#xff0c;只需要在內核配置中選上。 ppp撥號的方式使gprs進行上網與at指令使gprs上網&#xff0c;兩者之間有不同。ppp是一個將用at指令使…