使用Golang、WASM和React实现REST API调用的示例

使用Golang、WASM和React实现REST API调用的示例 我对Golang非常陌生,正在尝试创建一个可以通过WASM共享并在React应用中使用的Golang REST API。我正在寻找能够提供一个示例来帮助我的人。

3 回复

amitsingh0542:

我是 Go 语言的新手,正在尝试创建一个可以通过 WASM 共享并在 React 应用中使用的 Go REST API。我正在寻找能提供一个示例来帮助我的人。

要创建一个用于 React 应用(通过 WASM)的 Go REST API,你需要构建一个简单的 Go HTTP 服务器,使用 GOOS=js GOARCH=wasm go build 将其编译为 main.wasm,并通过一个基本的 HTML 文件配合 wasm_exec.js 来提供它。在 React 中,使用 axios 从 WASM API 的端点(示例中是 /message)获取数据。请记住处理好 CORS 和错误处理,并为生产环境正确提供 WASM 文件。

更多关于使用Golang、WASM和React实现REST API调用的示例的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


amitsingh0542: 创建一个可以通过WASM共享并在React应用中使用的Golang REST API。

据我所知,WASM减少了对与公共Web服务器交互的需求——仅仅是加载?这更像是一种旧的客户端-服务器解决方案。这意味着所有通信都直接从WASM客户端到一个完全开放的API?我认为,你可以通过某种令牌来限制从WASM(客户端)对API的访问。

我也对WASM感兴趣,但我的设想是API应该完全锁定,禁止从互联网访问。只能通过本地主机或内部IP访问。

所以我的理解是,WASM既是现代的Web应用,同时又是老式的桌面应用。为了使API尽可能“安全隔离”,我选择了一个传统的SPA/PWA Web应用,通过Web服务器从内部IP访问。

这不是对你问题的直接回答,而是为你构建API时如何思考提供一个基础?

以下是一个完整的示例,展示如何使用Go编译为WebAssembly,并在React应用中调用REST API:

1. Go WebAssembly 代码 (main.go)

package main

import (
    "encoding/json"
    "fmt"
    "syscall/js"
    "net/http"
    "io/ioutil"
    "bytes"
)

// APIResponse 定义API响应结构
type APIResponse struct {
    Data    interface{} `json:"data"`
    Message string      `json:"message"`
    Success bool        `json:"success"`
}

// User 示例数据结构
type User struct {
    ID       int    `json:"id"`
    Name     string `json:"name"`
    Email    string `json:"email"`
}

// 全局HTTP客户端
var client = &http.Client{}

func makeAPICall(this js.Value, args []js.Value) interface{} {
    url := args[0].String()
    method := args[1].String()
    bodyData := args[2].String()
    
    promise := js.Global().Get("Promise").New(js.FuncOf(func(this js.Value, promiseArgs []js.Value) interface{} {
        resolve := promiseArgs[0]
        reject := promiseArgs[1]
        
        go func() {
            var req *http.Request
            var err error
            
            if bodyData != "" {
                req, err = http.NewRequest(method, url, bytes.NewBuffer([]byte(bodyData)))
                if err != nil {
                    reject.Invoke(err.Error())
                    return
                }
                req.Header.Set("Content-Type", "application/json")
            } else {
                req, err = http.NewRequest(method, url, nil)
                if err != nil {
                    reject.Invoke(err.Error())
                    return
                }
            }
            
            resp, err := client.Do(req)
            if err != nil {
                reject.Invoke(err.Error())
                return
            }
            defer resp.Body.Close()
            
            body, err := ioutil.ReadAll(resp.Body)
            if err != nil {
                reject.Invoke(err.Error())
                return
            }
            
            // 创建JavaScript对象返回
            result := js.Global().Get("Object").New()
            result.Set("status", resp.StatusCode)
            result.Set("body", string(body))
            result.Set("headers", js.ValueOf(resp.Header))
            
            resolve.Invoke(result)
        }()
        
        return nil
    }))
    
    return promise
}

func getUsers(this js.Value, args []js.Value) interface{} {
    url := "https://jsonplaceholder.typicode.com/users"
    
    promise := js.Global().Get("Promise").New(js.FuncOf(func(this js.Value, promiseArgs []js.Value) interface{} {
        resolve := promiseArgs[0]
        reject := promiseArgs[1]
        
        go func() {
            resp, err := http.Get(url)
            if err != nil {
                reject.Invoke(err.Error())
                return
            }
            defer resp.Body.Close()
            
            body, err := ioutil.ReadAll(resp.Body)
            if err != nil {
                reject.Invoke(err.Error())
                return
            }
            
            var users []User
            err = json.Unmarshal(body, &users)
            if err != nil {
                reject.Invoke(err.Error())
                return
            }
            
            // 转换为JavaScript数组
            jsArray := js.Global().Get("Array").New(len(users))
            for i, user := range users {
                obj := js.Global().Get("Object").New()
                obj.Set("id", user.ID)
                obj.Set("name", user.Name)
                obj.Set("email", user.Email)
                jsArray.SetIndex(i, obj)
            }
            
            resolve.Invoke(jsArray)
        }()
        
        return nil
    }))
    
    return promise
}

