基础配置
examples/github/api/api.go 在 interface 上写了 @BaseURL 和 @Header。
// @BaseURL("https://api.github.com")
// @Header("Accept", "application/vnd.github.v3+json")
type GitHub interface {
genapi.Interface
// @GET("/repos/{owner}/{repo}/contributors")
Contributors(ctx context.Context, owner string, repo string) ([]Contributor, error)
// @POST("/repos/{owner}/{repo}/issues")
CreateIssue(ctx context.Context, issue Issue, owner string, repo string) error
}
HTTP 方法
examples/github 使用 @GET 和 @POST。website/api/todo.go 还使用 @PUT 和 @DELETE。解析器接受这些名字,以及 RequestLine.alias 返回的别名。
// @GET("/repos/{owner}/{repo}/contributors")
Contributors(ctx context.Context, owner string, repo string) ([]Contributor, error)
// @POST("/repos/{owner}/{repo}/issues")
CreateIssue(ctx context.Context, issue Issue, owner string, repo string) error
// @PUT("/todos/{id}")
UpdateTodo(ctx context.Context, id string, todo Todo) (Todo, error)
// @DELETE("/todos/{id}")
DeleteTodo(ctx context.Context, id string) error
func (r RequestLine) alias() []string {
return []string{
http.MethodGet,
http.MethodHead,
http.MethodPost,
http.MethodPut,
http.MethodPatch,
http.MethodDelete,
http.MethodConnect,
http.MethodOptions,
http.MethodTrace,
}
}
参数
路径里的 {owner} 这类占位符对应同名参数。端到端测试里的 @Query 接受一个 key 和一个 value。写成 {name} 的 value 取自同名参数。
// @GET("/get")
// @Query("key", "value")
// @Query("value", "{value}")
// @Header("X-Hello", "world")
Get(value string) (*Response, error)
// @POST("/post")
请求体
CreateIssue 接收一个 Issue。生成的方法把 Body 设为这个值。
// @POST("/repos/{owner}/{repo}/issues")
CreateIssue(ctx context.Context, issue Issue, owner string, repo string) error
}
type Contributor struct {
Login string `json:"login"`
Contributions int `json:"contributions"`
}
type Issue struct {
Title string `json:"title"`
Body string `json:"body"`
Assignees []string `json:"assignees"`
Milestone int `json:"milestone"`
Labels []string `json:"labels"`
}
func (i *implGitHub) CreateIssue(ctx context.Context, issue Issue, owner string, repo string) error {
resp, err := i.client.Do(&genapi.Request{
Method: "POST",
Path: "/repos/{owner}/{repo}/issues",
PathParams: map[string]string{
"owner": owner,
"repo": repo,
},
Context: ctx,
Body: issue,
})
return genapi.HandleResponse0(resp, err)
}
响应
Contributors 返回 ([]Contributor, error),生成的方法调用 genapi.HandleResponse。CreateIssue 返回 error,生成的方法调用 genapi.HandleResponse0。单个非 error 的返回值会使用 genapi.MustHandleResponse,binder 里就是这样写的。
switch typ {
case "error":
results = model.BindingResults{
Assignment: "resp, err",
Statement: "genapi.HandleResponse0(resp, err)",
}
case "genapi.Response":
results = model.BindingResults{
Assignment: "resp, _",
Statement: "*resp",
}
case "*genapi.Response":
results = model.BindingResults{
Assignment: "resp, _",
Statement: "resp",
}
default:
if disallowedResultPrimitives[typ] {
return &ErrUnsupported{Message: fmt.Sprintf("type [%s] is not supported as a result type", typ)}
}
results = model.BindingResults{
Assignment: "resp, err",
Statement: fmt.Sprintf("genapi.MustHandleResponse[%s](resp, err)", typ),
}
请求头
GitHub interface 为每次调用设置 Accept。httpbin 端到端测试还给方法设置了请求头。两处都是 @Header 加名字和值。
// @BaseURL("https://api.github.com")
// @Header("Accept", "application/vnd.github.v3+json")
type GitHub interface {
genapi.Interface
// @GET("/repos/{owner}/{repo}/contributors")
Contributors(ctx context.Context, owner string, repo string) ([]Contributor, error)
// @POST("/repos/{owner}/{repo}/issues")
CreateIssue(ctx context.Context, issue Issue, owner string, repo string) error
}
// @GET("/get")
// @Query("key", "value")
// @Query("value", "{value}")
// @Header("X-Hello", "world")
Get(value string) (*Response, error)
// @POST("/post")