04/04/2024 - GO
Go 1.22, net/http paketi yönlendiricisinin geliştirilmiş bir sürümünü tanıttı. Buna yöntem eşleştirme ve joker karakterler de dahildir. Bu örnekte bunun için özel bir HTTP paketi oluşturacağız. Paket aynı zamanda global ve yönlendirmeye özel ara katman yazılımlarını bonus puanı olarak kullanmamıza da olanak tanıyacak.
package main
import (
"context"
"log"
"os"
"os/signal"
"syscall"
"time"
"rest/pkg/http"
"rest/pkg/middleware"
"rest/src/api"
)
func main() {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
// HTTP api
user := api.User{}
// HTTP router
rtr := http.NewRouter()
rtr.Use(middleware.AccessLogger)
rtr.Add("POST /api/v1/users", user.Create, middleware.DeprecatedRoute)
rtr.Add("DELETE /api/v1/users/{id}", user.Delete)
// HTTP server
srv := http.NewServer(rtr, ":1234")
go func() {
log.Println("api running ...")
if err := srv.Start(); err != nil {
log.Fatalln(err)
}
}()
if err := srv.Shutdown(ctx, time.Second*5); err != nil {
log.Println(err)
}
log.Println("api shutdown ...")
}
package http
import (
"net/http"
"slices"
)
type Router struct {
handler *http.ServeMux
middlewares []func(http.Handler) http.Handler
}
func NewRouter() *Router {
return &Router{
handler: http.NewServeMux(),
}
}
func (r *Router) Use(middlewares ...func(http.Handler) http.Handler) {
r.middlewares = middlewares
}
func (r *Router) Add(path string, handler http.HandlerFunc, middlewares ...func(http.Handler) http.Handler) {
mids := slices.Concat(r.middlewares, middlewares)
for i := len(mids) - 1; i >= 0; i-- {
handler = mids[i](handler).(http.HandlerFunc)
}
r.handler.HandleFunc(path, handler)
}
package http
import (
"context"
"net/http"
"time"
)
type Server struct {
server *http.Server
}
func NewServer(router *Router, address string) Server {
return Server{
server: &http.Server{
Handler: router.handler,
Addr: address,
},
}
}
func (s Server) Start() error {
if err := s.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
return err
}
return nil
}
func (s Server) Shutdown(ctx context.Context, timeout time.Duration) error {
<-ctx.Done()
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
if err := s.server.Shutdown(ctx); err != nil {
return err
}
return nil
}
package middleware
import (
"log"
"net/http"
)
func AccessLogger(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println(r.Method + " " + r.RequestURI)
next.ServeHTTP(w, r)
})
}
package middleware
import (
"log"
"net/http"
)
func DeprecatedRoute(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("this route has been deprecated")
w.WriteHeader(http.StatusGone)
})
}
package api
import (
"log"
"net/http"
)
type User struct{}
func (u User) Create(w http.ResponseWriter, r *http.Request) {
log.Println("user create")
}
func (u User) Delete(w http.ResponseWriter, r *http.Request) {
log.Println("user delete", r.PathValue("id"))
}
$ curl -iX DELETE "http://localhost:1234/api/v1/users/1"
2024/06/04 19:56:52 DELETE /api/v1/users/1
2024/06/04 19:56:52 user delete 1
$ curl -iX POST "http://localhost:1234/api/v1/users" -d '{"key":"val"}'
2024/06/04 19:56:55 POST /api/v1/users
2024/06/04 19:56:55 this route has been deprecated