Refactor to JSON API with Chi router and interface-based design
- Add pkg/greet/api_v1.go with ApiV1Greet interface and handler - Implement Greeter interface for dependency injection - Return JSON responses with proper Content-Type headers - Move greet service instantiation to server package - Separate v1 route registration into registerApiV1Routes function - Health endpoint at /api level, greet endpoints at /api/v1/greet - Simplify server entrypoint by removing external dependencies
This commit is contained in:
@@ -8,10 +8,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
service := greet.NewService()
|
||||||
name := ""
|
name := ""
|
||||||
if len(os.Args) > 1 {
|
if len(os.Args) > 1 {
|
||||||
name = os.Args[1]
|
name = os.Args[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(greet.Greet(name))
|
fmt.Println(service.Greet(name))
|
||||||
}
|
}
|
||||||
15
cmd/server/main.go
Normal file
15
cmd/server/main.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"DanceLessonsCoach/pkg/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
server := server.NewServer()
|
||||||
|
|
||||||
|
fmt.Println("Server running on :8080")
|
||||||
|
http.ListenAndServe(":8080", server.Router())
|
||||||
|
}
|
||||||
2
go.mod
2
go.mod
@@ -1,3 +1,5 @@
|
|||||||
module DanceLessonsCoach
|
module DanceLessonsCoach
|
||||||
|
|
||||||
go 1.26.1
|
go 1.26.1
|
||||||
|
|
||||||
|
require github.com/go-chi/chi/v5 v5.2.5 // indirect
|
||||||
|
|||||||
2
go.sum
Normal file
2
go.sum
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug=
|
||||||
|
github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=
|
||||||
44
pkg/greet/api_v1.go
Normal file
44
pkg/greet/api_v1.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package greet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Greeter interface {
|
||||||
|
Greet(name string) string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ApiV1Greet interface {
|
||||||
|
RegisterRoutes(router chi.Router)
|
||||||
|
}
|
||||||
|
|
||||||
|
type apiV1GreetHandler struct {
|
||||||
|
greeter Greeter
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewApiV1GreetHandler(greeter Greeter) ApiV1Greet {
|
||||||
|
return &apiV1GreetHandler{greeter: greeter}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *apiV1GreetHandler) RegisterRoutes(router chi.Router) {
|
||||||
|
router.Get("/", h.handleGreetQuery)
|
||||||
|
router.Get("/{name}", h.handleGreetPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *apiV1GreetHandler) handleGreetQuery(w http.ResponseWriter, r *http.Request) {
|
||||||
|
name := r.URL.Query().Get("name")
|
||||||
|
h.writeJSONResponse(w, h.greeter.Greet(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *apiV1GreetHandler) handleGreetPath(w http.ResponseWriter, r *http.Request) {
|
||||||
|
name := chi.URLParam(r, "name")
|
||||||
|
h.writeJSONResponse(w, h.greeter.Greet(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *apiV1GreetHandler) writeJSONResponse(w http.ResponseWriter, message string) {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(map[string]string{"message": message})
|
||||||
|
}
|
||||||
@@ -1,8 +1,15 @@
|
|||||||
package greet
|
package greet
|
||||||
|
|
||||||
|
type Service struct{}
|
||||||
|
|
||||||
|
func NewService() *Service {
|
||||||
|
return &Service{}
|
||||||
|
}
|
||||||
|
|
||||||
// Greet returns a greeting message for the given name.
|
// Greet returns a greeting message for the given name.
|
||||||
// If name is empty, it defaults to "world".
|
// If name is empty, it defaults to "world".
|
||||||
func Greet(name string) string {
|
// Implements the Greeter interface.
|
||||||
|
func (s *Service) Greet(name string) string {
|
||||||
if name == "" {
|
if name == "" {
|
||||||
return "Hello world!"
|
return "Hello world!"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ package greet
|
|||||||
|
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestGreet(t *testing.T) {
|
func TestService_Greet(t *testing.T) {
|
||||||
|
service := NewService()
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
expected string
|
expected string
|
||||||
@@ -15,7 +16,7 @@ func TestGreet(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
result := Greet(tt.name)
|
result := service.Greet(tt.name)
|
||||||
if result != tt.expected {
|
if result != tt.expected {
|
||||||
t.Errorf("Greet(%q) = %q, want %q", tt.name, result, tt.expected)
|
t.Errorf("Greet(%q) = %q, want %q", tt.name, result, tt.expected)
|
||||||
}
|
}
|
||||||
|
|||||||
58
pkg/server/server.go
Normal file
58
pkg/server/server.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"DanceLessonsCoach/pkg/greet"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
"github.com/go-chi/chi/v5/middleware"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
router *chi.Mux
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServer() *Server {
|
||||||
|
s := &Server{
|
||||||
|
router: chi.NewRouter(),
|
||||||
|
}
|
||||||
|
s.setupRoutes()
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) setupRoutes() {
|
||||||
|
s.router.Use(middleware.Logger)
|
||||||
|
s.router.Route("/api", func(r chi.Router) {
|
||||||
|
r.Use(s.apiMiddlewares()...)
|
||||||
|
r.Get("/health", s.handleHealth)
|
||||||
|
s.registerApiV1Routes(r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) registerApiV1Routes(r chi.Router) {
|
||||||
|
r.Route("/v1", func(r chi.Router) {
|
||||||
|
|
||||||
|
greetService := greet.NewService()
|
||||||
|
greetHandler := greet.NewApiV1GreetHandler(greetService)
|
||||||
|
r.Route("/greet", func(r chi.Router) {
|
||||||
|
greetHandler.RegisterRoutes(r)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) apiMiddlewares() []func(http.Handler) http.Handler {
|
||||||
|
return []func(http.Handler) http.Handler{
|
||||||
|
middleware.StripSlashes,
|
||||||
|
middleware.Recoverer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Write([]byte(`{"status":"healthy"}`))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Router() http.Handler {
|
||||||
|
return s.router
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user