gorillamux

package
v0.2.57 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 13, 2023 License: MIT Imports: 8 Imported by: 2

Documentation

Overview

Package gorillamux provides OpenAPI docs collector for gorilla/mux web services.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func PathToURLValues

func PathToURLValues(r *http.Request) (url.Values, error)

PathToURLValues is a decoder function for parameters in path.

Example
package main

import (
	"bytes"
	"fmt"
	"net/http"
	"net/http/httptest"

	"github.com/gorilla/mux"
	"github.com/swaggest/rest"
	"github.com/swaggest/rest/gorillamux"
	"github.com/swaggest/rest/request"
)

func main() {
	// Instantiate decoder factory with gorillamux.PathToURLValues.
	// Single factory can be used to create multiple request decoders.
	decoderFactory := request.NewDecoderFactory()
	decoderFactory.ApplyDefaults = true
	decoderFactory.SetDecoderFunc(rest.ParamInPath, gorillamux.PathToURLValues)

	// Define request structure for your HTTP handler.
	type myRequest struct {
		Query1    int     `query:"query1"`
		Path1     string  `path:"path1"`
		Path2     int     `path:"path2"`
		Header1   float64 `header:"X-Header-1"`
		FormData1 bool    `formData:"formData1"`
		FormData2 string  `formData:"formData2"`
	}

	// Create decoder for that request structure.
	dec := decoderFactory.MakeDecoder(http.MethodPost, myRequest{}, nil)

	router := mux.NewRouter()

	// Now in router handler you can decode *http.Request into a Go structure.
	router.Handle("/foo/{path1}/bar/{path2}", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		var in myRequest

		_ = dec.Decode(r, &in, nil)

		fmt.Printf("%+v\n", in)
	}))

	// Serving example URL.
	w := httptest.NewRecorder()
	req, _ := http.NewRequest(http.MethodPost, "/foo/abc/bar/123?query1=321",
		bytes.NewBufferString("formData1=true&formData2=def"))

	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set("X-Header-1", "1.23")

	router.ServeHTTP(w, req)
}
Output:

{Query1:321 Path1:abc Path2:123 Header1:1.23 FormData1:true FormData2:def}

Types

type OpenAPICollector

type OpenAPICollector struct {
	// Collector is an actual OpenAPI collector.
	Collector *openapi.Collector

	// DefaultMethods list is used when handler serves all methods.
	DefaultMethods []string

	// OperationExtractor allows flexible extraction of OpenAPI information.
	OperationExtractor func(h http.Handler) func(oc oapi.OperationContext) error

	// Host filters routes by host, gorilla/mux can serve different handlers at
	// same method, paths with different hosts. This can not be expressed with a single
	// OpenAPI document.
	Host string
}

OpenAPICollector is a wrapper for openapi.Collector tailored to walk gorilla/mux router.

func NewOpenAPICollector

func NewOpenAPICollector(r oapi.Reflector) *OpenAPICollector

NewOpenAPICollector creates route walker for gorilla/mux, that collects OpenAPI operations.

Example
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"

	"github.com/gorilla/mux"
	"github.com/swaggest/openapi-go"
	"github.com/swaggest/openapi-go/openapi3"
	"github.com/swaggest/rest"
	"github.com/swaggest/rest/gorillamux"
	"github.com/swaggest/rest/nethttp"
	"github.com/swaggest/rest/request"
)

// Define request structure for your HTTP handler.
type myRequest struct {
	Query1    int     `query:"query1"`
	Path1     string  `path:"path1"`
	Path2     int     `path:"path2"`
	Header1   float64 `header:"X-Header-1"`
	FormData1 bool    `formData:"formData1"`
	FormData2 string  `formData:"formData2"`
}

type myResp struct {
	Sum    float64 `json:"sum"`
	Concat string  `json:"concat"`
}

func newMyHandler() *myHandler {
	decoderFactory := request.NewDecoderFactory()
	decoderFactory.ApplyDefaults = true
	decoderFactory.SetDecoderFunc(rest.ParamInPath, gorillamux.PathToURLValues)

	return &myHandler{
		dec: decoderFactory.MakeDecoder(http.MethodPost, myRequest{}, nil),
	}
}