func registerFunctions() {
    js.Global().Set("goMakeAPICall", js.FuncOf(makeAPICall))
    js.Global().Set("goGetUsers", js.FuncOf(getUsers))
}

func main() {
    c := make(chan struct{}, 0)
    registerFunctions()
    <-c
}

2. 编译为WebAssembly

创建编译脚本 build.sh

#!/bin/bash
GOOS=js GOARCH=wasm go build -o main.wasm main.go
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .

3. React组件 (GoWasmComponent.jsx)

import React, { useEffect, useRef, useState } from 'react';

const GoWasmComponent = () => {
    const [users, setUsers] = useState([]);
    const [loading, setLoading] = useState(false);
    const [response, setResponse] = useState('');
    const wasmLoaded = useRef(false);

    useEffect(() => {
        if (!wasmLoaded.current) {
            loadWasm();
            wasmLoaded.current = true;
        }
    }, []);

    const loadWasm = async () => {
        try {
            const go = new window.Go();
            const result = await WebAssembly.instantiateStreaming(
                fetch('/main.wasm'),
                go.importObject
            );
            go.run(result.instance);
            console.log('Go WASM loaded successfully');
        } catch (error) {
            console.error('Failed to load WASM:', error);
        }
    };

    const fetchUsers = async () => {
        setLoading(true);
        try {
            if (window.goGetUsers) {
                const users = await window.goGetUsers();
                setUsers(users);
            } else {
                console.error('WASM function not available');
            }
        } catch (error) {
            console.error('Error fetching users:', error);
        } finally {
            setLoading(false);
        }
    };

    const callCustomAPI = async () => {
        try {
            if (window.goMakeAPICall) {
                const apiUrl = 'https://jsonplaceholder.typicode.com/posts';
                const method = 'POST';
                const postData = JSON.stringify({
                    title: 'foo',
                    body: 'bar',
                    userId: 1
                });

                const result = await window.goMakeAPICall(apiUrl, method, postData);
                setResponse(JSON.stringify({
                    status: result.status,
                    body: JSON.parse(result.body),
                    headers: result.headers
                }, null, 2));
            }
        } catch (error) {
            console.error('API call failed:', error);
            setResponse(`Error: ${error.message}`);
        }
    };

    return (
        <div style={{ padding: '20px' }}>
            <h2>Go WASM REST API Demo</h2>
            
            <div style={{ marginBottom: '20px' }}>
                <button 
                    onClick={fetchUsers}
                    disabled={loading}
                    style={{
                        padding: '10px 20px',
                        marginRight: '10px',
                        backgroundColor: '#007bff',
                        color: 'white',
                        border: 'none',
                        borderRadius: '4px',
                        cursor: 'pointer'
                    }}
                >
                    {loading ? 'Loading...' : 'Fetch Users via WASM'}
                </button>
                
                <button 
                    onClick={callCustomAPI}
                    style={{
                        padding: '10px 20px',
                        backgroundColor: '#28a745',
                        color: 'white',
                        border: 'none',
                        borderRadius: '4px',
                        cursor: 'pointer'
                    }}
                >
                    Test POST Request
                </button>
            </div>

            {users.length > 0 && (
                <div style={{ marginBottom: '20px' }}>
                    <h3>Users fetched via Go WASM:</h3>
                    <ul style={{ listStyle: 'none', padding: 0 }}>
                        {users.map(user => (
                            <li key={user.id} style={{
                                padding: '10px',
                                marginBottom: '5px',
                                backgroundColor: '#f8f9fa',
                                border: '1px solid #dee2e6',
                                borderRadius: '4px'
                            }}>
                                <strong>{user.name}</strong> - {user.email}
                            </li>
                        ))}
                    </ul>
                </div>
            )}

            {response && (
                <div>
                    <h3>API Response:</h3>
                    <pre style={{
                        backgroundColor: '#f8f9fa',
                        padding: '15px',
                        border: '1px solid #dee2e6',
                        borderRadius: '4px',
                        overflow: 'auto'
                    }}>
                        {response}
                    </pre>
                </div>
            )}
        </div>
    );
};

export default GoWasmComponent;

4. 主应用入口 (App.js)

import React from 'react';
import GoWasmComponent from './GoWasmComponent';

function App() {
    return (
        <div className="App">
            <GoWasmComponent />
        </div>
    );
}

export default App;

5. 项目结构

project/
├── go/
│   ├── main.go
│   └── build.sh
├── public/
│   ├── index.html
│   └── main.wasm
├── src/
│   ├── App.js
│   └── GoWasmComponent.jsx
├── package.json
└── wasm_exec.js

6. 运行步骤

  1. 编译Go为WASM:
cd go
chmod +x build.sh
./build.sh
  1. 将生成的 main.wasmwasm_exec.js 复制到React项目的public目录

  2. 在React应用中导入wasm_exec.js,在public/index.html中添加:

<script src="%PUBLIC_URL%/wasm_exec.js"></script>
  1. 启动React应用:
npm start

这个示例展示了如何通过Go WebAssembly在React应用中执行REST API调用,包括GET和POST请求。Go代码编译为WASM后,通过JavaScript Promise与React组件进行异步交互。

回到顶部