Мне нужна помощь с дизайном моего API, написанного на Go.Это структура файла:
database/
database.go
middlewares/
authentication.go
models/
pageview
services/
pageviews/
create/
main.go
show/
main.go
serverless.yml
К настоящему времени у меня есть только служба просмотра страниц.
Позвольте мне показать вам, что находится внутри обработчика, отвечающего за создание просмотра страницы (services / pageviews /)create / main.go):
package main
import (
"context"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
"github.com/clickhound/api/models"
)
func Handler(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
var pageviews models.Pageview
if err := pageviews.Create(request.Body); err != nil {
return events.APIGatewayProxyResponse{}, err
}
return events.APIGatewayProxyResponse{
StatusCode: 201,
}, nil
}
func main() {
lambda.Start(Handler)
}
Как видите, обработчик запросов (или контроллер) отвечает за делегирование создания ресурса модели, давайте посмотрим, что внутри модели просмотра страниц:
package models
import (
"encoding/json"
)
type Pageview struct {
ID string
Hostname string `gorm:"not null"`
}
func (p *Pageview) Create(data string) error {
if err := json.Unmarshal([]byte(data), p); err != nil {
return err
}
// TODO validate error here.
db.Create(p)
return nil
}
Итак, модель отвечает за:
- Разобрать тело запроса
- Создать новый ресурс
Thisначинает сбиваться, когда мне нужно вернуть данные в контроллер, скажем, у меня есть Find
просмотр страницы.Это обработчик запроса (или контроллер):
package main
import (
"context"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
"github.com/clickhound/api/middlewares"
"github.com/clickhound/api/models"
)
func Handler(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
var pageview models.Pageview
data, err := pageview.Find(request.PathParameters["id"])
if err != nil {
return events.APIGatewayProxyResponse{}, err
}
return events.APIGatewayProxyResponse{
StatusCode: 200,
Body: string(data),
}, nil
}
func main() {
lambda.Start(Handler))
}
И модели Find
функции:
func (p *Pageview) Find(id string) ([]byte, error) {
p.ID = id
// TODO validate error here.
db.Find(p)
return json.Marshal(p)
}
В этом случае модель отвечает за:
- Найти ресурс
- Маршал ресурса в JSON
Как видите, модель отвечает как за логику персистентности, так и за возврат ответа, который контроллердолжен делать свою работу - я чувствую, что что-то неуместно, но зачем я это делаю?
Я введу аутентификацию, и некоторые действия (например, Поиск просмотра страницы) на моделях должны быть ограничены текущим пользователем.Для этого я буду использовать промежуточное программное обеспечение authentication
, которое вводит текущего пользователя в пространство имен моделей:
package middlewares
import (
"context"
"github.com/aws/aws-lambda-go/events"
"github.com/clickhound/api/models"
)
func Authentication(next MiddlewareSignature) MiddlewareSignature {
return func(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
claims := request.RequestContext.Authorizer["claims"]
if models.InjectUser(claims).RecordNotFound() {
return events.APIGatewayProxyResponse{StatusCode: 401}, nil
}
return next(ctx, request)
}
}
И в моделях пользователей:
package models
import (
"time"
"github.com/jinzhu/gorm"
"github.com/mitchellh/mapstructure"
)
type User struct {
ID string `gorm:"not null"`
Email string `gorm:"not null;unique"`
CreatedAt time.Time
UpdatedAt time.Time
}
func InjectUser(claims interface{}) *gorm.DB {
if err := mapstructure.Decode(claims, user); err != nil {
panic(err)
}
return db.Find(&user)
}
var user User
Теперь,любой обработчик запросов (контроллер), который должен выполнять операции, ограниченные текущим пользователем, я могу изменить:
func main() {
lambda.Start(middlewares.Authentication(Handler))
}
на:
func main() {
lambda.Start(
middlewares.Authentication(Handler),
)
}
Некоторые вопросы:
- Что вы думаете о внедрении пользователя в пространство имен моделей?
- Что вы думаете об использовании обработчиков запросов (контроллеров) для вызова только нужной функции?
- Что вы думаете о моделях, отвечающих за логику персистентности, проверку действия базы данных, маршалинг / демаршаллинг данных запроса / ответа.