You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 01:04:59