音响风尚圈

音响风尚圈

Hyperledger Fabric Rest API设计与实现

admin 38 254

HyperledgerFabric提供了软件开发包/SDK以帮助开发者访问fabric网络和部署在网络上的链码,但是HyperledgerFabric官方没有提供简单易用的RESTAPI访问接口,在这个教程里我们将学习如何利用HyperledgerFabric的SDK来开发RESTAPI服务器。

1、系统结构概述


整个系统包含两个物理节点:

Fabric节点:运行Fabric示例中的FirstNetwork,并且实例化了Fabcar链码

API服务器节点:运行RESTAPIServer代码供外部访问

下面是部署在AWS上的两个节点实例的情况:


首先参考官方文档安装hyperledgerfabric。

然后运行脚本fabcar/:

cdfabric-samples/fabcar./

上述脚本运行之后,我们就得到一个正常运转的HyperledgerFabric网络(著名的演示网络FirstNetwork),包含2个机构/4个对等节点,通道为mychannel,链码Fabcar安装在全部4个对等节点上并且在mychannel上激活。账本中有10条车辆记录,这是调用合约的initLedger方法的结果。

现在我们为RESTAPIServer准备身份标识数据。使用fabcar/javascript创建一个用户标识user1,我们将在RESTAPIServer中使用这个身份标识:

/user1

运行结果如下:


现在RestAPIServer需要的东西都备齐了:

org1的连接配置文件:first-network/

包文件:fabcar/

User1身份钱包:fabcar/javascript/wallet/user1/

后面我们会把这些数据文件拷贝到RestAPIServer。

2、RestAPIServer设计

我们使用ExressJS来开发API服务,利用和中的代码实现与fabric交互的逻辑。API设计如下:

GET/api/queryallcars:返回全部车辆记录

GET/api/query/CarID:返回指定ID的车辆记录

POST/api/addcar/:添加一条新的车辆记录

PUT/api/changeowner/CarID:修改指定ID的车辆记录

3、RestAPIServer代码实现

代码如下:

varbodyParser=require('body-parser');varapp=express();(());//SettingforHyperledgerFabricconst{FileSystemWallet,Gateway}=require('fabric-network');constpath=require('path');constccpPath=(__dirname,'.','');('/api/queryallcars',asyncfunction(req,res){try{//=((),'wallet');constwallet=newFileSystemWallet(walletPath);(`Walletpath:${walletPath}`);//Checktoseeifwe'=('user1');if(!userExists){('Anidentityfortheuser"user1"doesnotexistinthewallet');('');return;}//=newGateway();(ccpPath,{wallet,identity:'user1',discovery:{enabled:true,asLocalhost:false}});//Getthenetwork(channel)=('mychannel');//=('fabcar');//Evaluatethespecifiedtransaction.//queryCartransaction-requires1argument,ex:('queryCar','CAR4')//queryAllCarstransaction-requiresnoarguments,ex:('queryAllCars')constresult=('queryAllCars');(`Transactionhasbeenevaluated,resultis:${()}`);(200).json({response:()});}catch(error){(`Failedtoevaluatetransaction:${error}`);(500).json({error:error});(1);}});('/api/query/:car_index',asyncfunction(req,res){try{//=((),'wallet');constwallet=newFileSystemWallet(walletPath);(`Walletpath:${walletPath}`);//Checktoseeifwe'=('user1');if(!userExists){('Anidentityfortheuser"user1"doesnotexistinthewallet');('');return;}//=newGateway();(ccpPath,{wallet,identity:'user1',discovery:{enabled:true,asLocalhost:false}});//Getthenetwork(channel)=('mychannel');//=('fabcar');//Evaluatethespecifiedtransaction.//queryCartransaction-requires1argument,ex:('queryCar','CAR4')//queryAllCarstransaction-requiresnoarguments,ex:('queryAllCars')constresult=('queryCar',_index);(`Transactionhasbeenevaluated,resultis:${()}`);(200).json({response:()});}catch(error){(`Failedtoevaluatetransaction:${error}`);(500).json({error:error});(1);}});('/api/addcar/',asyncfunction(req,res){try{//=((),'wallet');constwallet=newFileSystemWallet(walletPath);(`Walletpath:${walletPath}`);//Checktoseeifwe'=('user1');if(!userExists){('Anidentityfortheuser"user1"doesnotexistinthewallet');('');return;}//=newGateway();(ccpPath,{wallet,identity:'user1',discovery:{enabled:true,asLocalhost:false}});//Getthenetwork(channel)=('mychannel');//=('fabcar');//Submitthespecifiedtransaction.//createCartransaction-requires5argument,ex:('createCar','CAR12','Honda','Accord','Black','Tom')//changeCarOwnertransaction-requires2args,ex:('changeCarOwner','CAR10','Dave')('createCar',,,,,);('Transactionhasbeensubmitted');('Transactionhasbeensubmitted');//();}catch(error){(`Failedtosubmittransaction:${error}`);(1);}})('/api/changeowner/:car_index',asyncfunction(req,res){try{//=((),'wallet');constwallet=newFileSystemWallet(walletPath);(`Walletpath:${walletPath}`);//Checktoseeifwe'=('user1');if(!userExists){('Anidentityfortheuser"user1"doesnotexistinthewallet');('');return;}//=newGateway();(ccpPath,{wallet,identity:'user1',discovery:{enabled:true,asLocalhost:false}});//Getthenetwork(channel)=('mychannel');//=('fabcar');//Submitthespecifiedtransaction.//createCartransaction-requires5argument,ex:('createCar','CAR12','Honda','Accord','Black','Tom')//changeCarOwnertransaction-requires2args,ex:('changeCarOwner','CAR10','Dave')('changeCarOwner',_index,);('Transactionhasbeensubmitted');('Transactionhasbeensubmitted');//();}catch(error){(`Failedtosubmittransaction:${error}`);(1);}})(8080);

