httpz

package module
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: Jan 13, 2025 License: MIT Imports: 16 Imported by: 1

README

Logo

httpz v1.0.0 is release, its API is stable.

简体中文

httpz is a lightweight library built on top of net/http version 1.22. It takes inspiration from Echo's centralized error handling and chi's adherence to the standard library. The problem it aims to solve is that while net/http version 1.22 enhances routing, its functionality is not as user-friendly as other frameworks like Echo and chi.

It functions more like a set of helper functions for net/http rather than a full-fledged web framework. Thanks to net/http handling most of the heavy lifting, httpz has minimal code.

It has the following features:

  1. Centralized error handling

  2. Convenient route grouping, where you can set middleware for each group or for individual routes.

  3. Complete compatibility with the standard library.

Quick Start

1.Installation

To install httpz, Go 1.22 or higher is required.

go get github.com/aeilang/httpz

2.Hello World

import (
	"net/http"

	"github.com/aeilang/httpz"
	"github.com/aeilang/httpz/middleware"
)

func main() {
	// Create a new mux
	mux := httpz.NewServeMux()

	// add logger middleware, it 's copy from chi/middleware
	mux.Use(middleware.Logger)

	// register a GET /hello route
	// GET /hello
	mux.Get("/hello", func(w http.ResponseWriter, r *http.Request) error {
		// rw is a helper responsewriter to send response
		rw := httpz.NewHelperRW(w)
		return rw.String(http.StatusOK, "hello httpz")
		
		// or you can write it by yourself.
		// hw.Header().Set("Content-Type", "text/plain; charset=UTF-8")
		// hw.WriteHeader(http.StatusOK)
		// hw.Write([]byte("hello httpz"))
		// return nil
	})
  
  // just like net/http's ServerMux
	http.ListenAndServe(":8080", mux)
}

the middleware package is copied from chi/middleware.

The complete example can be found in the example directory

3.grouping:

// group return a new *ServeMux base on path "/api/"
api := mux.Group("/api/")

// register GET /well route for api group.
// GET /api/well
api.Get("/well", func(w http.ResponseWriter, r *http.Request) error {	
	rw := httpz.NewHelperRW(w)
	return rw.JSON(http.StatusOK, httpz.Map{
		"data": "well well httpz",
	})
})

4.Centralized error handling

// The parent mux of v2 is api,
// allowing you to group routes infinitely.
v2 := api.Group("/v2/")

// GET /api/v2/hello
v2.Get("/hello", func(w http.ResponseWriter, r *http.Request) error {
	// centralized error handling in tests
	return httpz.NewHTTPError(http.StatusBadRequest, "bad reqeust")
})

// testing path parameters and centrialzed error handling.
// GET /api/v2/well/randomID
v2.Get("/well/{id}", func(w http.ResponseWriter, r *http.Request) error {
	id := r.PathValue("id")

	return httpz.NewHTTPError(http.StatusBadRequest, id)
})

// Get /api/v2/httperr
v2.Get("/httperr", func(w http.ResponseWriter, r *http.Request) error {

	// only *HTTPError will trigger the global error handling.
	// normal error just will just log the msg.
	return errors.New("some error")
})

You can customize the error handling function:

// Create a new mux
mux := httpz.NewServeMux()

mux.ErrHandler = func(err error, w http.ResponseWriter) {
  // for example:
	http.Error(w, err.Error(), http.StatusInternalServerError)
}

The default error handling function is as follows:

// default centrailzed error handling function.
// only the *HTTPError will triger sending error response.
func DefaultErrHandlerFunc(err error, w http.ResponseWriter) {
	if he, ok := err.(*HTTPError); ok {
		rw := NewHelperRW(w)
		rw.JSON(he.StatusCode, Map{"msg": he.Msg})
	} else {
		slog.Error(err.Error())
	}
}

5. Binding

You can also bind path parameters, query parameters, form parameters, and req.Body just like in Echo.

	type User struct {
		Name string `json:"name"`
	}

	// POST /api/v2/user
	v2.Post("/user", func(w http.ResponseWriter, r *http.Request) error {
		var u User
		if err := httpz.Bind(r, &u); err != nil {
			return err
		}

		w.WriteHeader(http.StatusCreated)
		return nil
	})

Feel free to contribute your code.

  • test

  • example

  • middleware

  • other

