Skip to main content

gin-gonic/gin 🥤


You have to create a gin middleware on your own. In the following demo code, BindInput is a good example to start.

Run Demo

package main

import (


// BindInput instances an httpin engine for an input struct as a gin middleware.
func BindInput(inputStruct interface{}) gin.HandlerFunc {
engine, err := httpin.New(inputStruct)
if err != nil {

return func(c *gin.Context) {
input, err := engine.Decode(c.Request)
if err != nil {
var invalidFieldError *httpin.InvalidFieldError
if errors.As(err, &invalidFieldError) {
c.AbortWithStatusJSON(http.StatusBadRequest, invalidFieldError)

ctx := context.WithValue(c.Request.Context(), httpin.Input, input)
c.Request = c.Request.WithContext(ctx)

type ListUsersInput struct {
Gender string `in:"query=gender"`
AgeRange []int `in:"query=age_range"`
IsMember bool `in:"query=is_member"`

func ListUsers(c *gin.Context) {
input := c.Request.Context().Value(httpin.Input).(*ListUsersInput)
fmt.Printf("input: %#v\n", input)

func main() {
router := gin.New()

// Bind input struct with handler.
router.GET("/users", BindInput(ListUsersInput{}), ListUsers)

r, _ := http.NewRequest("GET", "/users?gender=male&age_range=18&age_range=24&is_member=1", nil)

rw := httptest.NewRecorder()
router.ServeHTTP(rw, r)

Since it will run timeout on the Go Playground. I removed the Run button for the above demo code. You can test the above snippet by using the following command on your local host:

mkdir /tmp/test && cd $_

touch main.go
# then COPY & PASTE the above code to main.go

go mod init test
go mod tidy

go run main.go

The output on my machine looks like this:

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET /users --> main.ListUsers (2 handlers)
input: &main.ListUsersInput{Gender:"male", AgeRange:[]int{18, 24}, IsMember:true}