代码中对原来fabcar的和修改如下:

ccpPath修改为当前目录,因为我们要使用同一路径下的连接配置文件

在调用中,修改选项为false

4、RestAPIServer的连接配置文件

API服务依赖于连接配置文件来正确连接fabric网络。文件可以直接从fabric网络中获取:

{"name":"first-network-org1","version":"1.0.0","client":{"organization":"Org1","connection":{"timeout":{"peer":{"orser":"300"}}}},"organizations":{"Org1":{"mspid":"Org1MSP","peers":["",""],"certificateAuthorities":[""]}},"peers":{"":{"url":"grpcs://localhost:7051","tlsCACerts":{"pem":"-----BEGINCERTIFICATE-----\nMIICVjCCAf2gAwIBAgIQEB1sDT11gzTv0/N4cIGoEjAKBggqhkjOPQQDAjB2MQsw\nCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy\nYW5jaXNjbzEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEfMB0GA1UEAxMWdGxz\nY2Eub3JnMS5leGFtcGxlLmNvbTAeFw0xOTA5MDQwMjQzMDBaFw0yOTA5MDEwMjQz\nMDBaMHYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH\nEw1TYW4gRnJhbmNpc2NvMRkwFwYDVQQKExBvcmcxLmV4YW1wbGUuY29tMR8wHQYD\nVQQDExZ0bHNjYS5vcmcxLmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0D\nAQcDQgAEoN0qd5hM2SDfvGzNjTCXuQqyk+XK4VISa16/y9iXBPpa0onyAXJuv7T0\noPf+mh3T7/g8uYtV2bwTpT2XFO3Q6KNtMGswDgYDVR0PAQH/BAQDAgGmMB0GA1Ud\nJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1Ud\nDgQiBCCalpyChmrLtpgOll6TVmlMOO/2iiyI2PadNPsIYx51mTAKBggqhkjOPQQD\nAgNHADBEAiBLNoAYWe9LvoxxBxl3sUM64kl7rx6dI3JU+dJG6FRxWgIgCu1ONEyp\nfux9lZWr6gcrIdsn/8fQuWiOIbAgq0HSr60=\n-----ENDCERTIFICATE-----\n"},"grpcOptions":{"ssl-target-name-override":"","hostnameOverride":""}},"":{"url":"grpcs://localhost:8051","tlsCACerts":{"pem":"-----BEGINCERTIFICATE-----\nMIICVjCCAf2gAwIBAgIQEB1sDT11gzTv0/N4cIGoEjAKBggqhkjOPQQDAjB2MQsw\nCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy\nYW5jaXNjbzEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEfMB0GA1UEAxMWdGxz\nY2Eub3JnMS5leGFtcGxlLmNvbTAeFw0xOTA5MDQwMjQzMDBaFw0yOTA5MDEwMjQz\nMDBaMHYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH\nEw1TYW4gRnJhbmNpc2NvMRkwFwYDVQQKExBvcmcxLmV4YW1wbGUuY29tMR8wHQYD\nVQQDExZ0bHNjYS5vcmcxLmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0D\nAQcDQgAEoN0qd5hM2SDfvGzNjTCXuQqyk+XK4VISa16/y9iXBPpa0onyAXJuv7T0\noPf+mh3T7/g8uYtV2bwTpT2XFO3Q6KNtMGswDgYDVR0PAQH/BAQDAgGmMB0GA1Ud\nJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1Ud\nDgQiBCCalpyChmrLtpgOll6TVmlMOO/2iiyI2PadNPsIYx51mTAKBggqhkjOPQQD\nAgNHADBEAiBLNoAYWe9LvoxxBxl3sUM64kl7rx6dI3JU+dJG6FRxWgIgCu1ONEyp\nfux9lZWr6gcrIdsn/8fQuWiOIbAgq0HSr60=\n-----ENDCERTIFICATE-----\n"},"grpcOptions":{"ssl-target-name-override":"","hostnameOverride":""}}},"certificateAuthorities":{"":{"url":"https://localhost:7054","caName":"ca-org1","tlsCACerts":{"pem":"-----BEGINCERTIFICATE-----\nMIICUTCCAfegAwIBAgIQSiMHm4n9QvhD6wltAHkZPTAKBggqhkjOPQQDAjBzMQsw\nCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy\nYW5jaXNjbzEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEcMBoGA1UEAxMTY2Eu\nb3JnMS5leGFtcGxlLmNvbTAeFw0xOTA5MDQwMjQzMDBaFw0yOTA5MDEwMjQzMDBa\nMHMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1T\nYW4gRnJhbmNpc2NvMRkwFwYDVQQKExBvcmcxLmV4YW1wbGUuY29tMRwwGgYDVQQD\nExNjYS5vcmcxLmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE\nz93lOhLJG93uJQgnh93QcPPal5NQXQnAutFKYkun/eMHMe23wNPd0aJhnXdCjWF8\nMRHVAjtPn4NVCJYiTzSAnaNtMGswDgYDVR0PAQH/BAQDAgGmMB0GA1UdJQQWMBQG\nCCsGAQUFBwMCBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1UdDgQiBCDK\naDhLwl3RBO6eKgHh4lHJovIyDJO3jTNb1ix1W86bFjAKBggqhkjOPQQDAgNIADBF\nAiEA8KTKkjQwb1TduTWWkmsLmKdxrlE6/H7CfsdeGE+onewCIHJ1S0nLhbWYv+G9\nTbAFlNCpqr0AQefaRT3ghdURrlbo\n-----ENDCERTIFICATE-----\n"},"httpOptions":{"verify":false}}}}

