基于Hyperledger Fabric、Golang、React的银行应用链码UI调用及示例请求
Hyperledger Fabric + React 前端调用链码实操指南
核心流程说明
前端无法直接和Fabric链码交互,必须通过中间层代理实现:React发送HTTP请求到你用Golang写的后端服务,再由后端通过Fabric SDK(推荐fabric-gateway)和区块链网络通信,调用链码方法并返回结果。
步骤1:搭建Golang后端代理
1.1 引入依赖
在Golang项目中导入Fabric Gateway相关包:
import ( "encoding/json" "fmt" "log" "net/http" "time" "github.com/hyperledger/fabric-gateway/pkg/client" "github.com/hyperledger/fabric-gateway/pkg/identity" "google.golang.org/grpc" "google.golang.org/grpc/credentials" )
1.2 初始化Fabric Gateway连接
编写初始化函数,加载用户证书、私钥,建立到Peer节点的gRPC连接:
func NewGateway() (*client.Gateway, error) { // 加载用户签名证书 cert, err := identity.NewX509CertificateFromPEMFile("./crypto/org1/user1/signcerts/cert.pem") if err != nil { return nil, err } // 加载用户私钥 privateKey, err := identity.NewPrivateKeyFromPEMFile("./crypto/org1/user1/keystore/priv_sk") if err != nil { return nil, err } // 创建身份对象 id, err := identity.NewX509Identity("Org1MSP", cert, privateKey) if err != nil { return nil, err } // 配置Peer节点的TLS证书 tlsCreds, err := credentials.NewClientTLSFromFile("./crypto/org1/peer0/tls/ca.crt", "peer0.org1.example.com") if err != nil { return nil, err } // 建立gRPC连接 grpcConn, err := grpc.NewClient("localhost:7051", grpc.WithTransportCredentials(tlsCreds)) if err != nil { return nil, err } // 创建Gateway实例 gateway, err := client.Connect( id, client.WithGRPCConnection(grpcConn), client.WithDefaultTimeout(5*time.Second), ) return gateway, nil }
1.3 编写链码调用接口
以银行应用的转账和余额查询为例,实现HTTP接口:
func main() { // 初始化Gateway连接 gateway, err := NewGateway() if err != nil { log.Fatalf("Failed to create gateway: %v", err) } defer gateway.Close() // 获取通道和链码实例 network := gateway.GetNetwork("mychannel") contract := network.GetContract("bankcc") // 转账接口 http.HandleFunc("/api/transfer", func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { w.WriteHeader(http.StatusMethodNotAllowed) return } var req struct { From string `json:"from"` To string `json:"to"` Amount float64 `json:"amount"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(map[string]string{"error": "参数格式错误"}) return } // 调用链码的Transfer方法(提交交易) _, err := contract.SubmitTransaction("Transfer", req.From, req.To, fmt.Sprintf("%f", req.Amount)) if err != nil { w.WriteHeader(http.StatusInternalServerError) json.NewEncoder(w).Encode(map[string]string{"error": err.Error()}) return } w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(map[string]string{"message": "转账成功"}) }) // 余额查询接口 http.HandleFunc("/api/balance", func(w http.ResponseWriter, r *http.Request) { account := r.URL.Query().Get("account") if account == "" { w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(map[string]string{"error": "缺少账户参数"}) return } // 调用链码的QueryBalance方法(查询交易,无需共识) result, err := contract.EvaluateTransaction("QueryBalance", account) if err != nil { w.WriteHeader(http.StatusInternalServerError) json.NewEncoder(w).Encode(map[string]string{"error": err.Error()}) return } w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(map[string]string{"account": account, "balance": string(result)}) }) // 启动HTTP服务 log.Println("Server starting on :8080...") log.Fatal(http.ListenAndServe(":8080", nil)) }
步骤2:React前端调用接口
2.1 转账组件
import { useState } from 'react'; const TransferForm = () => { const [from, setFrom] = useState(''); const [to, setTo] = useState(''); const [amount, setAmount] = useState(''); const [status, setStatus] = useState(''); const handleSubmit = async (e) => { e.preventDefault(); setStatus('处理中...'); try { const res = await fetch('http://localhost:8080/api/transfer', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ from, to, amount: parseFloat(amount) }) }); const data = await res.json(); setStatus(data.message || data.error); } catch (err) { setStatus(`请求失败:${err.message}`); } }; return ( <div style={{ padding: '20px', maxWidth: '400px' }}> <h3>银行转账</h3> <form onSubmit={handleSubmit}> <div style={{ margin: '10px 0' }}> <label>转出账户:</label> <input type="text" value={from} onChange={(e) => setFrom(e.target.value)} required style={{ marginLeft: '10px' }} /> </div> <div style={{ margin: '10px 0' }}> <label>转入账户:</label> <input type="text" value={to} onChange={(e) => setTo(e.target.value)} required style={{ marginLeft: '10px' }} /> </div> <div style={{ margin: '10px 0' }}> <label>金额:</label> <input type="number" step="0.01" value={amount} onChange={(e) => setAmount(e.target.value)} required style={{ marginLeft: '10px' }} /> </div> <button type="submit">确认转账</button> </form> {status && <p style={{ marginTop: '15px' }}>{status}</p>} </div> ); }; export default TransferForm;
2.2 余额查询组件
import { useState } from 'react'; const BalanceQuery = () => { const [account, setAccount] = useState(''); const [balance, setBalance] = useState(''); const [error, setError] = useState(''); const handleQuery = async (e) => { e.preventDefault(); setBalance(''); setError(''); try { const res = await fetch(`http://localhost:8080/api/balance?account=${encodeURIComponent(account)}`); const data = await res.json(); if (data.error) { setError(data.error); } else { setBalance(data.balance); } } catch (err) { setError(`查询失败:${err.message}`); } }; return ( <div style={{ padding: '20px', maxWidth: '400px' }}> <h3>余额查询</h3> <form onSubmit={handleQuery}> <div style={{ margin: '10px 0' }}> <label>账户名:</label> <input type="text" value={account} onChange={(e) => setAccount(e.target.value)} required style={{ marginLeft: '10px' }} /> </div> <button type="submit">查询余额</button> </form> {balance && <p style={{ marginTop: '15px' }}>账户 {account} 的余额:{balance}</p>} {error && <p style={{ marginTop: '15px', color: 'red' }}>{error}</p>} </div> ); }; export default BalanceQuery;
关键注意事项
- 跨域处理:React开发环境默认有跨域限制,可在
package.json中添加"proxy": "http://localhost:8080",或在Golang后端添加CORS中间件。 - 证书路径:确保Golang代码中的证书、私钥路径与你的Fabric网络实际路径一致(比如使用
fabric-samples/test-network时,路径需对应生成的crypto文件)。 - 链码方法匹配:后端调用的链码方法名(如
Transfer、QueryBalance)必须与你部署的链码中的方法名完全一致。
内容的提问来源于stack exchange,提问作者Rahul Thongam
相关产品推荐
相关产品推荐

