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.