Documentation

Index

Constants

View Source
const (
	// MIMEApplicationJSON JavaScript Object Notation (JSON) https://www.rfc-editor.org/rfc/rfc8259
	MIMEApplicationJSON                  = "application/json"
	MIMEApplicationJavaScript            = "application/javascript"
	MIMEApplicationJavaScriptCharsetUTF8 = MIMEApplicationJavaScript + "; " + charsetUTF8
	MIMEApplicationXML                   = "application/xml"
	MIMEApplicationXMLCharsetUTF8        = MIMEApplicationXML + "; " + charsetUTF8
	MIMETextXML                          = "text/xml"
	MIMETextXMLCharsetUTF8               = MIMETextXML + "; " + charsetUTF8
	MIMEApplicationForm                  = "application/x-www-form-urlencoded"
	MIMEApplicationProtobuf              = "application/protobuf"
	MIMEApplicationMsgpack               = "application/msgpack"
	MIMETextHTML                         = "text/html"
	MIMETextHTMLCharsetUTF8              = MIMETextHTML + "; " + charsetUTF8
	MIMETextPlain                        = "text/plain"
	MIMETextPlainCharsetUTF8             = MIMETextPlain + "; " + charsetUTF8
	MIMEMultipartForm                    = "multipart/form-data"
	MIMEOctetStream                      = "application/octet-stream"
	MIMETextEventStream                  = "text/event-stream"
	MIMEXNDJSON                          = "application/x-ndjson"
)

MIME types

View Source
const (

	// PROPFIND Method can be used on collection and property resources.
	PROPFIND = "PROPFIND"
	// REPORT Method can be used to get information about a resource, see rfc 3253
	REPORT = "REPORT"
	// RouteNotFound is special method type for routes handling "route not found" (404) cases
	RouteNotFound = "echo_route_not_found"
)
View Source
const (
	HeaderAccept         = "Accept"
	HeaderAcceptEncoding = "Accept-Encoding"
	// HeaderAllow is the name of the "Allow" header field used to list the set of methods
	// advertised as supported by the target resource. Returning an Allow header is mandatory
	// for status 405 (method not found) and useful for the OPTIONS method in responses.
	// See RFC 7231: https://datatracker.ietf.org/doc/html/rfc7231#section-7.4.1
	HeaderAllow               = "Allow"
	HeaderAuthorization       = "Authorization"
	HeaderContentDisposition  = "Content-Disposition"
	HeaderContentEncoding     = "Content-Encoding"
	HeaderContentLength       = "Content-Length"
	HeaderContentType         = "Content-Type"
	HeaderCookie              = "Cookie"
	HeaderSetCookie           = "Set-Cookie"
	HeaderIfModifiedSince     = "If-Modified-Since"
	HeaderLastModified        = "Last-Modified"
	HeaderLocation            = "Location"
	HeaderRetryAfter          = "Retry-After"
	HeaderUpgrade             = "Upgrade"
	HeaderVary                = "Vary"
	HeaderWWWAuthenticate     = "WWW-Authenticate"
	HeaderXForwardedFor       = "X-Forwarded-For"
	HeaderXForwardedProto     = "X-Forwarded-Proto"
	HeaderXForwardedProtocol  = "X-Forwarded-Protocol"
	HeaderXForwardedSsl       = "X-Forwarded-Ssl"
	HeaderXUrlScheme          = "X-Url-Scheme"
	HeaderXHTTPMethodOverride = "X-HTTP-Method-Override"
	HeaderXRealIP             = "X-Real-Ip"
	HeaderXRequestID          = "X-Request-Id"
	HeaderXCorrelationID      = "X-Correlation-Id"
	HeaderXRequestedWith      = "X-Requested-With"
	HeaderServer              = "Server"
	HeaderOrigin              = "Origin"
	HeaderCacheControl        = "Cache-Control"
	HeaderConnection          = "Connection"

	// Access control
	HeaderAccessControlRequestMethod    = "Access-Control-Request-Method"
	HeaderAccessControlRequestHeaders   = "Access-Control-Request-Headers"
	HeaderAccessControlAllowOrigin      = "Access-Control-Allow-Origin"
	HeaderAccessControlAllowMethods     = "Access-Control-Allow-Methods"
	HeaderAccessControlAllowHeaders     = "Access-Control-Allow-Headers"
	HeaderAccessControlAllowCredentials = "Access-Control-Allow-Credentials"
	HeaderAccessControlExposeHeaders    = "Access-Control-Expose-Headers"
	HeaderAccessControlMaxAge           = "Access-Control-Max-Age"

	// Security
	HeaderStrictTransportSecurity         = "Strict-Transport-Security"
	HeaderXContentTypeOptions             = "X-Content-Type-Options"
	HeaderXXSSProtection                  = "X-XSS-Protection"
	HeaderXFrameOptions                   = "X-Frame-Options"
	HeaderContentSecurityPolicy           = "Content-Security-Policy"
	HeaderContentSecurityPolicyReportOnly = "Content-Security-Policy-Report-Only"
	HeaderXCSRFToken                      = "X-CSRF-Token"
	HeaderReferrerPolicy                  = "Referrer-Policy"
)

