Files
Tosone 7b98fc29c0 refactor(server): simplify handler wrapping and request binding
Remove the unused Wrap/WrapHandle helpers and the HandlerFunc type; route
registration now passes handler method values directly. Drop the
unreachable application/x-www-form-urlencoded and multipart/form-data
branches in bindBody since every bound endpoint is JSON, and merge
adapter.go into request.go. Add tests for the query/param mapping error
paths, the nil-validator path, and non-JSON bodies, raising the package
coverage to 100%.

Verification:
- go build -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./...
- go vet -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/server/
- CI_DATABASE_TYPE=turso go test -tags "netgo,timetzdata,exclude_graphdriver_btrfs,containers_image_openpgp" ./pkg/server/...
- go tool cover -func: pkg/server 100.0% of statements
2026-10-07 21:07:06 +08:00

81 lines
2.3 KiB
Go

// Copyright 2026 sigma
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package server
import (
"encoding/json/v2"
"log/slog"
"net/http"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/go-sigma/sigma/pkg/server/errcode"
)
// RequestHandlerFunc is the handler signature for endpoints with a typed,
// validated request object.
type RequestHandlerFunc[T any] func(c *gin.Context, req *T)
// WrapRequest binds and validates a typed request before invoking h.
func WrapRequest[T any](h RequestHandlerFunc[T]) gin.HandlerFunc {
return func(c *gin.Context) {
req := new(T)
if err := BindRequest(c, req); err != nil {
slog.Error("bind and validate request failed", "err", err, "path", c.Request.URL.Path)
errcode.NewHTTPError(c, errcode.HTTPErrCodeBadRequest, err.Error())
return
}
h(c, req)
}
}
// BindRequest binds request body, query parameters, and path parameters into
// dst, then validates the fully populated request once.
func BindRequest(c *gin.Context, dst any) error {
if err := bindBody(c, dst); err != nil {
return err
}
if err := binding.MapFormWithTag(dst, c.Request.URL.Query(), "query"); err != nil {
return err
}
if err := binding.MapFormWithTag(dst, paramsMap(c.Params), "param"); err != nil {
return err
}
if binding.Validator == nil {
return nil
}
return binding.Validator.ValidateStruct(dst)
}
func bindBody(c *gin.Context, dst any) error {
if c.Request == nil || c.Request.Body == nil || c.Request.Body == http.NoBody || c.Request.ContentLength == 0 {
return nil
}
if c.ContentType() != binding.MIMEJSON {
return nil
}
return json.UnmarshalRead(c.Request.Body, dst)
}
func paramsMap(params gin.Params) map[string][]string {
values := make(map[string][]string, len(params))
for _, param := range params {
values[param.Key] = []string{param.Value}
}
return values
}