We can officially provide: github.com/go-swagger/… The main ways are: 1. Write our GO program; 2. Let Swagger tool scan our GO file; 3.
We will start with the following problems: 1. Local generation of annotations will cause problems due to inconsistent versions of Swagger, and various Git conflicts will occur. 2. It violates the way programming is done. 3. The reason why Java swagger is so popular is that it is perfectly integrated with Spring-boot, so what Spring solves is actually ioc control, so go Web does not need programs to control, routing, input and output, so we need to do so.
preparation
1. The controoler interface of spring-boot needs to be implemented
General requirements: 1, get the route, 2, get the request, response (this is go all web framework can not do, so need to change your mind)
@RestController
public class UserController {
@Autowired
private UserRepository repository;
@PostMapping("/save")
public User save(UserInfoDto info) {
returnrepository.save(User.builder().id(info.getId()).userName(info.getName()).build()); }}Copy the code
2. Change your mind
This reflection call framework, I wrote a, I hope you submit more bugs, because reflection itself has many defects: github.com/Anthony-Don…
var (
userService = service.NewUserService()
)
func userRoute(e *engines.Engine) {
g := e.Group("/report")
g.POST("benchmark", http.NewHandlerFunc(userService.BenchMark))
}
// Define context. context (essential), similar to Java's ThreadLocal
// request * to.benchmarkRequest request
/ / * dto. BenchmarkResponse response
// error Since go itself has no throw, a display declaration is required
// Consider adding the Web module later, but the current mainstream Web frameworks rely on CTX to pass down, so go and Java are different (not currently considered, you can consider using gin context). Although http-Request and http-Response can be accepted in Springmvc, the user doesn't care because of its bind.
type UserService interface {
BenchMark(ctx context.Context, request *dto.BenchmarkRequest) (*dto.BenchmarkResponse, error)
}
Copy the code
That’s done, so let’s get started, because we can get all of this.
3. Define a strong route
1. Since GO itself does not have method-level annotations, what if we could declare interfaces on every interface and routes on every method
// path=/user_service
type UserService interface {
// path=/benchmark method=get
BenchMark(ctx context.Context, request *dto.BenchmarkRequest) (*dto.BenchmarkResponse, cerror.Cerror)
}
Copy the code
If so, it would be particularly unfriendly to go developers
2. All shall be registered by FUNC
You can specify get/ other after the interface
g.POST("benchmark", http.NewHandlerFunc(userService.BenchMark),op.Get())
Copy the code
The current approach: 1, can display the declaration, which is in line with the go developers’ habit, 2, can not scan the GO file (spring-Boot adopted this approach)
Integrate Swagger client
Find swagger’s Web resource bundle and expose it all. The core is /swagger. Json.
Resources in my swagger of this project github.com/Anthony-Don… In the future, static resources will be considered in the way of API call (forwarding). At present, it is written in the go file with the help of tools: github.com/a-urth/go-b… “, this tool is nice, it can write files to go files, in binary form, we know that GO can not be packaged into jar packages, only binary files, static resources is a very uncomfortable thing, so we need to do this.
const (
js = "js"
css = "css"
html = "html"
byte = "byte"
json = "json"
)
func addSwagger(path string, _type string) func(writer http.ResponseWriter, request *http.Request) {
return func(writer http.ResponseWriter, request *http.Request) {
body, _ := swagger.Asset(path)
switch _type {
case js:
writer.Header().Set("content-type"."application/javascript")
case css:
writer.Header().Set("content-type"."text/css")
case json:
writer.Header().Set("content-type"."application/json")}if _type == byte {
fmt.Fprint(writer, body)
return
}
fmt.Fprint(writer, string(body))
}
}
func addSwaggerRouter(path string, _type string) {
http.HandleFunc("/"+path, addSwagger(path, _type))
}
func main(a) {
// Swagger built-in server needs something
addSwaggerRouter("swagger-ui/absolute-path.js", js)
addSwaggerRouter("swagger-ui/favicon-16x16.png".byte)
addSwaggerRouter("swagger-ui/favicon-32x32.png".byte)
addSwaggerRouter("swagger-ui/index.html", html)
addSwaggerRouter("swagger-ui/index.js", js)
addSwaggerRouter("swagger-ui/oauth2-redirect.html", html)
addSwaggerRouter("swagger-ui/package.json", json)
addSwaggerRouter("swagger-ui/swagger-ui-bundle.js", js)
addSwaggerRouter("swagger-ui/swagger-ui-bundle.js.map", html)
addSwaggerRouter("swagger-ui/swagger-ui-standalone-preset.js", js)
addSwaggerRouter("swagger-ui/swagger-ui-standalone-preset.js.map", html)
addSwaggerRouter("swagger-ui/swagger-ui.css", css)
addSwaggerRouter("swagger-ui/swagger-ui.css.map", html)
addSwaggerRouter("swagger-ui/swagger-ui.js", js)
addSwaggerRouter("swagger-ui/swagger-ui.js.map", html)
http.HandleFunc("/swagger.json".func(writer http.ResponseWriter, request *http.Request) {
writer.Header().Set("content-type"."application/json")
fmt.Fprintf(writer, doc)
})
http.ListenAndServe(": 8888".nil)}Copy the code
swagger-json
This file comes from Faygo, swagger core is an API interface, similar to the following
{
"swagger": "2.0"."info": {
"description": "Spring Swaager2 REST API"."version": "V1"."title": "REST API"."contact": {
"name": "anthony"."url": "https://github.com/Anthony-Dong"."email": "[email protected]"
},
"license": {
"name": "The Apache License"."url": "https://opensource.org/licenses/MIT"}},"host": "localhost:8888"."basePath": "/"."tags": [{"name": "user-controller"."description": "User Controller"}]."paths": {
"/find/{id}": {
"get": {
"tags": [
"user-controller"]."summary": "find"."operationId": "findUsingGET"."produces": [
"* / *"]."parameters": [{"name": "id"."in": "path"."description": "id"."required": true."type": "integer"."format": "int64"}]."responses": {
"200": {
"description": "OK"."schema": {
"$ref": "#/definitions/User"}}},"deprecated": false}},"/save/{name}": {
"get": {
"tags": [
"user-controller"]."summary": "echo"."operationId": "echoUsingGET"."produces": [
"* / *"]."parameters": [{"name": "name"."in": "path"."description": "name"."required": true."type": "string"}]."responses": {
"200": {
"description": "OK"."schema": {
"$ref": "#/definitions/User"}}},"deprecated": false}}},"definitions": {
"User": {
"type": "object"."properties": {
"id": {
"type": "integer"."format": "int64"
},
"userName": {
"type": "string"}},"title": "User"}}}Copy the code
Write doc step by step
Here is the rough structure of swagger-doc that we have to inject our API into
doc := swagger.Swagger{ Version: swagger.Version, Info: &swagger.Info{ Title: "REST API", Contact: &swagger.Contact{ Email: "[email protected]", }, License: &swagger.License{ Name: "The Apache License", Url: "https://opensource.org/licenses/MIT", }, }, Host: "localhost:8888", BasePath: "/", Tags: []* Paths: Map [string] Paths: map[string] Paths: map[string] Paths: map[string] Paths: map[string] Paths: map[string] Paths: map[string] Paths: map[string] Paths: map[string] Paths: Map [string]*swagger.Definition{},Copy the code
What is the tag
User-controller is a tag, for go it’s usually a group and you can define it as a controller
// Tag object
Tag struct {
Name string `json:"name"` // A tag: user-controller
Description string `json:"description"` // a des: user-controller
}
Copy the code
All paths can also specify multiple tags
Opera struct {
Tags []string `json:"tags"` // Here is the tag
Summary string `json:"summary"`
Description string `json:"description"`
OperationId string `json:"operationId"`
Consumes []string `json:"consumes,omitempty"`
Produces []string `json:"produces,omitempty"`
Parameters []*Parameter `json:"parameters,omitempty"`
Responses map[string]*Resp `json:"responses"` // {"httpcode":resp}
Security []map[string] []string `json:"security,omitempty"`
}
Copy the code
To know the path
This is a path
Paths map[string]map[string]*Opera `json:"paths,omitempty"`
Copy the code
The structure is a two-level map
"/save": {
"post": {
"tags": [
"user-controller"]."summary": "echo"."operationId": "echoUsingPOST"."consumes": [ // Request type
"application/json"]."produces": [ // Response type
"* / *"]."parameters": [ // Core points of concern, if multiple levels
{
"name": "id".// Field description
"in": "query"./ / query
"required": false.// For strong requirements, you can use binding (https://github.com/go-playground/validator).
"type": "integer"./ / type
"format": "int64" // True type (go type)
},
{
"name": "name"."in": "query"."required": false."type": "string"
},
{
"name": "user.id"."in": "query"."required": false."type": "integer"."format": "int64"
},
{
"name": "user.userName"."in": "query"."required": false."type": "string"}]."responses": {
"200": {/ / state
"description": "OK".// Response description
"schema": {
"$ref": "#/definitions/User" // specify routing :(nice, so our dtos are all in modle)}}},"deprecated": false}},Copy the code
conclusion
From this, we can get that there is a certain rule about whether it is particularly difficult or not, so next, we start to design the framework.