type myHandler struct {
	// Automated request decoding is not required to collect OpenAPI schema,
	// but it is good to have to establish a single source of truth and to simplify request reading.
	dec nethttp.RequestDecoder
}

func (m *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var in myRequest

	if err := m.dec.Decode(r, &in, nil); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)

		return
	}

	// Serve request.
	out := myResp{
		Sum:    in.Header1 + float64(in.Path2) + float64(in.Query1),
		Concat: in.Path1 + in.FormData2 + strconv.FormatBool(in.FormData1),
	}

	j, err := json.Marshal(out)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)

		return
	}

	_, _ = w.Write(j)
}

// SetupOpenAPIOperation declares OpenAPI schema for the handler.
func (m *myHandler) SetupOpenAPIOperation(oc openapi.OperationContext) error {
	oc.SetTags("My Tag")
	oc.SetSummary("My Summary")
	oc.SetDescription("This endpoint aggregates request in structured way.")

	oc.AddReqStructure(myRequest{})
	oc.AddRespStructure(myResp{})
	oc.AddRespStructure(nil, openapi.WithContentType("text/html"), openapi.WithHTTPStatus(http.StatusBadRequest))
	oc.AddRespStructure(nil, openapi.WithContentType("text/html"), openapi.WithHTTPStatus(http.StatusInternalServerError))

	return nil
}

func main() {
	// Your router does not need special instrumentation.
	router := mux.NewRouter()

	// If handler implements gorillamux.OpenAPIPreparer, it will contribute detailed information to OpenAPI document.
	router.Handle("/foo/{path1}/bar/{path2}", newMyHandler()).Methods(http.MethodGet)

	// If handler does not implement gorillamux.OpenAPIPreparer, it will be exposed as incomplete.
	router.Handle("/uninstrumented-handler/{path-item}",
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})).Methods(http.MethodPost)

	// Setup OpenAPI schema.
	refl := openapi3.NewReflector()
	refl.SpecSchema().SetTitle("Sample API")
	refl.SpecSchema().SetVersion("v1.2.3")
	refl.SpecSchema().SetDescription("This is an example.")

	// Walk the router with OpenAPI collector.
	c := gorillamux.NewOpenAPICollector(refl)

	_ = router.Walk(c.Walker)

	// Get the resulting schema.
	yml, _ := refl.Spec.MarshalYAML()
	fmt.Println(string(yml))

}
Output:

openapi: 3.0.3
info:
  description: This is an example.
  title: Sample API
  version: v1.2.3
paths:
  /foo/{path1}/bar/{path2}:
    get:
      description: This endpoint aggregates request in structured way.
      parameters:
      - in: query
        name: query1
        schema:
          type: integer
      - in: path
        name: path1
        required: true
        schema:
          type: string
      - in: path
        name: path2
        required: true
        schema:
          type: integer
      - in: header
        name: X-Header-1
        schema:
          type: number
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GorillamuxTestMyResp'
          description: OK
        "400":
          content:
            text/html:
              schema:
                type: string
          description: Bad Request
        "500":
          content:
            text/html:
              schema:
                type: string
          description: Internal Server Error
      summary: My Summary
      tags:
      - My Tag
  /uninstrumented-handler/{path-item}:
    post:
      description: Information about this operation was obtained using only HTTP method
        and path pattern. It may be incomplete and/or inaccurate.
      parameters:
      - in: path
        name: path-item
        required: true
        schema:
          type: string
      responses:
        "200":
          content:
            text/html:
              schema:
                type: string
          description: OK
      tags:
      - Incomplete
components:
  schemas:
    GorillamuxTestMyResp:
      properties:
        concat:
          type: string
        sum:
          type: number
      type: object

func (*OpenAPICollector) Walker

func (dc *OpenAPICollector) Walker(route *mux.Route, _ *mux.Router, _ []*mux.Route) error

Walker walks route tree and collects OpenAPI information.

type OpenAPIPreparer

type OpenAPIPreparer interface {
	SetupOpenAPIOperation(oc oapi.OperationContext) error
}

OpenAPIPreparer defines http.Handler with OpenAPI information.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL