剥开比原看代码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.mzph.cn/news/390077.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

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. 参数变量…

一名3年工作经验的程序员应该具备的技能

本文转自:https://m.imooc.com/article/details?article_id7557 前言 因为和同事有约定再加上LZ自己也喜欢做完一件事之后进行总结&#xff0c;因此有了这篇文章。这篇文章大部分内容都是面向整个程序员群体的&#xff0c;当然因为LZ本身是做Java开发的&#xff0c;因此有一部…

多语言版希尔排序

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

浏览器兼容CSS渐进增强 VS 优雅降级如何选择

由于低级浏览器不支持 CSS3&#xff0c;但是 CSS3 特效太优秀不忍放弃&#xff0c;所以在高级浏览器中使用CSS3&#xff0c;而在低级浏览器只保证最基本的功能。二者的目的都是关注不同浏览器下的不同体验&#xff0c;但是它们侧重点不同&#xff0c;所以导致了工作流程上的不同…

2018年,牛客网小白月赛5

第一次啊&#xff0c;补题&#xff0c;希望大佬批评。 题目按我补题顺序来的。 https://www.nowcoder.com/acm/contest/135#question H 题 最大公倍数 题意:给出两个数&#xff0c;求最大公倍数 欧几里德算法算出最大公约数k; 然后算出。最大公倍数即可 代码如下&#xff1a; …

爬虫笔记(十二)——浏览器伪装技术

为什么要进行浏览器伪装技术&#xff1f; 有一些网站为了避免爬虫的恶意访问&#xff0c;会设置一些反爬虫机制&#xff0c;对方服务器会对爬虫进行屏蔽。常见的饭爬虫机制主要有下面几个&#xff1a; 1. 通过分析用户请求的Headers信息进行反爬虫 2. 通过检测用户行为进行反…

《活出生命的意义》:人生有何意义?

在你一生的阅读体验中&#xff0c;如果能够有一本书&#xff0c;它的某个章节、某种思想、或者某句话能够触动你的内心&#xff0c;解决你的困惑&#xff0c;甚至能改变你的命运&#xff0c;那这样的一本书你一定要视如珍宝&#xff0c;经常翻阅&#xff0c;维克多弗兰克尔的《…

右键添加git-bash

主要&#xff1a; 右键如果没有git-bash&#xff0c;如何给右键手动添加 前面对右键存在git-bash但使用出现问题的解决&#xff0c;也想到如果右键都没有&#xff0c;该如何给右键添加了&#xff0c;于是接着记录下如何添加的过程&#xff1a; 情形&#xff1a; 手动给右键添加…

Weblogic的缓存

2019独角兽企业重金招聘Python工程师标准>>> 最近遇到一个关于weblogic缓存的问题。再把war包放入到weblogic指定目录启动以后&#xff0c;访问页面信息没有更新。最后发现是\weblogic\user_projects\domains\base_domain\servers\AdminServer下的文件没有清除&…

725. 分隔链表

725. 分隔链表 给你一个头结点为 head 的单链表和一个整数 k &#xff0c;请你设计一个算法将链表分隔为 k 个连续的部分。 每部分的长度应该尽可能的相等&#xff1a;任意两部分的长度差距不能超过 1 。这可能会导致有些部分为 null 。 这 k 个部分应该按照在链表中出现的顺…

LAMP介绍-MySQL安装

2019独角兽企业重金招聘Python工程师标准>>> LAMP: linux-apache-mysql-php &#xff08;安装方式有&#xff1a;rpm&#xff0c;源码&#xff0c;二进制免编译&#xff09; linux-操作系统 apache-web服务软件&#xff08;httpd&#xff09; mysql-存储数据库 php…

java1

不知道为啥粘贴的图片是一堆编码。。。。 如何插入图片 博客后后台MarkDown编辑器上只有一个按钮&#xff0c;就是用来上传图片并自动插入MarkDown标记的&#xff0c;超级好用 &#xff08;一&#xff09;学习总结 1.在java中通过Scanner类完成控制台的输入&#xff0c;查阅JDK…

430. 扁平化多级双向链表

430. 扁平化多级双向链表 多级双向链表中&#xff0c;除了指向下一个节点和前一个节点指针之外&#xff0c;它还有一个子链表指针&#xff0c;可能指向单独的双向链表。这些子列表也可能会有一个或多个自己的子项&#xff0c;依此类推&#xff0c;生成多级数据结构&#xff0c…

标记偏见_分析师的偏见

标记偏见“Beware of the HiPPO in the room” — The risks and dangers of top-down, intuition-based decision making are well known in the business world. Experimentation and data-based decision making become widely acknowledged as the right way to steer a bu…

用户体验数据分析 书单_如何使用数据改善用户体验设计

用户体验数据分析 书单In the current age of technology, if an entrepreneur comes up with a grand idea, chances are they’ll need a pretty sweet website to go along with it. And if they want their idea to really sell, they will also need a website that reall…

推荐11个实用的JavaScript库

2019独角兽企业重金招聘Python工程师标准>>> JavaScript 仍然是 2018 年最受欢迎和使用最为广泛的编程语言&#xff0c;因此 JavaScript 生态系统也会继续发展壮大。 然而&#xff0c;JavaScript 的标准库仍然继续保持“短小精悍”的身材。为了填补标准库功能方面的…

371. 两整数之和

371. 两整数之和 给你两个整数 a 和 b &#xff0c;不使用 运算符 和 - ​​​​​​​&#xff0c;计算并返回两整数之和。 示例 1&#xff1a; 输入&#xff1a;a 1, b 2 输出&#xff1a;3 示例 2&#xff1a; 输入&#xff1a;a 2, b 3 输出&#xff1a;5 提示&a…

【福利】微信小程序精选Demo合集

小编最近在开发小程序&#xff0c;也读到了不少优秀的小程序源码&#xff0c;项目中有些需求可以直接从源码里粘贴复制过来&#xff0c;虽然这样做不利于自己独立编写代码&#xff0c;但比较是给公司做项目啊&#xff0c;秉着效率第一的原则&#xff0c;简直没有什么比ctrlc,ct…

为什么选择做班级管理系统_为什么即使在平衡的班级下准确性也很麻烦

为什么选择做班级管理系统Accuracy is a go-to metric because it’s highly interpretable and low-cost to evaluate. For this reason, accuracy — perhaps the most simple of machine learning metrics — is (rightfully) commonplace. However, it’s also true that m…