Golang中如何在请求体中查找特定键值?未找到时如何报错处理

Golang中如何在请求体中查找特定键值?未找到时如何报错处理 这是我的 HandleFunc

type User struct {
	Username string `json:"username"`
	Password string `json:"password"`
}

type ResponseResult struct {
	Error  string `json:"error"`
	Result string `json:"result"`
}

func Registration(w http.ResponseWriter, r *http.Request) {
	var res ResponseResult
	w.Header().Set("Content-Type", "application/json")

	body, err := ioutil.ReadAll(r.Body)
	// 没有 JSON 请求体
	if err != nil {
		res.Error = err.Error()
		// 返回 400 状态码
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(res)
		return
	}
}

如果请求中没有传递任何 JSON 数据体,这段代码工作正常。

但我希望,如果提供了错误的数据,它也应该报错。例如,我不希望以下数据被视为正确:

{
	"usernm": "foo",
	"passwd": "bar"
}

这是我正在使用的代码:

var user model.User
// 检查是否接收到有效数据,即用户名和密码
err = json.Unmarshal(body, &user)
// JSON 请求体中的键不正确
if err != nil {
	res.Error = "发送的数据不正确。请参阅 API 文档。"
	// 返回 400 状态码
	w.WriteHeader(http.StatusBadRequest)
	json.NewEncoder(w).Encode(res)
	return
}

res.Result = "无异常"
json.NewEncoder(w).Encode(res)

即使请求体是错误的,我在 json.Unmarshal 处也没有收到任何错误。


更多关于Golang中如何在请求体中查找特定键值?未找到时如何报错处理的实战教程也可以访问 https://www.itying.com/category-94-b0.html

4 回复

defer r.Body.Close() 应在错误检查块之后调用。

更多关于Golang中如何在请求体中查找特定键值?未找到时如何报错处理的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我也尝试过使用 json.Decode

err := json.NewDecoder(r.Body).Decode(&user)
// 没有 JSON 请求体
if err != nil {
	res.Error = err.Error()
	// 返回 400 状态码
	w.WriteHeader(http.StatusBadRequest)
	json.NewEncoder(w).Encode(res)
	return
}

但由于解码过程没有错误,这段代码块无法被执行到。

使用解码器中的 DisallowUnknownFields 方法:

jsonDecoder := json.NewDecoder(r.Body)
jsonDecoder.DisallowUnknownFields()
defer r.Body.Close()

// 检查是否存在正确的JSON正文或错误
if err := jsonDecoder.Decode(&user); err != nil {
	res.Error = err.Error()
	// 返回400状态码
	w.WriteHeader(http.StatusBadRequest)
	json.NewEncoder(w).Encode(res)
	return
}

在Golang中处理JSON请求体时,json.Unmarshal不会因为JSON中存在额外字段而报错。要确保请求体包含特定键值,需要在反序列化后手动验证。以下是修改后的代码:

func Registration(w http.ResponseWriter, r *http.Request) {
    var res ResponseResult
    w.Header().Set("Content-Type", "application/json")

    // 读取请求体
    body, err := ioutil.ReadAll(r.Body)
    if err != nil {
        res.Error = "无法读取请求体"
        w.WriteHeader(http.StatusBadRequest)
        json.NewEncoder(w).Encode(res)
        return
    }

    // 使用map接收JSON以检查额外字段
    var rawData map[string]interface{}
    if err := json.Unmarshal(body, &rawData); err != nil {
        res.Error = "无效的JSON格式"
        w.WriteHeader(http.StatusBadRequest)
        json.NewEncoder(w).Encode(res)
        return
    }

    // 检查必需字段是否存在
    requiredFields := []string{"username", "password"}
    for _, field := range requiredFields {
        if _, exists := rawData[field]; !exists {
            res.Error = fmt.Sprintf("缺少必需字段: %s", field)
            w.WriteHeader(http.StatusBadRequest)
            json.NewEncoder(w).Encode(res)
            return
        }
    }

    // 检查是否有额外字段
    allowedFields := map[string]bool{
        "username": true,
        "password": true,
    }
    for field := range rawData {
        if !allowedFields[field] {
            res.Error = fmt.Sprintf("不允许的字段: %s", field)
            w.WriteHeader(http.StatusBadRequest)
            json.NewEncoder(w).Encode(res)
            return
        }
    }

    // 反序列化到User结构体
    var user User
    if err := json.Unmarshal(body, &user); err != nil {
        res.Error = "数据格式错误"
        w.WriteHeader(http.StatusBadRequest)
        json.NewEncoder(w).Encode(res)
        return
    }

    // 验证字段值不为空
    if user.Username == "" || user.Password == "" {
        res.Error = "用户名和密码不能为空"
        w.WriteHeader(http.StatusBadRequest)
        json.NewEncoder(w).Encode(res)
        return
    }

    res.Result = "注册成功"
    json.NewEncoder(w).Encode(res)
}

更简洁的验证方案是使用json.DecoderDisallowUnknownFields方法:

func Registration(w http.ResponseWriter, r *http.Request) {
    var res ResponseResult
    w.Header().Set("Content-Type", "application/json")

    var user User
    decoder := json.NewDecoder(r.Body)
    decoder.DisallowUnknownFields() // 禁止未知字段
    
    if err := decoder.Decode(&user); err != nil {
        res.Error = fmt.Sprintf("请求数据错误: %v", err)
        w.WriteHeader(http.StatusBadRequest)
        json.NewEncoder(w).Encode(res)
        return
    }

    // 验证必需字段
    if user.Username == "" || user.Password == "" {
        res.Error = "用户名和密码不能为空"
        w.WriteHeader(http.StatusBadRequest)
        json.NewEncoder(w).Encode(res)
        return
    }

    res.Result = "注册成功"
    json.NewEncoder(w).Encode(res)
}

如果需要更复杂的验证,可以使用go-playground/validator包:

import "github.com/go-playground/validator/v10"

type User struct {
    Username string `json:"username" validate:"required,min=3,max=32"`
    Password string `json:"password" validate:"required,min=8"`
}

func Registration(w http.ResponseWriter, r *http.Request) {
    var res ResponseResult
    w.Header().Set("Content-Type", "application/json")

    var user User
    decoder := json.NewDecoder(r.Body)
    decoder.DisallowUnknownFields()
    
    if err := decoder.Decode(&user); err != nil {
        res.Error = fmt.Sprintf("请求数据错误: %v", err)
        w.WriteHeader(http.StatusBadRequest)
        json.NewEncoder(w).Encode(res)
        return
    }

    // 使用validator进行验证
    validate := validator.New()
    if err := validate.Struct(user); err != nil {
        res.Error = fmt.Sprintf("验证失败: %v", err)
        w.WriteHeader(http.StatusBadRequest)
        json.NewEncoder(w).Encode(res)
        return
    }

    res.Result = "注册成功"
    json.NewEncoder(w).Encode(res)
}
回到顶部