Pada artikel ini saya ingin memberi tahu Anda tentang bagaimana Anda dapat dengan cepat dan mudah membuat server web dalam bahasa Golang dengan dokumentasi untuknya. Dan tentang apa saja pendekatan dan alat untuk implementasinya
Hari ini kita akan menganalisis alat yang sudah jadi ini:
Sombong codegen
Mari kita mulai dengan swagger-api / swagger-codegen yang disediakan oleh swagger.io . Swagger Codegen menyederhanakan proses pembuatan dengan membuat stubs server dan SDK klien untuk API apa pun yang ditentukan dalam spesifikasi OpenAPI (sebelumnya dikenal sebagai Swagger), yang harus Anda lakukan adalah menerapkan API Anda.
Di bawah ini adalah contoh file swagger kami dan tampilan visualnya di Swagger Editor, di mana kami dapat menghasilkan implementasi kami untuk satu hal.
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" ]
}
}
}
}
}
Tampilan visual di UI Sombong Ekspor dari Editor Swagger Klien dan server memiliki objek struktur yang identik. File contoh go-client-generated/model_pet.go
dan go-server-server-generated/go/model_pet.go
.
Model struktur hewan peliharaan di klien dan 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"`
}
Klien juga berisi metode untuk mengirim permintaan, dan endpionts dan stub server untuk memprosesnya.
, . , .
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.
, , .
Sekarang kami baru saja memperluas pemahaman kami tentang kemungkinan interaksi dengan dokumentasi kesombongan. Kami memeriksa berbagai cara untuk merancang kode dan menyusun dokumentasi untuknya.
Dan saya pikir beberapa telah memilih metode yang tepat untuk mengimplementasikan API dari proyek baru mereka, atau mencoba contoh yang dianalisis terhadap yang sekarang.