В этой статье мы сосредоточимся на пошаговой реализации создания REST API с использованием архитектурного шаблона Model-View-Controller (MVC) в Golang.
В шаблоне MVC модель представляет данные и бизнес-логику,представление представляет уровень представления, а Контроллер действует как посредник между Моделью и Представлением, получая данные от пользователя и соответствующим образом обновляя Модель и Представление. Компоненты Repository и Service часто используются для взаимодействия с источником данных и выполнения дополнительной бизнес-логики. .
В целом шаблон MVC помогает разделить задачи приложения на отдельные компоненты, что может сделать код более организованным и более простым в обслуживании.
Создайте новый проект и инициализируйте новый модуль Go:
$ mkdir myapi $ cd myapi $ go mod init github.com/myuser/myapi
Давайте начнем с создания нашей структуры каталогов:В этом примере у нас есть шесть каталогов: контроллеры, фиктивные данные, модели, репозиторий, маршруты и сервис, а также один основной файл main.go.
├── controllers │ └── user_controller.go ├── mockdata │ └── user_data.go ├── models │ └── user_model.go ├── repository │ └── user_repository.go ├── routes │ └── user_routes.go ├── service │ └── user_service.go └── main.go
Далее давайте определим нашу модель. В нашей модели мы определяем нашу структуру Пользователь, которая имеет четыре поля: идентификатор, имя, фамилия, адрес электронной почты и возраст. Мы также создаем пустой фрагмент структур User с именем Users.
package models type User struct { ID int64 `json:"id"` FirstName string `json:"first_name"` LastName string `json:"last_name"` Email string `json:"email"` Age int16 `json:"age"` } // This will serve as our database var Users []User
Давайте определим фиктивные данные пользователя для достижения этой цели, здесь вы можете получить реальные данные из базы данных, создав и подключившись к базе данных.
package mockdata import "restapi.com/myuser/myapi/models" var Users = []models.User{ { ID: 1, FirstName: "Pooja", LastName: "Varma", Email: "[email protected]", Age: 25, }, { ID: 2, FirstName: "pooja", LastName: "varma", Email: "[email protected]", Age: 30, }, }
Теперь давайте определим наш уровень репозитория среализацией интерфейса UserRepository в Golang, который использует фиктивные данные:
В этом примере мы определили структуру UserRepository, которая содержит фрагмент объектов User для представления фиктивных пользовательских данных. Структура UserRepository реализует методы интерфейса UserRepository (GetAllUsers, GetUserByID, CreateUser, UpdateUser и DeleteUser) для упрощения операций CRUD с пользовательскими данными.
В этом примере мы имеем дело с фиктивными данными, но здесь вы можете подключиться к базе данных и выполнять операции с базой данных.
// repository/user_repository.go package repository import ( "restapi.com/myuser/myapi/mockdata" "restapi.com/myuser/myapi/models" ) // UserRepository interface type UserRepository interface { GetAllUsers() ([]models.User, error) GetUserByID(id int64) (models.User, error) CreateUser(user models.User) (int64, error) UpdateUser(id int64, user models.User) error DeleteUser(id int64) error } // Mock data to use during testing // UserRepository struct type userRepository struct { } // NewUserRepository function returns a new UserRepository instance with mock data func NewUserRepository() UserRepository { return &userRepository{} } // GetAllUsers function returns all users func (*userRepository) GetAllUsers() ([]models.User, error) { return mockdata.Users, nil } // GetUserByID function returns a user by ID func (*userRepository) GetUserByID(id int64) (models.User, error) { for _, user := range mockdata.Users { if user.ID == id { return user, nil } } return models.User{}, nil } // CreateUser function creates a new user func (*userRepository) CreateUser(user models.User) (int64, error) { user.ID = int64(len(mockdata.Users) + 1) mockdata.Users = append(mockdata.Users, user) return user.ID, nil } // UpdateUser function updates an existing user func (*userRepository) UpdateUser(id int64, user models.User) error { for i, u := range mockdata.Users { if u.ID == id { user.ID = id mockdata.Users[i] = user return nil } } return nil } // DeleteUser function deletes a user by ID func (r *userRepository) DeleteUser(id int64) error { for i, user := range mockdata.Users { if user.ID == id { mockdata.Users = append(mockdata.Users[:i], mockdata.Users[i+1:]...) break } } return nil }
Вот пример реализации UserService в Golang. В этом примере мы определили структуру UserService, содержащую ссылку на интерфейс UserRepository. Структура UserService реализует методы для облегчения операций CRUD с пользовательскими данными путем вызова соответствующих методов интерфейса UserRepository. К этому уровню можно добавить бизнес-логику.
// service/user_service.go package service import ( "restapi.com/myuser/myapi/models" "restapi.com/myuser/myapi/repository" ) // UserRepository interface type UserService interface { GetAllUsers() ([]models.User, error) GetUserByID(id int64) (models.User, error) CreateUser(user models.User) (int64, error) UpdateUser(id int64, user models.User) error DeleteUser(id int64) error } // UserService struct type userService struct { } func NewUserService() UserService { return &userService{} } // Variable of UserRepository var ( s repository.UserRepository = repository.NewUserRepository() ) // GetUsers function returns all users func (*userService) GetAllUsers() ([]models.User, error) { return s.GetAllUsers() } // GetUserByID function returns a user by ID func (*userService) GetUserByID(id int64) (models.User, error) { return s.GetUserByID(id) } // CreateUser function creates a new user func (*userService) CreateUser(user models.User) (int64, error) { return s.CreateUser(user) } // UpdateUser function updates an existing user func (*userService) UpdateUser(id int64, user models.User) error { return s.UpdateUser(id, user) } // DeleteUser function deletes a user by ID func (*userService) DeleteUser(id int64) error { return s.DeleteUser(id) }
Далее давайте определим наш контроллер: в нашем контроллере мы определяем пять функций: GetUsers, GetUserByID, CreateUser, UpdateUser и DeleteUser. Эти функции обрабатывают HTTP-запросы.
// controllers/user_controller.go package controllers import ( "encoding/json" "net/http" "strconv" "github.com/gorilla/mux" "restapi.com/myuser/myapi/models" "restapi.com/myuser/myapi/service" ) //creating user_service variable var ( c service.UserService = service.NewUserService() ) // GetUsers function handles GET /users requests func GetUsers(w http.ResponseWriter, r *http.Request) { //calling GetAllUsers method from UserService users, err := c.GetAllUsers() if err != nil { w.WriteHeader(http.StatusInternalServerError) return } json.NewEncoder(w).Encode(users) } // GetUserByID function handles GET /users/{id} requests func GetUserByID(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id, _ := strconv.ParseInt(vars["id"], 10, 64) user, err := c.GetUserByID(id) if err != nil { w.WriteHeader(http.StatusNotFound) return } json.NewEncoder(w).Encode(user) } // CreateUser function handles POST /users requests func CreateUser(w http.ResponseWriter, r *http.Request) { var user models.User err := json.NewDecoder(r.Body).Decode(&user) if err != nil { w.WriteHeader(http.StatusBadRequest) return } id, err := c.CreateUser(user) if err != nil { w.WriteHeader(http.StatusInternalServerError) return } w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(map[string]int64{"id": id}) } // UpdateUser function handles PUT /users/{id} requests func UpdateUser(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id, _ := strconv.ParseInt(vars["id"], 10, 64) var user models.User err := json.NewDecoder(r.Body).Decode(&user) if err != nil { w.WriteHeader(http.StatusBadRequest) return } err = c.UpdateUser(id, user) if err != nil { w.WriteHeader(http.StatusNotFound) return } w.WriteHeader(http.StatusNoContent) } // DeleteUser function handles DELETE /users/{id} requests func DeleteUser(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id, _ := strconv.ParseInt(vars["id"], 10, 64) err := c.DeleteUser(id) if err != nil { w.WriteHeader(http.StatusNotFound) return } w.WriteHeader(http.StatusNoContent) }
Затем давайте определим нашуреализацию пакета маршрутов для определения логики маршрутизации для пользовательских HTTP-запросов с использованием Golang и пакета мультиплексирования Gorilla:
В этом примере мы определили функцию SetupRoutes, которая создает новый маршрутизатор с помощью пакета мультиплексирования Gorilla и определяет маршруты для пользовательских HTTP-запросов, используя соответствующие функции контроллера из пакета контроллера. Маршрутизатор также определяет маршрут по умолчанию для всех остальных HTTP-запросов.
// routes/routes.go package routes import ( "net/http" "restapi.com/myuser/myapi/controllers" "github.com/gorilla/mux" ) func SetupRoutes() *mux.Router { router := mux.NewRouter() // Define routes for user-related HTTP requests router.HandleFunc("/users", controllers.GetUsers).Methods("GET") router.HandleFunc("/users/{id}", controllers.GetUserByID).Methods("GET") router.HandleFunc("/users", controllers.CreateUser).Methods("POST") router.HandleFunc("/users/{id}", controllers.UpdateUser).Methods("PUT") router.HandleFunc("/users/{id}", controllers.DeleteUser).Methods("DELETE") // Define default route for all other HTTP requests router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Welcome to my API!")) }) return router }
Теперь, наконец, реализация основного пакета, который создает новый экземпляр маршрутизатора, определенного в пакете маршрутов, и запускает веб-сервер с помощью http.ListenAndServe() функция
// main.go package main import ( "log" "net/http" "restapi.com/myuser/myapi/routes" ) func main() { router := routes.SetupRoutes() log.Println("Starting server on :8080...") err := http.ListenAndServe(":8080", router) if err != nil { log.Fatal(err) } }
В этом примере мы создали новый экземпляр маршрутизатора, определенного в пакете маршрутов, с помощью функции SetupRoutes и запустили веб-сервер с помощью функции http.ListenAndServe(). Сервер прослушивает входящие HTTP-запросы через порт 8080.
Теперь выполните приведенную ниже команду там, где находится проект, т. е. перейдите в корневой каталог вашего проекта Go, который содержит файл go.mod.
$ cd myapi $ go mod tidy
go mod tidy — это команда в Go, которая автоматически обновляет зависимости модулей проекта Go до версий, указанных в файле go.mod.
Наконец, запустите приложение, выполнив
$ go run main.go
После выполнения команды go run main.go сервер запустился на порте 8080. Протестируйте REST API с помощью postman или любого другого подходящего инструмента.
В заключение, реализация REST API с использованием шаблона проектирования Go Lang MVC может стать отличным способом создания масштабируемого и удобного в сопровождении веб-приложения. Шаблон проектирования Model-View-Controller (MVC) позволяет разработчикам разделять задачи и организовывать код таким образом, чтобы его было легко понять и модифицировать.
Используя встроенные функции Go Lang, такие как пакет net/http и мультиплексор Gorilla, разработчики могут легко создавать быстрые, эффективные и гибкие REST API. Внедряя правильную маршрутизацию, обработчики и промежуточное ПО, разработчики могут обеспечить безопасность и надежность своих конечных точек API.
Кроме того, поддержка параллелизма в Go Lang позволяет создавать высокопроизводительные и масштабируемые веб-приложения. Разработчики могут использовать go-процедуры и каналы для обработки больших объемов запросов и обеспечения гибкости и надежности своего API.
В целом, использование шаблона проектирования Go Lang MVC для реализации REST API может стать эффективным способом создания современных веб-приложений, которые легко поддерживать, расширять и масштабировать.
Вы можете найти исходный код этого урока здесь.