Headers

Variables

View Source
var (
	ErrBadRequest                    = NewHTTPError(helper(http.StatusBadRequest))                    // HTTP 400 Bad Request
	ErrUnauthorized                  = NewHTTPError(helper(http.StatusUnauthorized))                  // HTTP 401 Unauthorized
	ErrPaymentRequired               = NewHTTPError(helper(http.StatusPaymentRequired))               // HTTP 402 Payment Required
	ErrForbidden                     = NewHTTPError(helper(http.StatusForbidden))                     // HTTP 403 Forbidden
	ErrNotFound                      = NewHTTPError(helper(http.StatusNotFound))                      // HTTP 404 Not Found
	ErrMethodNotAllowed              = NewHTTPError(helper(http.StatusMethodNotAllowed))              // HTTP 405 Method Not Allowed
	ErrNotAcceptable                 = NewHTTPError(helper(http.StatusNotAcceptable))                 // HTTP 406 Not Acceptable
	ErrProxyAuthRequired             = NewHTTPError(helper(http.StatusProxyAuthRequired))             // HTTP 407 Proxy AuthRequired
	ErrRequestTimeout                = NewHTTPError(helper(http.StatusRequestTimeout))                // HTTP 408 Request Timeout
	ErrConflict                      = NewHTTPError(helper(http.StatusConflict))                      // HTTP 409 Conflict
	ErrGone                          = NewHTTPError(helper(http.StatusGone))                          // HTTP 410 Gone
	ErrLengthRequired                = NewHTTPError(helper(http.StatusLengthRequired))                // HTTP 411 Length Required
	ErrPreconditionFailed            = NewHTTPError(helper(http.StatusPreconditionFailed))            // HTTP 412 Precondition Failed
	ErrStatusRequestEntityTooLarge   = NewHTTPError(helper(http.StatusRequestEntityTooLarge))         // HTTP 413 Payload Too Large
	ErrRequestURITooLong             = NewHTTPError(helper(http.StatusRequestURITooLong))             // HTTP 414 URI Too Long
	ErrUnsupportedMediaType          = NewHTTPError(helper(http.StatusUnsupportedMediaType))          // HTTP 415 Unsupported Media Type
	ErrRequestedRangeNotSatisfiable  = NewHTTPError(helper(http.StatusRequestedRangeNotSatisfiable))  // HTTP 416 Range Not Satisfiable
	ErrExpectationFailed             = NewHTTPError(helper(http.StatusExpectationFailed))             // HTTP 417 Expectation Failed
	ErrTeapot                        = NewHTTPError(helper(http.StatusTeapot))                        // HTTP 418 I'm a teapot
	ErrMisdirectedRequest            = NewHTTPError(helper(http.StatusMisdirectedRequest))            // HTTP 421 Misdirected Request
	ErrUnprocessableEntity           = NewHTTPError(helper(http.StatusUnprocessableEntity))           // HTTP 422 Unprocessable Entity
	ErrLocked                        = NewHTTPError(helper(http.StatusLocked))                        // HTTP 423 Locked
	ErrFailedDependency              = NewHTTPError(helper(http.StatusFailedDependency))              // HTTP 424 Failed Dependency
	ErrTooEarly                      = NewHTTPError(helper(http.StatusTooEarly))                      // HTTP 425 Too Early
	ErrUpgradeRequired               = NewHTTPError(helper(http.StatusUpgradeRequired))               // HTTP 426 Upgrade Required
	ErrPreconditionRequired          = NewHTTPError(helper(http.StatusPreconditionRequired))          // HTTP 428 Precondition Required
	ErrTooManyRequests               = NewHTTPError(helper(http.StatusTooManyRequests))               // HTTP 429 Too Many Requests
	ErrRequestHeaderFieldsTooLarge   = NewHTTPError(helper(http.StatusRequestHeaderFieldsTooLarge))   // HTTP 431 Request Header Fields Too Large
	ErrUnavailableForLegalReasons    = NewHTTPError(helper(http.StatusUnavailableForLegalReasons))    // HTTP 451 Unavailable For Legal Reasons
	ErrInternalServerError           = NewHTTPError(helper(http.StatusInternalServerError))           // HTTP 500 Internal Server Error
	ErrNotImplemented                = NewHTTPError(helper(http.StatusNotImplemented))                // HTTP 501 Not Implemented
	ErrBadGateway                    = NewHTTPError(helper(http.StatusBadGateway))                    // HTTP 502 Bad Gateway
	ErrServiceUnavailable            = NewHTTPError(helper(http.StatusServiceUnavailable))            // HTTP 503 Service Unavailable
	ErrGatewayTimeout                = NewHTTPError(helper(http.StatusGatewayTimeout))                // HTTP 504 Gateway Timeout
	ErrHTTPVersionNotSupported       = NewHTTPError(helper(http.StatusHTTPVersionNotSupported))       // HTTP 505 HTTP Version Not Supported
	ErrVariantAlsoNegotiates         = NewHTTPError(helper(http.StatusVariantAlsoNegotiates))         // HTTP 506 Variant Also Negotiates
	ErrInsufficientStorage           = NewHTTPError(helper(http.StatusInsufficientStorage))           // HTTP 507 Insufficient Storage
	ErrLoopDetected                  = NewHTTPError(helper(http.StatusLoopDetected))                  // HTTP 508 Loop Detected
	ErrNotExtended                   = NewHTTPError(helper(http.StatusNotExtended))                   // HTTP 510 Not Extended
	ErrNetworkAuthenticationRequired = NewHTTPError(helper(http.StatusNetworkAuthenticationRequired)) // HTTP 511 Network Authentication Required

	ErrValidatorNotRegistered = errors.New("validator not registered")
	ErrRendererNotRegistered  = errors.New("renderer not registered")
	ErrInvalidRedirectCode    = errors.New("invalid redirect status code")
	ErrCookieNotFound         = errors.New("cookie not found")
	ErrInvalidCertOrKeyType   = errors.New("invalid cert or key type, must be string or []byte")
	ErrInvalidListenerNetwork = errors.New("invalid listener network")
)

