In diesem Artikel möchte ich Ihnen erklären, wie Sie schnell und einfach einen Webserver in der Sprache Golang mit Dokumentation dafür erstellen können. Und was sind die Ansätze und Werkzeuge für ihre Umsetzung
Heute werden wir diese vorgefertigten Werkzeuge analysieren:
Swagger Codegen
Beginnen wir mit swagger-api / swagger-codegen von swagger.io . Swagger Codegen vereinfacht den Erstellungsprozess, indem Serverstubs und Client-SDKs für jede in der OpenAPI-Spezifikation (früher als Swagger bezeichnet) definierte API erstellt werden. Sie müssen lediglich Ihre API implementieren.
Unten finden Sie ein Beispiel für unsere Swagger-Datei und ihr visuelles Erscheinungsbild im Swagger-Editor, in dem wir unsere Implementierungen für eine Sache generieren können.
swagger.json{
"swagger" : "2.0",
"info" : {
"version" : "1.0.0",
"title" : "Swagger Petstore"
},
"host" : "localhost",
"basePath" : "/v1",
"tags" : [ {
"name" : "pet"
} ],
"schemes" : [ "https", "http" ],
"paths" : {
"/pet" : {
"post" : {
"tags" : [ "pet" ],
"summary" : "Add a new pet to the store",
"operationId" : "addPet",
"consumes" : [ "application/json" ],
"produces" : [ "application/json" ],
"parameters" : [ {
"in" : "body",
"name" : "body",
"description" : "Pet object that needs to be added to the store",
"required" : true,
"schema" : {
"$ref" : "#/definitions/Pet"
}
} ],
"responses" : {
"405" : {
"description" : "Invalid input"
}
}
}
}
},
"definitions" : {
"Category" : {
"type" : "object",
"properties" : {
"id" : {
"type" : "integer",
"format" : "int64"
},
"name" : {
"type" : "string"
}
}
},
"Tag" : {
"type" : "object",
"properties" : {
"id" : {
"type" : "integer",
"format" : "int64"
},
"name" : {
"type" : "string"
}
}
},
"Pet" : {
"type" : "object",
"required" : [ "name", "photoUrls" ],
"properties" : {
"id" : {
"type" : "integer",
"format" : "int64"
},
"category" : {
"$ref" : "#/definitions/Category"
},
"name" : {
"type" : "string",
"example" : "doggie"
},
"photoUrls" : {
"type" : "array",
"items" : {
"type" : "string"
}
},
"tags" : {
"type" : "array",
"items" : {
"$ref" : "#/definitions/Tag"
}
},
"status" : {
"type" : "string",
"description" : "pet status in the store",
"enum" : [ "available", "pending", "sold" ]
}
}
}
}
}
Visuelle Ansicht in der Swagger-Benutzeroberfläche Export aus dem Swagger Editor Der Client und der Server haben identische Strukturobjekte. Beispieldatei go-client-generated/model_pet.go
und go-server-server-generated/go/model_pet.go
.
Haustierstrukturmodell in Client und Server
package swagger
type Pet struct {
Id int64 `json:"id,omitempty"`
Category *Category `json:"category,omitempty"`
Name string `json:"name"`
PhotoUrls []string `json:"photoUrls"`
Tags []Tag `json:"tags,omitempty"`
Status string `json:"status,omitempty"`
}
Der Client enthält auch Methoden zum Senden von Anforderungen sowie Server-Endpionten und -Stubs zum Verarbeiten dieser Anforderungen.
, . , .
go-swagger
— go-swagger/go-swagger. go API- Swagger API: , .
- Swagger
- Swagger
- , jsonschema swagger,
- swagger go
- swagger
- ,
:
$ swagger generate server -f ./swagger.json
, , . .
— go-swagger-service/restapi/configure_swagger_petstore.go
. : handler', middleware, , , .
package restapi
import (
"crypto/tls"
"net/http"
errors "github.com/go-openapi/errors"
runtime "github.com/go-openapi/runtime"
middleware "github.com/go-openapi/runtime/middleware"
"go-generator/go-swagger-service/restapi/operations"
"go-generator/go-swagger-service/restapi/operations/pet"
)
func configureFlags(api *operations.SwaggerPetstoreAPI) {
}
func configureAPI(api *operations.SwaggerPetstoreAPI) http.Handler {
api.ServeError = errors.ServeError
api.JSONConsumer = runtime.JSONConsumer()
api.JSONProducer = runtime.JSONProducer()
if api.PetAddPetHandler == nil {
api.PetAddPetHandler = pet.AddPetHandlerFunc(func(params pet.AddPetParams) middleware.Responder {
return middleware.NotImplemented("operation pet.AddPet has not yet been implemented")
})
}
api.ServerShutdown = func() {}
return setupGlobalMiddleware(api.Serve(setupMiddlewares))
}
func configureTLS(tlsConfig *tls.Config) {
}
func configureServer(s *http.Server, scheme, addr string) {
}
func setupMiddlewares(handler http.Handler) http.Handler {
return handler
}
func setupGlobalMiddleware(handler http.Handler) http.Handler {
return handler
}
. 422
.
swagger . enum
, required
, type
, maximum
.
Pet
package models
import (
"encoding/json"
"strconv"
strfmt "github.com/go-openapi/strfmt"
"github.com/go-openapi/errors"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
type Pet struct {
Category *Category `json:"category,omitempty"`
ID int64 `json:"id,omitempty"`
Name *string `json:"name"`
PhotoUrls []string `json:"photoUrls"`
Status string `json:"status,omitempty"`
Tags []*Tag `json:"tags"`
}
func (m *Pet) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateCategory(formats); err != nil {
res = append(res, err)
}
if err := m.validateName(formats); err != nil {
res = append(res, err)
}
if err := m.validatePhotoUrls(formats); err != nil {
res = append(res, err)
}
if err := m.validateStatus(formats); err != nil {
res = append(res, err)
}
if err := m.validateTags(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *Pet) validateCategory(formats strfmt.Registry) error {
if swag.IsZero(m.Category) {
return nil
}
if m.Category != nil {
if err := m.Category.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("category")
}
return err
}
}
return nil
}
func (m *Pet) validateName(formats strfmt.Registry) error {
if err := validate.Required("name", "body", m.Name); err != nil {
return err
}
return nil
}
func (m *Pet) validatePhotoUrls(formats strfmt.Registry) error {
if err := validate.Required("photoUrls", "body", m.PhotoUrls); err != nil {
return err
}
return nil
}
var petTypeStatusPropEnum []interface{}
func init() {
var res []string
if err := json.Unmarshal([]byte(`["available","pending","sold"]`), &res); err != nil {
panic(err)
}
for _, v := range res {
petTypeStatusPropEnum = append(petTypeStatusPropEnum, v)
}
}
const (
PetStatusAvailable string = "available"
PetStatusPending string = "pending"
PetStatusSold string = "sold"
)
func (m *Pet) validateStatusEnum(path, location string, value string) error {
if err := validate.Enum(path, location, value, petTypeStatusPropEnum); err != nil {
return err
}
return nil
}
func (m *Pet) validateStatus(formats strfmt.Registry) error {
if swag.IsZero(m.Status) {
return nil
}
if err := m.validateStatusEnum("status", "body", m.Status); err != nil {
return err
}
return nil
}
func (m *Pet) validateTags(formats strfmt.Registry) error {
if swag.IsZero(m.Tags) {
return nil
}
for i := 0; i < len(m.Tags); i++ {
if swag.IsZero(m.Tags[i]) {
continue
}
if m.Tags[i] != nil {
if err := m.Tags[i].Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("tags" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
func (m *Pet) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
func (m *Pet) UnmarshalBinary(b []byte) error {
var res Pet
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
$ run ./go-swagger-service/cmd/swagger-petstore-server/main.go --port 8090
:
$ swagger generate client -f ./swagger.json
, api-sdk. , , .
package pet
import (
"github.com/go-openapi/runtime"
strfmt "github.com/go-openapi/strfmt"
)
func New(transport runtime.ClientTransport, formats strfmt.Registry) *Client {
return &Client{transport: transport, formats: formats}
}
type Client struct {
transport runtime.ClientTransport
formats strfmt.Registry
}
func (a *Client) AddPet(params *AddPetParams) error {
if params == nil {
params = NewAddPetParams()
}
_, err := a.transport.Submit(&runtime.ClientOperation{
ID: "addPet",
Method: "POST",
PathPattern: "/pet",
ProducesMediaTypes: []string{"application/json"},
ConsumesMediaTypes: []string{"application/json"},
Schemes: []string{"http", "https"},
Params: params,
Reader: &AddPetReader{formats: a.formats},
Context: params.Context,
Client: params.HTTPClient,
})
if err != nil {
return err
}
return nil
}
func (a *Client) SetTransport(transport runtime.ClientTransport) {
a.transport = transport
}
. Generate a spec from source code.
. , swagger .
grpc-gateway
swagger . grpc-ecosystem/grpc-gateway — protoc
. gRPC -, RESTful JSON API gRPC. gRPC.
HTTP + JSON gRPC. API gRPC RESTful .
grpc-gateway reverse-proxy swagger.json protoc-gen-swagger.
. .
$ go install \
github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway \
github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger \
github.com/golang/protobuf/protoc-gen-go
.proto
protobufsyntax = "proto3";
import "google/api/annotations.proto";
import "google/protobuf/empty.proto";
package pet;
message AddPetRequest {
int64 id = 1;
message Category {
int64 id = 1;
string name = 2;
}
Category category = 2;
string name = 3;
repeated string photo_urls = 4;
message Tag {
int64 id = 1;
string name = 2;
}
repeated Tag tags = 5;
//pet status in the store
enum Status {
available = 0;
pending = 1;
sold = 2;
}
Status status = 6;
}
service PetService {
rpc AddPet (AddPetRequest) returns (google.protobuf.Empty) {
option (google.api.http) = {
post: "/pet"
body: "*"
};
}
}
gRPC stub:
$ protoc -I/usr/local/include -I. \
-I$GOPATH/src \
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--go_out=plugins=grpc:. \
pet.proto
pet.pb.go
. reverse-proxy protoc-gen-grpc-gateway
:
$ protoc -I/usr/local/include -I. \
-I$GOPATH/src \
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--grpc-gateway_out=logtostderr=true:. \
pet.proto
pet.pb.gw.go
. gateway. pet.swagger
.json protoc-gen-swagger
:
$ protoc -I/usr/local/include -I. \
-I$GOPATH/src \
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--swagger_out=logtostderr=true:. \
pet.proto
PetServiceServer.
type PetServiceServer interface {
AddPet(context.Context, *AddPetRequest) (*empty.Empty, error)
}
:
main.gopackage pet
import (
"context"
"flag"
"net/http"
"github.com/golang/glog"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
gw "grpc-gateway/pet"
"grpc-gateway/service"
)
func run() error {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
mux := runtime.NewServeMux()
err := gw.RegisterPetServiceHandlerServer(ctx, mux, &service.PetService{})
if err != nil {
return err
}
return http.ListenAndServe(":8081", mux)
}
func main() {
flag.Parse()
defer glog.Flush()
if err := run(); err != nil {
glog.Fatal(err)
}
}
, . , c grpc-gateway.
Swag
Swag Go Swagger 2.0. - Go. swaggo/swag Go ( Swagger UI).
:
Swagger , endpoint'. .
API-
func (c *ExampleController) ShowAccount(ctx *contex.Context) {
}
github.
, , .
Jetzt haben wir gerade unser Verständnis möglicher Interaktionen mit der Prahlerdokumentation erweitert. Wir haben Möglichkeiten untersucht, den Code zu entwerfen und die Dokumentation dafür zu kompilieren.
Und ich denke, dass einige bereits die geeignete Methode zur Implementierung der API ihres neuen Projekts ausgewählt oder die analysierten Beispiele mit den aktuellen verglichen haben.