Devolver errores de postgres en la respuesta de la API

Dec 24 2020

Tengo dos métodos api simples en mi código. El método con endpoind /api/user/createcrea usuario. El campo usernamees único. Cuando intento crear un usuario con el mismo nombre de usuario que ya existe en la base de datos, tengo un error en la consola:

(/home/andrej/go/src/go_contacts/models/users.go:19) 
[2020-12-23 22:03:10]  pq: duplicate key value violates unique constraint "users_username_key"

Quiero mostrar este error en respuesta al usuario, o identificar de alguna manera el tipo de error en mi código, para mostrar diferentes mensajes de error para el usuario. Solo sé que si tengo un error, userme devuelve id = 0. Pero no parece un buen mensaje para el usuario.

main.go

package main

import (
    "fmt"
    "go_contacts/controllers"
    "net/http"
    "os"

    "github.com/gorilla/mux"
    "github.com/joho/godotenv"
)

func main() {
    godotenv.Load(".env")


    router := mux.NewRouter()
    router.HandleFunc("/", controllers.ReturnHello).Methods("GET")
    router.HandleFunc("/api/user/create", controllers.CreateUser).Methods("POST")

    port := os.Getenv("PORT")
    if port == "" {
        port = "8000"
    }

    err := http.ListenAndServe(":"+port, router)
    if err != nil {
        fmt.Print(err)
    }
}

models.go con estructura de usuario:

package models

import (
    u "go_contacts/utils"

    "github.com/jinzhu/gorm"
)

// User base model
type User struct {
    gorm.Model
    Username string `json:"username" gorm:"unique"`
    Password string `json:"password"`
    Email    string `json:"email"`
}

// Create new user
func (user *User) Create() map[string]interface{} {
    GetDB().Create(user)

    if user.ID <= 0 {
        return u.Message(false, "Failed to create user, connection error.")
    }

    response := u.Message(true, "Account has been created")
    response["user"] = user
    return response
}

Respuestas

1 AlekseySpirin Dec 24 2020 at 17:04

En cuanto a pq v1.5.2 y gorm v1.9.12

Primero, debe identificar si la llamada al método de creación devuelve un error o no. Al igual que

err := GetDB().Create(user).Error
if err != nil {
    // code to handle error
}

Luego, el paquete pq tiene un tipo especial que se asigna al error del servidor de Postgres. Contiene campos que pueden ayudarlo a identificar la gravedad del error, su código, tabla relacionada con el error, etc.

La lista completa de identificadores de campos psql se puede encontrar aquí en

Campos de mensaje de error y aviso

https://www.postgresql.org/docs/current/protocol-error-fields.html

Para resolver su problema como una opción, podemos usar el campo

Code

Que es una representación de tipo cadena del código de error. Pero, en primer lugar, necesitamos emitir un error y comprobar que el tipo de conversión se realizó correctamente. Al igual que

err := GetDB().Create(user).Error
if err != nil {
    pqErr, ok := err.(pq.Error)
    if !ok {
        log.Fatal(err)
    }

    // code to handle specific error code
}

La lista de códigos de error se puede encontrar tanto en los documentos oficiales de postgresql https://www.postgresql.org/docs/9.3/errcodes-appendix.html

Y asignaciones específicas de go pq al error en github.com/lib/pq/error.go como para la biblioteca pq

Y finalmente podemos manejar el error como duplicado por código.

err := GetDB().Create(user).Error
if err != nil {
    pqErr, ok := err.(pq.Error)
    if !ok {
        log.Fatal(err)
    }

    // note that type casting here is redundant and used for showing specific 
    // string type from pq library
    if pqErr.Code == pq.ErrorCode("23505") { // 23505 is unique_violation error code for psql
        // now you can create error and write it to a message to return it for 
        // a client
    }
}
1 Brits Dec 24 2020 at 02:59

Según los comentarios, gorm proporciona acceso a los errores de la base de datos desde la función Crear registro de la siguiente manera:

result := GetDB().Create(user)
if result.Error != nil {
   // Do something with the error
}

Tenga en cuenta que es probable que la comprobación de la cadena de error haga que la base de datos de su aplicación sea específica (pero es posible que esto no sea un problema para usted). Las respuestas a esta pregunta pueden proporcionar más ayuda.