Errors

Functions

func Bind added in v1.1.0

func Bind(r *http.Request, i interface{}) (err error)

Bind implements the `Binder#Bind` function. Binding is done in following order: 1) path params; 2) query params; 3) request body. Each step COULD override previous step binded values. For single source binding use their own methods BindBody, BindQueryParams, BindPathParams.

func BindBody added in v1.1.0

func BindBody(req *http.Request, i interface{}) (err error)

BindBody binds request body contents to bindable object NB: then binding forms take note that this implementation uses standard library form parsing which parses form data from BOTH URL and BODY if content type is not MIMEMultipartForm See non-MIMEMultipartForm: https://golang.org/pkg/net/http/#Request.ParseForm See MIMEMultipartForm: https://golang.org/pkg/net/http/#Request.ParseMultipartForm

func BindHeaders added in v1.1.0

func BindHeaders(r *http.Request, i interface{}) error

BindHeaders binds HTTP headers to a bindable object

func BindPathParams added in v1.1.0

func BindPathParams(r *http.Request, i interface{}) error

BindPathParams binds path params to bindable object

func BindQueryParams added in v1.1.0

func BindQueryParams(r *http.Request, i interface{}) error

BindQueryParams binds query params to bindable object

func DefaultErrHandlerFunc

func DefaultErrHandlerFunc(err error, w http.ResponseWriter)

default centrailzed error handling function. only the *HTTPError will triger error response.

Types

type ErrHandlerFunc

type ErrHandlerFunc func(err error, w http.ResponseWriter)

centralized error handling function type.

type HTTPError

