19/04/2023 - GO
Bu örnekte, HTTP işleyicilerinin kullanımını standart hale getirmesi için bir arayüz oluşturacağız. İşleyiciler bir yanıt nesnesi ve hata döndürür. API çağrıları için hata ve yanıt işlemeyi basitleştirmeye yardımcı olacaktır. Örnek basit bit seviyede tutuldu, ancak onu geliştirmelisiniz, o nedenle olduğu gibi kabul etmeyin.
package main
import (
"log"
"random/http"
"random/users"
)
func main() {
rtr := http.NewRouter()
rtr.Add("/api/v1/users", users.Handler)
srv := http.NewServer(rtr.Handler, ":3000")
log.Fatal(srv.ListenAndServe())
}
package http
import (
"net/http"
)
func NewServer(handler http.Handler, address string) *http.Server {
return &http.Server{
Handler: handler,
Addr: address,
}
}
package http
import "net/http"
type Router struct {
Handler *http.ServeMux
}
func NewRouter() *Router {
return &Router{
Handler: http.NewServeMux(),
}
}
func (r *Router) Add(path string, hnd handler) {
r.Handler.Handle(path, handler(hnd))
}
package http
import (
"errors"
"fmt"
"net/http"
)
type handler func(http.ResponseWriter, *http.Request) (*Response, error)
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
res, err := h(w, r)
if err == nil {
h.success(w, res)
return
}
h.error(w, err)
}
func (h handler) success(w http.ResponseWriter, res *Response) {
if err := res.Write(w); err != nil {
log.Println(err)
}
}
func (h handler) error(w http.ResponseWriter, err error) {
var e Error
if errors.As(err, &e) {
res := &Response{
Status: e.Status,
Code: e.Code,
Message: e.Message,
}
if err := res.Write(w); err != nil {
log.Println(err)
}
return
}
log.Println(err)
res := &Response{
Status: http.StatusInternalServerError,
Code: "internal_error",
Message: "An internal error has occurred",
}
if err := res.Write(w); err != nil {
log.Println(err)
}
}
package http
type Error struct {
Status int
Code string
Message string
}
func (e Error) Error() string {
return e.Message
}
import (
"encoding/json"
"fmt"
"net/http"
)
type Response struct {
Status int `json:"-"`
Headers map[string]string `json:"-"`
Data any `json:"data,omitempty"`
Meta any `json:"meta,omitempty"`
Code string `json:"code,omitempty"`
Message string `json:"message,omitempty"`
Errors map[string]string `json:"errors,omitempty"`
}
func (r *Response) Write(w http.ResponseWriter) error {
if r.Status < 200 || r.Status > 599 {
return fmt.Errorf("response: invalid status code: %d", r.Status)
}
for k, v := range r.Headers {
w.Header().Add(k, v)
}
if (r.Status >= 100 && r.Status <= 199) || r.Status == 204 || r.Status == 304 {
w.WriteHeader(r.Status)
return nil
}
bdy, err := json.Marshal(r)
if err != nil {
return fmt.Errorf("response: marshal body: %s", err.Error())
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(r.Status)
if _, err := w.Write(bdy); err != nil {
return fmt.Errorf("response: write body: %s", err.Error())
}
return nil
}
package domain
import (
"net/http"
httppkg "random/http"
)
var (
ErrInternalError = httppkg.Error{
Status: http.StatusInternalServerError,
Code: "internal_error",
Message: "An internal error has occurred",
}
ErrInvalidRequest = httppkg.Error{
Status: http.StatusBadRequest,
Code: "invalid_request",
Message: "Request is invalid",
}
ErrResourceConflict = httppkg.Error{
Status: http.StatusConflict,
Code: "resource_conflict",
Message: "A resource conflict has been detected",
}
ErrResourceNotFound = httppkg.Error{
Status: http.StatusNotFound,
Code: "resource_not_found",
Message: "Resource is not found",
}
)
package users
import (
"errors"
"net/http"
"random/domain"
httppkg "random/http"
)
func Handler(w http.ResponseWriter, r *http.Request) (*httppkg.Response, error) {
id := r.Header.Get("id")
if id == "" {
return nil, errors.New("not known error")
}
if id == "0" {
return nil, domain.ErrResourceNotFound
}
if id == "1" {
return nil, domain.ErrResourceConflict
}
if id == "3" {
return &httppkg.Response{
Status: domain.ErrInvalidRequest.Status,
Code: domain.ErrInvalidRequest.Code,
Message: domain.ErrInvalidRequest.Message,
Errors: map[string]string{"id": "Invalid digit"},
}, nil
}
return &httppkg.Response{
Status: http.StatusOK,
Data: "good job",
Meta: "paging",
}, nil
}