我需要帮助设计用go编写的API。这就是文件结构:
database/
database.go
middlewares/
authentication.go
models/
pageview
services/
pageviews/
create/
main.go
show/
main.go
serverless.yml
现在我只有页面浏览服务。
让我向您展示负责创建页面视图(services/pageview s/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)
}
如您所见,请求处理程序(或控制器)负责将资源的创建委托给模型,让我们看看pageview模型中的内容:
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
}
因此,模型负责:
取消标记请求正文
创建新资源
当我需要将数据返回到控制器时,这就开始变得混乱,假设我有一个
Find
pageview。这是请求处理程序(或控制器):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))
}
模型的功能是:
func (p *Pageview) Find(id string) ([]byte, error) {
p.ID = id
// TODO validate error here.
db.Find(p)
return json.Marshal(p)
}
在这种情况下,模型负责:
查找资源
将资源封送到JSON
正如您所看到的,模型既负责持久性逻辑,也返回控制器完成其工作所需的响应——我感觉有些东西放错了地方,但为什么要这样做?
我将介绍身份验证,并且模型上的一些操作(如find pageview)应该仅限于当前用户。为了实现这一点,我将使用一个将当前用户注入模型名称空间的
Find
中间件: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),
)
}
一些问题:
您对在模型的名称空间中注入用户有什么看法?
您认为使用请求处理程序(控制器)只调用正确的函数如何?
您对负责持久性逻辑、验证数据库操作、对请求/响应数据进行编组/解编组的模型有什么看法?
最佳答案
最好使用一些模块将业务逻辑与传输细节隔离开来。它只是两个不同的抽象层次,如果我们不混合它们,代码就会变得更干净。尽管如此,它应该是实用的,我们可以保留HTTP代码,因为它们现在是通用语言,并且如果您的业务逻辑返回500和400来处理不同类型的错误,也没有什么错。
如果我编写这段代码,那么这种分离将是控制器的主要目标。
业务逻辑层(模型)应该与建模业务域的强类型对象一起工作,而不需要了解HTTP或AWS lambda实现的详细信息。
要处理的控制器:
路由
API版本控制
序列化/反序列化,包括URL参数、头、AWS lambda特定字段等
模型:
接收并返回强类型对象和错误
验证输入(根据框架,这可以部分移动到控制器)
IO,包括为身份验证和业务事务按ID加载用户
关于go - 用Go编写的lambda API的设计,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53582834/