Answer the question
In order to leave comments, you need to log in
How to structure http server?
I'm using the gorilla/mux package and I've run into the problem that I have to put all the logic in one file. No matter how much I searched, I can’t find an example of how to properly separate an application.
I'll give you an example.
server.go file. It describes the server type and contains the function for creating a new server and all handlers, middlewares.
type server struct {
router *mux.Router
logger *logrus.Logger
store store.Store
sessionStore sessions.Store
}
func newServer(store store.Store, sessionStore sessions.Store) *server {
s := &server{
router: mux.NewRouter(),
logger: logrus.New(),
store: store,
sessionStore: sessionStore,
}
s.configureRouter()
return s
}
func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
s.router.ServeHTTP(w, r)
}
func (s *server) configureRouter() {
s.router.Use(s.setRequestID)
s.router.Use(s.logRequest)
s.router.Use(handlers.CORS(handlers.AllowedOrigins([]string{"*"})))
s.router.HandleFunc("/users", s.handleUsersCreate()).Methods("POST")
s.router.HandleFunc("/sessions", s.handleSessionCreate()).Methods("POST")
private := s.router.PathPrefix("/private").Subrouter()
private.Use(s.authenticateUser)
private.HandleFunc("/whoami", s.handleWhoami()).Methods("GET")
}
func (s *server) handleWhoami() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
s.respond(w, r, http.StatusOK, r.Context().Value(ctxKeyUser).(*model.User))
}
}
Answer the question
In order to leave comments, you need to log in
I use this approach:
1) Handlers are only concerned with processing input parameters from the client, for example, extracting parameters, displaying errors to the user;
2) Business logic is placed in a separate package or class;
3) Requests to external storages are also in a separate package;
4) Passing database or storage connections only through context using middleware.
Project structure example:
| + api
| - - router.go
| + models
| - - modelA.go
| - - modelB.go
| + app
| - - taskA.go
| - - taskB.go
| + database
| - - requests.go
| main.go
main.go:
package main
import "api"
func main(){
...
r := api.NewRouter(...)
http.ListenAndServe(...)
}
package api
import (
"app"
"models"
)
func NewRouter(...) ...{
// Инициализация роутера, регистрация обработчиков и т.д.
...
return r
}
func handleEndpointA(w http.ResponseWriter, r *http.Request){
// Извлечение, валидация параметров.
// Извлечение соединений БД и друхих хранилищ из контекста.
// Обработка ошибок.
...
result, err := app.DoTaskA(ctx, db, paramA, paramB)
...
render(w, result)
}
func handleEndpointB(w http.ResponseWriter, r *http.Request){
...
result, err := app.DoTaskB(ctx, db, paramC)
...
render(w, result)
}
package app
import(
"database"
"models"
)
func DoTaskA(ctx, db, paramA, paramB) (result, err){
// Какие-то манипуляции с параметрами.
...
// Запрос в бд.
modelA, err := database.RequestA(ctx, db, param)
...
return result, err
}
package database
import (
"models"
)
func RequestA(ctx, db, param) (result, err) {
// Не забываем поставить таймаут на запрос к БД.
ctx, cancel = context.WithTimeout(ctx, defaultRequestTimeout)
defer cancel()
...
result, err := db.DoRequest(ctx, ...)
...
return result, err
}
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question