Skip to main content

header

header is a directive executor who decodes/encodes a field from/to HTTP headers, i.e. http.Request.Header.

Signature

Name: "header"
Args: KEY1 [,KEY2 [,KEY3, ...]]

Decoding

header will examine values of the keys one by one (KEY1 -> KEY2 -> ...) from the HTTP headers, the first non-empty value will be used to set the corresponding field of the input struct.

caution

The keys will be normalized, see CanonicalHeaderKey, e.g. the header key of Content-Type and content-type are the same, which works like case-insensitive.

Usage

type TokenInput struct {
Token string `in:"header=x-api-token,Authorization"`
}
Request (Header)TokenInput
GET /users HTTP/1.1

Host: foo.example
X-API-Token: abc
{ Token: "abc" }
GET /users HTTP/1.1

Host: foo.example
x-api-token: abc
{ Token: "abc" }
GET /users HTTP/1.1

Host: foo.example
Authorization: good
{ Token: "good" }
GET /users HTTP/1.1

Host: foo.example
X-Api-Token: apple
Authorization: banana
{ Token: "apple" }

// Check order: X-Api-Token -> Authorization

Runable Example

package main

import (
"fmt"
"io"
"net/http"
"net/http/httptest"

"github.com/ggicci/httpin"
"github.com/go-chi/chi/v5"
)

type UpdateAccountInput struct {
AccessToken string `in:"header=x-myapp-access-token"`
ClientVersion int `in:"header=x-myapp-client-version"`
}

func UpdateAccount(rw http.ResponseWriter, r *http.Request) {
input := r.Context().Value(httpin.Input).(*UpdateAccountInput)
fmt.Printf("input: %#v\n", input)
}

func main() {
router := chi.NewRouter()
router.With(
httpin.NewInput(UpdateAccountInput{}),
).Patch("/me", UpdateAccount)

r, _ := http.NewRequest("PATCH", "/me", nil)
r.Header.Set("x-myapp-access-token", "rainbow")
r.Header.Set("X-Myapp-Client-Version", "284")
r.Body = io.NopCloser(nil)

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

Output:

input: &main.UpdateAccountInput{AccessToken:"rainbow", ClientVersion:284}

Encoding

header will encode the field value to the HTTP headers, the first key will be used, i.e. KEY1, results in http.Request.Header.Set(KEY1, VALUE).

Runable Example

package main

import (
"fmt"
"net/http/httputil"

"github.com/ggicci/httpin"
)

type UpdateAccountInput struct {
AccessToken string `in:"header=x-myapp-access-token"`
ClientVersion int `in:"header=x-myapp-client-version"`
}

func main() {
payload := &UpdateAccountInput{
AccessToken: "rainbow",
ClientVersion: 813,
}
r, _ := httpin.NewRequest("PATCH", "/me", payload)
data, _ := httputil.DumpRequest(r, true)
fmt.Printf("%s\n", data)
}

Output:

PATCH /me HTTP/1.1
X-Myapp-Access-Token: rainbow
X-Myapp-Client-Version: 813