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

Go后端与React-admin集成时Content-Range Header缺失问题求助

Go+React-admin应用中Content-Range Header缺失导致分页失效问题

我基于Go后端(对接PostgreSQL)和React-admin前端开发应用时遇到问题:React-admin要求的Content-Range Header在API响应中缺失,导致管理面板无法正确展示分页数据。我已在Go接口中尝试设置该Header,但获取用户数据的响应仍未包含它。

Go后端测试API代码

package main

import (
    "database/sql"
    "encoding/json"
    "fmt"
    "log"
    "net/http"

    "github.com/gorilla/mux"
    _ "github.com/lib/pq"
    "github.com/rs/cors"
)

const (
    host     = "localhost"
    port     = 5432
    user     = "postgres"
    password = "123"
    dbname   = "orbita"
)

var db *sql.DB

func main() {
    // Create a connection to the PostgreSQL database
    connStr := "user=" + user + " password=" + password + " dbname=" + dbname + " sslmode=disable"
    var err error
    db, err = sql.Open("postgres", connStr)
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    r := mux.NewRouter()

    // Configure routes for your REST API
    r.HandleFunc("/users", GetUsers).Methods("GET")
    // Create a CORS instance
    c := cors.New(cors.Options{
        AllowedOrigins: []string{"*"}, // Allow access from all domains
        AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
        AllowedHeaders: []string{"*"}, // Allow all headers
    })

    // Wrap the routes with CORS middleware
    handler := c.Handler(r)

    http.Handle("/", handler)
    http.ListenAndServe(":8080", nil)
}

// User structure
type User struct {
    Username     string `json:"username"`
    PasswordHash string `json:"password_hash"`
    FirstName    string `json:"first_name"`
    LastName     string `json:"last_name"`
    Position     string `json:"position"`
    AccessLevel  string `json:"access_level"`
}

// GetUsers returns a list of users from the database
func GetUsers(w http.ResponseWriter, r *http.Request) {
    rows, err := db.Query("SELECT username, password_hash, first_name, last_name, position, access_level FROM users")
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    defer rows.Close()

    var users []User
    for rows.Next() {
        var user User
        if err := rows.Scan(&user.Username, &user.PasswordHash, &user.FirstName, &user.LastName, &user.Position, &user.AccessLevel); err != nil {
            log.Println(err)
            continue
        }
        users = append(users, user)
    }

    w.Header().Set("Content-Type", "application/json")

    // Add the Content-Range header in the format 'users 0-24/324'
    totalUsers := len(users)
    contentRange := fmt.Sprintf("users 0-%d/%d", totalUsers, totalUsers)
    w.Header().Set("Content-Range", contentRange)

    w.WriteHeader(http.StatusOK)

    responseData := map[string]interface{}{"users": users}
    json.NewEncoder(w).Encode(responseData)
}

React-admin前端代码

App.tsx

import {
  Admin,
  Resource
} from "react-admin";
import { UserList } from "./users";
import { dataProvider } from "./dataProvider";

export const App = () => <Admin dataProvider={dataProvider}>
  <Resource name="users" list={UserList} />
</Admin>;

users.tsx

import * as React from 'react';
import { List, Datagrid, TextField, EmailField } from 'react-admin';

export const UserList: React.FC = (props) => (
  <List {...props}>
    <Datagrid rowClick="edit">
      <TextField source="username" />
      <EmailField source="email" />
      <TextField source="first_name" label="First Name" />
      <TextField source="last_name" label="Last Name" />
      <TextField source="position" />
      <TextField source="access_level" label="Access Level" />
    </Datagrid>
  </List>
);

问题截图

响应头中无Content-Range
React-admin报错提示
请求详情


问题原因与解决方法

  1. CORS配置未暴露自定义Header
    rs/cors中间件默认不会将自定义的Content-Range Header暴露给前端,需在配置中添加ExposedHeaders字段指定要暴露的Header:

    c := cors.New(cors.Options{
        AllowedOrigins: []string{"*"},
        AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
        AllowedHeaders: []string{"*"},
        ExposedHeaders: []string{"Content-Range"}, // 新增此行,暴露Content-Range
    })
    
  2. 适配React-admin的分页规则与响应格式
    React-admin会传递_start和_end分页参数,且要求返回206状态码、顶层数组格式数据,同时Content-Range需符合resource start-end/total规范。修改GetUsers函数:

    import "strconv" // 需引入strconv包
    
    func GetUsers(w http.ResponseWriter, r *http.Request) {
        // 解析分页参数,设置默认值
        startStr := r.URL.Query().Get("_start")
        endStr := r.URL.Query().Get("_end")
        start, err := strconv.Atoi(startStr)
        if err != nil {
            start = 0
        }
        end, err := strconv.Atoi(endStr)
        if err != nil {
            end = 25 // 默认每页25条
        }
    
        // 查询总用户数
        var totalUsers int
        err = db.QueryRow("SELECT COUNT(*) FROM users").Scan(&totalUsers)
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
    
        // 查询指定范围的用户
        rows, err := db.Query(`
            SELECT username, password_hash, first_name, last_name, position, access_level 
            FROM users 
            LIMIT $1 OFFSET $2
        `, end-start, start)
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        defer rows.Close()
    
        var users []User
        for rows.Next() {
            var user User
            if err := rows.Scan(&user.Username, &user.PasswordHash, &user.FirstName, &user.LastName, &user.Position, &user.AccessLevel); err != nil {
                log.Println(err)
                continue
            }
            users = append(users, user)
        }
    
        w.Header().Set("Content-Type", "application/json")
        // 正确设置Content-Range格式
        contentRange := fmt.Sprintf("users %d-%d/%d", start, start+len(users)-1, totalUsers)
        w.Header().Set("Content-Range", contentRange)
        // 返回React-admin要求的206状态码
        w.WriteHeader(http.StatusPartialContent)
    
        // 直接返回用户数组,而非嵌套结构
        json.NewEncoder(w).Encode(users)
    }
    
  3. 前端字段适配
    注意到users.tsx中使用了EmailField绑定email字段,但Go后端的User结构体中无该字段,需确保后端返回数据包含email,或修改前端为TextField避免报错。

内容的提问来源于stack exchange,提问作者Kirill Kolesnikov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 18:14:52