剥开比原看代码09:通过dashboard创建密钥时,前端的数据是如何传到后端的?
2019獨角獸企業重金招聘Python工程師標準>>>
作者:freewind
比原項目倉庫:
Github地址:https://github.com/Bytom/bytom
Gitee地址:https://gitee.com/BytomBlockchain/bytom
在前面一篇文章,我們粗略的研究了一下比原的dashboard是如何做出來的,但是對里面提到的各種細節功能,并沒有深入的去研究。那么從本文開始,我們將在這一段時間,分別研究里面提到的每一項功能。
在前一篇文章中,當我們第一次在瀏覽器中打開dashboard時,因為還沒有創建過密鑰,所以比原會提示我們輸入一些別名和密碼,為我們創建一個密鑰和相應的帳戶。就是下面這張圖所對應的: 那么本文就將研究一下,當我們點擊了"Register"按鈕以后,我們在前端頁面上填寫的參數,到底是如何一步步的傳到比原的后端的。
跟之前一樣,我們將對這個問題進行細分,然后各個擊破:
前端:當我們填完表單,點了提交以后,數據會發送到后端的哪個接口?
當我們點擊了"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個數字并不是從上到下標注,而是按照我們關注的順序來的:
從這里我們還看不到調用的是哪個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})}})// ...})// ...} }可以看到,在這個函數中,做的事情還是很多的。而且并不是我一開始預料的調用一次后臺接口就行了,而是調用了兩次(分別是創建密鑰和創建帳戶)。下面進行分析:
關于CREATE_REGISTER_ACCOUNT,我在代碼中找到了兩處相關:
第一個看起來沒什么用,第二個應該是用來在操作完成后,顯示相關的錯誤信息。
那就讓我們把關注點放在1.2和2.3這兩個后臺調用的地方吧。
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接口。經過一長串的跟蹤,我們終于找到了一個。
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))// ...很快,我們就發現了:
讓我們先看一下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,它有兩個字段,分別是Alias和Password,這正好和前面從前端傳過來的參數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 {// ... }與前面一樣,這個方法的參數RootXPubs、Quorum和Alias也是由前端提交,并且由jsonHandler自動轉換好的。
當我們清楚了在本文中,前后端數據是如何交互的,就很容易推廣到更多的情景。在前端還在很多的頁面和表單,在很多地方都需要調用后端的接口,我相信按照本文的思路,應該都可以快速的找到。如果有比較特殊的情況,我們以后會再專門寫文章講解。
轉載于:https://my.oschina.net/u/3886279/blog/1861147
總結
以上是生活随笔為你收集整理的剥开比原看代码09:通过dashboard创建密钥时,前端的数据是如何传到后端的?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 梦到自己喜欢的人是什么
- 下一篇: 梦里梦到老鼠什么预兆