Как правильно использовать UUID4 в приложении Golang? - PullRequest
0 голосов
/ 28 февраля 2019

Я новичок в Golang и мне нужна помощь!У меня есть несколько вопросов.

В базе данных PostgreSQL у меня есть таблица с именем surveys.

CREATE TABLE SURVEYS(
  SURVEY_ID UUID PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4(),
  SURVEY_NAME VARCHAR NOT NULL,
  SURVEY_DESCRIPTION TEXT,
  START_PERIOD TIMESTAMP,
  END_PERIOD TIMESTAMP
);

Как видите, столбец SURVEY_ID равен PRIMARY KEY, а его тип UUID4.

В приложении Golang я создаю для этой таблицы struct:

type Survey struct {
    ID string `json:"survey_id"`
    Name string `json:"survey_name"`
    Description utils.NullString `json:"survey_description"`
    StartPeriod utils.NullTime `json:"start_period"`
    EndPeriod utils.NullTime `json:"end_period"`
}

Как видите, тип поля идентификатора - string.Это правильно?Я не уверен, что это лучшая практика.

Мой второй вопрос о странном результате, который у меня возникает, когда я GET запрашиваю конкретный опрос по его идентификатору.

Например, когда я делаю такой запрос:

http://localhost:8000/api/survey/0cf1cf18-d5fd-474e-a8be-754fbdc89720

В качестве ответа я получаю следующее:

{
    "survey_id": "0cf1cf18-d5fd-474e-a8be-754fbdc89720",
    "survey_name": "NAME",
    "survey_description": {
        "String": "DESCRIPTION",
        "Valid": true
    },
    "start_period": {
        "Time": "2019-01-01T00:00:00Z",
        "Valid": false
    },
    "end_period": {
        "Time": "0001-01-01T00:00:00Z",
        "Valid": false
    }
}

Как вы можете видеть что-то не так с последними 3 полями: survey_description, start_period и end_period.Я хочу видеть ключ и значение в одной строке.Например, как здесь:

{
    "survey_id": "0cf1cf18-d5fd-474e-a8be-754fbdc89720",
    "survey_name": "NAME",
    "survey_description": "DESCRIPTION",
    "start_period": "2019-01-01 00:00:00",
    "end_period": null
}

Где именно я ошибаюсь в своем коде?

utils.go:

package utils

import (
    "database/sql"
    "encoding/json"
    "fmt"
    "github.com/lib/pq"
    "time"
)

// NullTime is an alias for pq.NullTime data type.
type NullTime struct {
    pq.NullTime
}

// MarshalJSON for NullTime.
func (nt *NullTime) MarshalJSON() ([]byte, error) {
    if !nt.Valid {
        return []byte("null"), nil
    }
    val := fmt.Sprintf("\"%s\"", nt.Time.Format(time.RFC3339))
    return []byte(val), nil
}

// UnmarshalJSON for NullTime.
func (nt *NullTime) UnmarshalJSON(b []byte) error {
    err := json.Unmarshal(b, &nt.Time)
    nt.Valid = err == nil
    return err
}

// NullInt64 is an alias for sql.NullInt64 data type.
type NullInt64 struct {
    sql.NullInt64
}

// MarshalJSON for NullInt64.
func (ni *NullInt64) MarshalJSON() ([]byte, error) {
    if !ni.Valid {
        return []byte("null"), nil
    }
    return json.Marshal(ni.Int64)
}

// UnmarshalJSON for NullInt64.
func (ni *NullInt64) UnmarshalJSON(b []byte) error {
    err := json.Unmarshal(b, &ni.Int64)
    ni.Valid = err == nil
    return err
}

// NullString is an alias for sql.NullString data type.
type NullString struct {
    sql.NullString
}

// MarshalJSON for NullString.
func (ns *NullString) MarshalJSON() ([]byte, error) {
    if !ns.Valid {
        return []byte("null"), nil
    }
    return json.Marshal(ns.String)
}

// UnmarshalJSON for NullString.
func (ns *NullString) UnmarshalJSON(b []byte) error {
    err := json.Unmarshal(b, &ns.String)
    ns.Valid = err == nil
    return err
}

rout.go:

router.HandleFunc("/api/survey/{survey_id:[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}}", controllers.GetSurvey).Methods("GET")

controllers / survey.go:

var GetSurvey = func(responseWriter http.ResponseWriter, request *http.Request) {
    // Initialize variables.
    survey := models.Survey{}
    var err error

    vars := mux.Vars(request)

    // Execute SQL statement.
    err = database.DB.QueryRow("SELECT * FROM surveys WHERE survey_id = $1;", vars["survey_id"]).Scan(&survey.ID, &survey.Name, &survey.Description, &survey.StartPeriod, &survey.EndPeriod)

    // Shape the response depending on the result of the previous command.
    if err != nil {
        log.Println(err)
        switch err {
        case sql.ErrNoRows:
            utils.ResponseWithError(responseWriter, http.StatusNotFound, "The entry not found.")
        default:
            utils.ResponseWithError(responseWriter, http.StatusInternalServerError, err.Error())
        }
        return
    }
    utils.Response(responseWriter, http.StatusOK, survey)
}

Ответы [ 2 ]

0 голосов
/ 01 марта 2019

Ну наконец-то я нашел результат.

Я изменил структуру таблицы:

type Survey struct {
    ID string `json:"survey_id"`
    Name string `json:"survey_name"`
    Description *string `json:"survey_description", sql:"index"`
    StartPeriod *time.Time `json:"start_period", sql:"index"`
    EndPeriod *time.Time `json:"end_period", sql:"index"`
}
0 голосов
/ 28 февраля 2019

Я не вижу проблем с использованием string для UUID.

Что касается MarshalJSON не работает, я думаю, что я знаю, что происходит.Ваши нулевые типы не реализуют MarshalJSON, только указатели на них.Исправление будет состоять в том, чтобы либо изменить функцию на использование приемника без указателя, либо сделать указатели полей в вашей структуре.

func (ns *NullString) MarshalJSON() ([]byte, error)

Если вы сделали их указателями, то вы могли бы просто оставить их такими, так как они обнуляемы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...