当fabcar/执行时,我们可以交叉检查证书的传播是否正确。和的证书是org1的TLSrootCA证书签名的。

注意所有的节点都以localhost引用,我们稍后会将其修改为FabricNode的公开IP地址。

5、用户身份标识

我们已经在Fabric节点上生成了一个用户标识user1并保存在wallet目录中,我们可以看到有三个对应的文件:私钥、公钥和证书对象:


稍后我们会把这些文件拷贝到RestAPIServer上。

6、安装RestAPIServer节点

1、首先在RestAPIServer节点上安装npm、node:

验证结果如下:


2、然后在RestAPIServer上创建一个目录:

mkdirapiservercdapiserver

3、接下来将下面的文件从Fabric节点拷贝到RestAPIServer节点。我们利用loccalhost在两个EC2实例间拷贝:

tempisanemptydirectorycdtempscp-i~/Downloads/@[Fabric-Node-IP]:/home/ubuntu/fabric-samples/first-network/~/Downloads/@[Fabric-Node-IP]:/home/ubuntu/fabric-samples/fabcar/javascript/~/Downloads/@[Fabric-Node-IP]:/home/ubuntu/fabric-samples/fabcar/javascript/wallet/user1/.scp-r-i~/Downloads/*ubuntu@[API-Server-Node-IP]:/home/ubuntu/apiserver/

运行结果如下:


4、可以看到现在所有的文件都拷贝到RestAPIServer了,为了保持一致,我们将user1/改名为wallet/user1/:

cdapiservermkdirwalletmvuser1wallet/user1

运行结果如下:


5、现在在RestAPIServer上创建上面的文件。

6、修改连接配置文件中的fabric节点的ip地址:

sed-i's/localhost/[Fabric-Node-IP]/g'

运行结果如下:


7、在/etc/hosts中增加条目以便可以正确解析fabric节点的IP:

127.0.0.1localhost[Fabric-Node-IP][Fabric-Node-IP][Fabric-Node-IP][Fabric-Node-IP][Fabric-Node-IP]

运行结果如下:


8、安装必要的依赖包:

npminstallnpminstallexpressbody-parser--save

9、万事俱备,启动RestAPIServer:

7、访问API

我们的API服务在8080端口监听,在下面的示例中,我们使用curl来演示如何访问。

1、查询所有车辆记录

curlhttp://[API-Server-Node-IP]:8080/api/queryallcars

运行结果如下:


2、添加新的车辆记录并查询

curl-d'{"carid":"CAR12","make":"Honda","model":"Accord","colour":"black","owner":"Tom"}'-H"Content-Type:application/json"-XPOSThttp://[API-Server-Node-IP]:8080/api/addcarcurlhttp://[API-Server-Node-IP]:8080/api/query/CAR12

运行结果如下:


3、修改车辆所有者并再次查询

curlhttp://[API-Server-Node-IP]:8080/api/query/CAR4curl-d'{"owner":"KC"}'-H"Content-Type:application/json"-XPUThttp://[API-Server-Node-IP]:8080/api/changeowner/CAR4curlhttp://[API-Server-Node-IP]:8080/api/query/CAR4

运行结果如下:

我们也可以用postman得到同样的结果:


汇智网原文链接: