response

package
v0.4.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 17, 2026 License: MIT Imports: 2 Imported by: 0

README

http/response

简体中文

JSON writers and optional error handlers. Import github.com/Ithildur/EiluneKit/http/response.

Error handlers

Constructor Status Code Message
NotFound() 404 not_found resource not found
MethodNotAllowed() 405 method_not_allowed method not allowed
Unauthorized() 401 unauthorized authentication required
InternalServerError() 500 internal_error internal server error

Each constructor returns a standard http.HandlerFunc, writing ErrorResponse with Content-Type: application/json; charset=utf-8. Handlers do not select paths or install themselves. The router owns Allow; authentication-specific headers belong to the application.

Inject the presets when building an API handler:

handler, err := routes.NewHandler(api.RoutesAt("/api"), routes.HandlerOptions{
    NotFound:         response.NotFound(),
    MethodNotAllowed: response.MethodNotAllowed(),
    Unauthorized:     response.Unauthorized(),
    Middleware: []routes.Middleware{
        middleware.Recover(middleware.RecoverOptions{
            Logger:  logger,
            OnPanic: response.InternalServerError(),
        }),
    },
})
if err != nil {
    return err
}

Here api is the application's Blueprint and logger is its *slog.Logger. NewHandler sets Allow before the 405 handler. Recover only invokes OnPanic before a response starts. Injecting presets does not change defaults on other handlers.

When sharing a server with a SPA, the application must select which paths receive JSON errors; see SPA fallback.

Override a preset

Replace the corresponding injected handler. The remaining presets can stay in place:

func unauthorized(w http.ResponseWriter, r *http.Request) {
    response.WriteJSONError(w, http.StatusUnauthorized,
        "session_expired", "please sign in again")
}

handler, err := routes.NewHandler(api.RoutesAt("/api"), routes.HandlerOptions{
    NotFound:         response.NotFound(),
    MethodNotAllowed: response.MethodNotAllowed(),
    Unauthorized:     http.HandlerFunc(unauthorized),
})
if err != nil {
    return err
}

This replacement belongs to this handler instance. It does not redefine a Kit function or mutate global configuration. Use WriteJSON with an application struct if the response shape also needs to change.

Add a business error handler

Define an ordinary handler and attach it at the relevant application boundary. For example, an export quota response can be used as a rate-limit callback:

func quotaExceeded(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Retry-After", "60")
    response.WriteJSONError(w, http.StatusTooManyRequests,
        "export_quota_exceeded", "try again in one minute")
}

exports := routes.NewBlueprint(routes.DefaultMiddleware(
    middleware.RateLimit(middleware.RateLimitOptions{
        Requests: 10,
        Window:   time.Minute,
        OnLimit:  quotaExceeded,
    }),
))
exports.Post("/exports", "Create export", createExport)

createExport is the application's endpoint. No registration with the response package is needed. A business handler can also call quotaExceeded(w, r) directly and return.

The executable examples exercise presets, a custom 401 response, and a new quota handler. Run them with go test ./http/response -run Example -v.

Documentation

Overview

Package response provides JSON response helpers. Package response 提供 JSON 响应辅助函数。

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func InternalServerError added in v0.4.2

func InternalServerError() http.HandlerFunc

InternalServerError returns a JSON 500 handler without internal error details. InternalServerError 返回不含内部错误详情的 JSON 500 handler。

func MethodNotAllowed added in v0.4.2

func MethodNotAllowed() http.HandlerFunc

MethodNotAllowed returns a JSON 405 handler. The router must set Allow. MethodNotAllowed 返回 JSON 405 handler;Allow 必须由路由器设置。

func NotFound added in v0.4.2

func NotFound() http.HandlerFunc

NotFound returns a JSON 404 handler for any request path. NotFound 返回适用于任意请求路径的 JSON 404 handler。

Example
package main

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

	"github.com/Ithildur/EiluneKit/http/middleware"
	"github.com/Ithildur/EiluneKit/http/response"
	"github.com/Ithildur/EiluneKit/http/routes"
)

func main() {
	api := routes.NewBlueprint()
	endpoint := func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) }
	api.Get("/items", "", endpoint)
	api.Get("/private", "", endpoint, routes.Auth(routes.AuthRequired))
	api.Get("/panic", "", func(http.ResponseWriter, *http.Request) { panic("private details") })
	h, err := routes.NewHandler(api.Routes(), routes.HandlerOptions{
		NotFound:         response.NotFound(),
		MethodNotAllowed: response.MethodNotAllowed(),
		Unauthorized:     response.Unauthorized(),
		Middleware: []routes.Middleware{
			middleware.Recover(middleware.RecoverOptions{
				Logger:  slog.New(slog.NewTextHandler(io.Discard, nil)),
				OnPanic: response.InternalServerError(),
			}),
		},
	})
	if err != nil {
		panic(err)
	}
	for _, request := range []struct{ method, path string }{
		{"GET", "/missing"}, {"POST", "/items"}, {"GET", "/private"}, {"GET", "/panic"},
	} {
		w := httptest.NewRecorder()
		h.ServeHTTP(w, httptest.NewRequest(request.method, request.path, nil))
		fmt.Println(w.Code, w.Header().Get("Content-Type"), w.Body.String())
		if w.Code == http.StatusMethodNotAllowed {
			fmt.Println("Allow:", w.Header().Get("Allow"))
		}
	}
}
Output:
404 application/json; charset=utf-8 {"code":"not_found","message":"resource not found"}
405 application/json; charset=utf-8 {"code":"method_not_allowed","message":"method not allowed"}
Allow: GET
401 application/json; charset=utf-8 {"code":"unauthorized","message":"authentication required"}
500 application/json; charset=utf-8 {"code":"internal_error","message":"internal server error"}

func Unauthorized added in v0.4.2

func Unauthorized() http.HandlerFunc

Unauthorized returns a JSON 401 handler. Authentication headers belong to the application. Unauthorized 返回 JSON 401 handler;认证响应头由应用设置。

Example (Custom)
package main

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

	"github.com/Ithildur/EiluneKit/http/response"
	"github.com/Ithildur/EiluneKit/http/routes"
)

func main() {
	api := routes.NewBlueprint(routes.DefaultAuth(routes.AuthRequired))
	api.Get("/account", "", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) })
	h, err := routes.NewHandler(api.Routes(), routes.HandlerOptions{
		NotFound: response.NotFound(),
		Unauthorized: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			response.WriteJSONError(w, http.StatusUnauthorized, "session_expired", "please sign in again")
		}),
	})
	if err != nil {
		panic(err)
	}
	w := httptest.NewRecorder()
	h.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/account", nil))
	fmt.Println(w.Code, w.Body.String())
}
Output:
401 {"code":"session_expired","message":"please sign in again"}

func WriteJSON

func WriteJSON(w http.ResponseWriter, status int, v any)

WriteJSON writes v as JSON with status. Call WriteJSON(w, status, value). WriteJSON 以 JSON 写入 value 和 status。 调用 WriteJSON(w, status, value)。

Example / 示例:

response.WriteJSON(w, http.StatusOK, map[string]any{"ok": true})

func WriteJSONError

func WriteJSONError(w http.ResponseWriter, status int, code, msg string)

WriteJSONError writes ErrorResponse as JSON. Call WriteJSONError(w, status, code, message). WriteJSONError 以 JSON 写入 ErrorResponse。 调用 WriteJSONError(w, status, code, message)。

Example / 示例:

response.WriteJSONError(w, http.StatusBadRequest, "invalid_json", "invalid json")
Example (CustomHandler)
package main

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

	"github.com/Ithildur/EiluneKit/http/middleware"
	"github.com/Ithildur/EiluneKit/http/response"
)

func quotaExceeded(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Retry-After", "60")
	response.WriteJSONError(w, http.StatusTooManyRequests, "export_quota_exceeded", "try again in one minute")
}

func main() {
	h := middleware.RateLimit(middleware.RateLimitOptions{
		Requests: 1,
		Window:   time.Minute,
		OnLimit:  quotaExceeded,
	})(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusNoContent)
	}))
	for range 2 {
		w := httptest.NewRecorder()
		h.ServeHTTP(w, httptest.NewRequest(http.MethodPost, "/exports", nil))
		fmt.Println(w.Code)
		if w.Code == http.StatusTooManyRequests {
			fmt.Println("Retry-After:", w.Header().Get("Retry-After"))
			fmt.Println(w.Body.String())
		}
	}
}
Output:
204
429
Retry-After: 60
{"code":"export_quota_exceeded","message":"try again in one minute"}

Types

type ErrorResponse

type ErrorResponse struct {
	Code    string `json:"code"`
	Message string `json:"message"`
}

ErrorResponse is the standard JSON error payload. ErrorResponse 是标准 JSON 错误响应体。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL