Skip to content

Commit

Permalink
feat: add support for Go 1.22+ net/http routing
Browse files Browse the repository at this point in the history
As part of #1068, we want to add support for the new Go 1.22+
`net/http`-only router, which will allow using `oapi-codegen` with
reduced external dependencies.

This requires we:

- wire in a new server, `std-http`
- add relevant templates for the router and strict server
- conditionally build/test/lint/etc the code when running on older
  versions of Go, which requires a bit of work in our `Makefile`
- use a separate module for the generated code, as it must set `go 1.22`
  in the `go.mod`
- document the fact that the `go.mod` needs updating, too, as it's
  caused some time to be lost in the past

Closes #1068.

Co-authored-by: Jamie Tanna <jamie@jamietanna.co.uk>
Signed-off-by: Donnie Adams <donnie@acorn.io>
  • Loading branch information
thedadams and jamietanna committed Mar 4, 2024
1 parent 74e9346 commit dd08298
Show file tree
Hide file tree
Showing 31 changed files with 3,523 additions and 14 deletions.
70 changes: 68 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ you can focus on implementing the business logic for your service.
We have chosen to focus on [Echo](https://github.com/labstack/echo) as
our default HTTP routing engine, due to its speed and simplicity for the generated
stubs. [Chi](https://github.com/go-chi/chi), [Gin](https://github.com/gin-gonic/gin),
[gorilla/mux](https://github.com/gorilla/mux), [Fiber](https://github.com/gofiber/fiber), and
[Iris](https://github.com/kataras/iris) have also been added by contributors as additional routers.
[gorilla/mux](https://github.com/gorilla/mux), [Fiber](https://github.com/gofiber/fiber),
[Iris](https://github.com/kataras/iris), and [1.22+ net/http](https://pkg.go.dev/net/http)
have also been added by contributors as additional routers.
We chose Echo because the `Context` object is a mockable interface, and it allows for some advanced
testing.

Expand Down Expand Up @@ -300,6 +301,69 @@ Alternatively, [Gorilla](https://github.com/gorilla/mux) is also 100% compatible

</details>

<details><summary><code>1.22+ net/http</code></summary>

As of Go 1.22, enhancements have been made to the routing of the `net/http` package in the standard library.
You can use `-generate std-http` to generate functions to help you associate your handlers with the auto-generated code.
For the pet store, it looks like this:

```go
// HandlerWithOptions creates http.Handler with additional options
func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.Handler {
m := options.BaseRouter

if m == nil {
m = http.NewServeMux()
}
if options.ErrorHandlerFunc == nil {
options.ErrorHandlerFunc = func(w http.ResponseWriter, r *http.Request, err error) {
http.Error(w, err.Error(), http.StatusBadRequest)
}
}

wrapper := ServerInterfaceWrapper{
Handler: si,
HandlerMiddlewares: options.Middlewares,
ErrorHandlerFunc: options.ErrorHandlerFunc,
}

m.HandleFunc("GET "+options.BaseURL+"/pets", wrapper.FindPets)
m.HandleFunc("POST "+options.BaseURL+"/pets", wrapper.AddPet)
m.HandleFunc("DELETE "+options.BaseURL+"/pets/{id}", wrapper.DeletePet)
m.HandleFunc("GET "+options.BaseURL+"/pets/{id}", wrapper.FindPetByID)

return m
}
```

The wrapper functions referenced above contain generated code which pulls parameters off the request and unmarshals them into Go objects.

You would register the generated handlers as follows:

```go
type PetStoreImpl struct {}
func (*PetStoreImpl) GetPets(w http.ResponseWriter, r *http.Request) {
// Implement me
}

func SetupHandler() {
var myApi PetStoreImpl

options := petstore.StdHTTPServerOptions{
BaseRouter: http.DefaultServeMux, // Or use a new ServeMux
}
petstore.HandlerWithOptions(&myApi, options)
}
```

**Note** that if you feel like you've done everything right, but are still receiving `404 page not found` errors, make sure that you've got the `go` directive in your `go.mod` updated to:

```go.mod
go 1.22
```

</details>

<details><summary><code>Iris</code></summary>

Code generated using `-generate iris`.
Expand Down Expand Up @@ -826,6 +890,8 @@ you can specify any combination of those.
on that produced by the `types` target.
- `iris`: generate the Iris server boilerplate. This code is dependent
on that produced by the `types` target.
- `std-http`: generate the Go stdlib net/http server boilerplate. This code is
dependent on that produced by the `types` target.
- `client`: generate the client boilerplate. It, too, requires the types to be
present in its package.
- `spec`: embed the OpenAPI spec into the generated code as a gzipped blob.
Expand Down
4 changes: 3 additions & 1 deletion cmd/oapi-codegen/oapi-codegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func main() {
// All flags below are deprecated, and will be removed in a future release. Please do not
// update their behavior.
flag.StringVar(&flagGenerate, "generate", "types,client,server,spec",
`Comma-separated list of code to generate; valid options: "types", "client", "chi-server", "server", "gin", "gorilla", "spec", "skip-fmt", "skip-prune", "fiber", "iris".`)
`Comma-separated list of code to generate; valid options: "types", "client", "chi-server", "server", "gin", "gorilla", "spec", "skip-fmt", "skip-prune", "fiber", "iris", "std-http".`)
flag.StringVar(&flagIncludeTags, "include-tags", "", "Only include operations with the given tags. Comma-separated list of tags.")
flag.StringVar(&flagExcludeTags, "exclude-tags", "", "Exclude operations that are tagged with the given tags. Comma-separated list of tags.")
flag.StringVar(&flagIncludeOperationIDs, "include-operation-ids", "", "Only include operations with the given operation-ids. Comma-separated list of operation-ids.")
Expand Down Expand Up @@ -486,6 +486,8 @@ func generationTargets(cfg *codegen.Configuration, targets []string) error {
opts.GinServer = true
case "gorilla", "gorilla-server":
opts.GorillaServer = true
case "std-http", "std-http-server":
opts.StdHTTPServer = true
case "strict-server":
opts.Strict = true
case "client":
Expand Down
36 changes: 36 additions & 0 deletions examples/petstore-expanded/stdhttp/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
SHELL:=/bin/bash

YELLOW := \e[0;33m
RESET := \e[0;0m

GOVER := $(shell go env GOVERSION)
GOMINOR := $(shell bash -c "cut -f2 -d. <<< $(GOVER)")

define execute-if-go-122
@{ \
if [[ 22 -le $(GOMINOR) ]]; then \
$1; \
else \
echo -e "$(YELLOW)Skipping task as you're running Go v1.$(GOMINOR).x which is < Go 1.22, which this module requires$(RESET)"; \
fi \
}
endef

lint:
$(call execute-if-go-122,$(GOBIN)/golangci-lint run ./...)

lint-ci:

$(call execute-if-go-122,$(GOBIN)/golangci-lint run ./... --out-format=github-actions --timeout=5m)

generate:
$(call execute-if-go-122,go generate ./...)

test:
$(call execute-if-go-122,go test -cover ./...)

tidy:
$(call execute-if-go-122,go mod tidy)

tidy-ci:
$(call execute-if-go-122,tidied -verbose)
6 changes: 6 additions & 0 deletions examples/petstore-expanded/stdhttp/api/cfg.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package: api
generate:
std-http-server: true
embedded-spec: true
models: true
output: petstore.gen.go

0 comments on commit dd08298

Please sign in to comment.