【发布时间】:2021-11-28 14:41:31
【问题描述】:
我不确定是什么原因造成的,但它抛出的原因是当我调用dec.DisallowUnknownFields() 时,它以某种方式认为密码不是用户结构/请求正文中的字段。尽管密码 JSON 是“-”。所以我最初想将结构字段密码的 JSON 更改为“密码”,但这会引发
internal server error: illegal base64 data at input byte 4
Postman 中的请求正文
{
"firstname": "George",
"lastname": "Costanza",
"email": "george@gmail.com",
"phone": "703-123-4567",
"password": "abc12"
}
我还尝试将密码类型更改为字符串,这也不起作用,但可以说是错误的解决方案,因为我们应该将密码作为哈希值存储在数据库中。所以,在这一点上,我已经没有地方可以转向为什么会发生这种情况了......
models.go
type User struct {
ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
FirstName string `json:"firstname"`
LastName string `json:"lastname"`
Email string `json:"email"`
Phone string `json:"phone"`
Password []byte `json:"-"`
Created time.Time `json:"-"`
Active bool `json:"-"`
Address Address `json:"address,omitempty"`
}
Helpers.go
type malformedRequest struct {
status int
msg string
}
func (mr *malformedRequest) Error() string {
return mr.msg
}
func (app *appInjection) decodeJSONBody(w http.ResponseWriter, r *http.Request, dst interface{}) error {
if r.Header.Get("Content-Type") != "" {
value, _ := header.ParseValueAndParams(r.Header, "Content-Type")
if value != "application/json" {
msg := "Content-Type header is not application/json"
return &malformedRequest{status: http.StatusUnsupportedMediaType, msg: msg}
}
}
r.Body = http.MaxBytesReader(w, r.Body, 1048576)
dec := json.NewDecoder(r.Body)
dec.DisallowUnknownFields()
err := dec.Decode(&dst)
if err != nil {
var syntaxError *json.SyntaxError
var unmarshalTypeError *json.UnmarshalTypeError
switch {
case errors.As(err, &syntaxError):
msg := fmt.Sprintf("Request body contains badly-formed JSON (at position %d)", syntaxError.Offset)
return &malformedRequest{status: http.StatusBadRequest, msg: msg}
case errors.Is(err, io.ErrUnexpectedEOF):
msg := fmt.Sprintf("Request body contains badly-formed JSON")
return &malformedRequest{status: http.StatusBadRequest, msg: msg}
case errors.As(err, &unmarshalTypeError):
msg := fmt.Sprintf("Request body contains an invalid value for the %q field (at position %d)", unmarshalTypeError.Field, unmarshalTypeError.Offset)
return &malformedRequest{status: http.StatusBadRequest, msg: msg}
case strings.HasPrefix(err.Error(), "json: unknown field "):
fieldName := strings.TrimPrefix(err.Error(), "json: unknown field ")
msg := fmt.Sprintf("Request body contains unknown field %s", fieldName)
return &malformedRequest{status: http.StatusBadRequest, msg: msg}
case errors.Is(err, io.EOF):
msg := "Request body must not be empty"
return &malformedRequest{status: http.StatusBadRequest, msg: msg}
case err.Error() == "http: request body too large":
msg := "Request body must not be larger than 1MB"
return &malformedRequest{status: http.StatusRequestEntityTooLarge, msg: msg}
default:
return err
}
}
err = dec.Decode(&struct{}{})
if err != io.EOF {
msg := "Request body must only contain a single JSON object"
return &malformedRequest{status: http.StatusBadRequest, msg: msg}
}
return nil
}
Handlers.go
func (app *appInjection) RegisterUser(w http.ResponseWriter, r *http.Request) {
// Guide addressing headers, syntax error's, and preventing extra data fields
// https://www.alexedwards.net/blog/how-to-properly-parse-a-json-request-body
w.Header().Set("Content-Type", "application/json")
var newUser models.User
//Parse the form data
//err := json.NewDecoder(r.Body).Decode(&newUser)
err := app.decodeJSONBody(w, r, &newUser)
if err != nil {
var mr *malformedRequest
if errors.As(err, &mr) {
http.Error(w, mr.msg, mr.status)
//app.clientError(w, mr.status)
} else {
log.Println(err.Error())
//app.clientError(w, http.StatusInternalServerError)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
return
}
//TODO: Validate the form
//If there is no error and the form is validated, create a new user from http request
//Insert the new user into the database
uid, _ := app.user.Insert(
newUser.FirstName,
newUser.LastName,
newUser.Email,
newUser.Phone,
string(newUser.Password))
json.NewEncoder(w).Encode("Record Inserted")
json.NewEncoder(w).Encode(uid)
}
用户.go
func (u *UserFunctions) Insert(firstname, lastname, email, phone, password string) (primitive.ObjectID, error) {
//Insert user to the database
userCollection := u.CLIENT.Database("queue").Collection("users")
var user models.User
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), 12)
if err != nil {
object, _ := primitive.ObjectIDFromHex("")
return object, err
}
user.FirstName = firstname
user.LastName = lastname
user.Email = email
user.Phone = phone
user.Password = hashedPassword
user.Created = time.Now().UTC()
user.Active = true
//Insert the user into the database
result, err := userCollection.InsertOne(context.TODO(), user)
if err != nil {
fmt.Println(err)
}
//Check ID of the inserted document
insertedID := result.InsertedID.(primitive.ObjectID)
//fmt.Println(insertedID)
return insertedID, nil
}
当我在 Postman 中运行请求以注册用户时,它会抛出消息
Request body contains unknown field "password"
【问题讨论】:
-
DisallowUnknownFields 导致解码器在目标是结构并且输入包含与目标中任何未忽略的导出字段不匹配的对象键时返回错误。字段标签
json:"-"指定忽略该字段。 -
如果我将“-”更改为“密码”,则会引发“内部服务器错误:输入字节 4 处的 base64 数据非法”
-
@PatrickCockrill: 因为
[]byte必须在 json 中进行 base64 编码。 -
«输入字节 4 处的非法 base64 数据»——这完全是另一回事。在您急于就 SO 提出另一个问题之前,请花点时间阅读golang.org/pkg/encoding/json,其中包含这两个问题的答案。谢谢!
-
base64