type HTTPError struct {
	StatusCode int
	Msg        string
	Internal   error
}

The custom Error type is inspired by Echo.

func NewHTTPError

func NewHTTPError(statusCode int, msg string) *HTTPError

func (*HTTPError) Error

func (e *HTTPError) Error() string

func (*HTTPError) SetInternal added in v1.1.0

func (e *HTTPError) SetInternal(err error) *HTTPError

type HandlerFunc

type HandlerFunc func(w http.ResponseWriter, r *http.Request) error

HandlerFunc defines the function signature for a handler. It returns an error, which is used for centralized error handling.

func Adator

func Adator(fn func(w http.ResponseWriter, r *http.Request)) HandlerFunc

type HelperResponseWriter

type HelperResponseWriter struct {
	http.ResponseWriter
}

A built-in type, used only to record the StatusCode and quickly send responses.

func (*HelperResponseWriter) Flush

func (rw *HelperResponseWriter) Flush()

implement http.Flusher

func (*HelperResponseWriter) HTML

func (rw *HelperResponseWriter) HTML(statusCode int, html string) error

send html

func (*HelperResponseWriter) Hijack

func (rw *HelperResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error)

implement http.Hijacker

func (*HelperResponseWriter) JSON

func (rw *HelperResponseWriter) JSON(statusCode int, data any) error

send json

func (*HelperResponseWriter) Push

func (rw *HelperResponseWriter) Push(target string, opts *http.PushOptions) error

implement http.Pusher

func (*HelperResponseWriter) String

func (rw *HelperResponseWriter) String(statusCode int, s string) error

send string

func (*HelperResponseWriter) Unwrap

get the wrapped ResponseWriter

func (*HelperResponseWriter) XML

func (rw *HelperResponseWriter) XML(statusCode int, data any, indent string) error

send xml

type Map

type Map map[string]any

type MiddlewareFunc

type MiddlewareFunc func(next http.Handler) http.Handler

Middleware function signatrue

type RouteMiddlewareFunc

type RouteMiddlewareFunc func(next HandlerFunc) HandlerFunc

type ServeMux

type ServeMux struct {
	http.ServeMux
	ErrHandlerFunc ErrHandlerFunc
	// contains filtered or unexported fields
}

ServeMux embeds http.ServeMux

func NewServeMux

func NewServeMux() *ServeMux

NewServeMux return a new ServeMux

func (*ServeMux) Connect

func (sm *ServeMux) Connect(path string, h HandlerFunc, m ...RouteMiddlewareFunc)

func (*ServeMux) Delete

func (sm *ServeMux) Delete(path string, h HandlerFunc, m ...RouteMiddlewareFunc)

func (*ServeMux) Get

func (sm *ServeMux) Get(path string, h HandlerFunc, m ...RouteMiddlewareFunc)

Here is the helper function:

func (*ServeMux) Group

func (sm *ServeMux) Group(prefix string) *ServeMux

Group creates a group mux based on a prefix.

func (*ServeMux) HandleFunc

func (sm *ServeMux) HandleFunc(pattern string, h HandlerFunc)

To rewrite the HandleFunc function signature

func (*ServeMux) Head

func (sm *ServeMux) Head(path string, h HandlerFunc, m ...RouteMiddlewareFunc)

func (*ServeMux) Options

func (sm *ServeMux) Options(path string, h HandlerFunc, m ...RouteMiddlewareFunc)

func (*ServeMux) Patch

func (sm *ServeMux) Patch(path string, h HandlerFunc, m ...RouteMiddlewareFunc)

func (*ServeMux) Post

func (sm *ServeMux) Post(path string, h HandlerFunc, m ...RouteMiddlewareFunc)

func (*ServeMux) Put

func (sm *ServeMux) Put(path string, h HandlerFunc, m ...RouteMiddlewareFunc)

func (*ServeMux) ServeHTTP

func (sm *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request)

To rewrite the ServeHTTP function

func (*ServeMux) Trace

func (sm *ServeMux) Trace(path string, h HandlerFunc, m ...RouteMiddlewareFunc)

func (*ServeMux) Use

func (sm *ServeMux) Use(m ...MiddlewareFunc)

Use adds middleware for the mux.

Directories

Path Synopsis
_example
hello_world command

Jump to

Keyboard shortcuts

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