goh

package module
v0.1.12 Latest Latest
Warning

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

Go to latest
Published: Aug 7, 2025 License: Unlicense Imports: 9 Imported by: 0

README

Overview

goh = Go HTTP Handlers.

Utility types that represent a not-yet-sent HTTP response as a value (status, header, body) with no added abstractions. All types implement http.Hander.

Recommended in conjunction with github.com/mitranim/rout: router with support for returning responses as http.Handler.

Small and dependency-free.

See API docs at https://pkg.go.dev/github.com/mitranim/goh.

Usage

import "github.com/mitranim/goh"

type Val struct {
  One int64 `json:"one"`
  Two int64 `json:"two"`
}

type ErrJson struct {
  Error error `json:"error"`
}

// Simple string example.
func handler(req *http.Request) http.Handler {
  return goh.StringOk(`response text`)
}

// String example with status and headers.
func handler(req *http.Request) http.Handler {
  return goh.String{
    Status: http.StatusCreated,
    Header: http.Header{`Content-Type`: {`text/html`}},
    Body:   `<body>response text</body>`,
  }
}

// Simple JSON example.
func handler(req *http.Request) http.Handler {
  return goh.JsonOk(Val{10, 20})
}

// JSON example with custom error handler.
func handler(req *http.Request) http.Handler {
  return goh.Json{
    Body: Val{10, 20},
    ErrFunc: writeErrAsJson,
  }
}

// You can customize the default error handler.
func init() {
  goh.HandleErr = writeErrAsJson
}

// Example custom error handler.
// Should be provided to response types as `ErrFunc: writeErrAsJson`.
func writeErrAsJson(
  rew http.ResponseWriter, req *http.Request, err error, wrote bool,
) {
  if err == nil {
    return
  }

  if !wrote {
    rew.WriteHeader(http.StatusInternalServerError)
    err := json.NewEncoder(rew).Encode(ErrJson{err})
    if err != nil {
      fmt.Fprintf(os.Stderr, "secondary error while writing error response: %+v\n", err)
      return
    }
  }

  fmt.Fprintf(os.Stderr, "error while writing HTTP response: %+v\n", err)
}

Changelog

v0.1.11

Breaking:

  • Renamed .MaybeHan to .HanOpt in various types.

Added:

  • File.ServedHTTP.
  • File.Exists.
  • File.Existing.
  • Dir.ServedHTTP.
  • Dir.Resolve.
  • Dir.Allow.
  • Dir.File.
  • HttpHandlerOpt (implemented by File and Dir).

Fixed:

  • File no longer writes its .Header when the target file is not found.

v0.1.10

Json and Xml now support pretty-printing via field .Indent.

v0.1.9

Cosmetic renaming and minor cleanup. Renamed ErrHandlerDefaultHandleErr, ErrHandlerWriteErr, tweaked argument order in ErrFunc, tweaked default error handling in WriteErr, tweaked error messages.

v0.1.8

Lexicon change: "Res" → "Han" for functions that return http.Handler.

Add TryJsonBytes.

v0.1.7

Added file-serving facilities:

  • File
  • Dir
  • Filter
  • FilterFunc
  • AllowDors

This provides richer file-serving functionality than net/http, including the ability to serve paths from a folder selectively, or being able to "try file" and fall back on something else.

v0.1.6

Json.TryBytes and Xml.TryBytes no longer panic on nil header.

v0.1.5

Added Json.TryBytes and Xml.TryBytes for pre-encoding static responses.

v0.1.4

Added .Res() methods for request → response signatures.

v0.1.3

Added Err, Handler, Respond.

v0.1.2

Redirect no longer writes the HTTP status before invoking http.Redirect.

v0.1.1

Optional support for <?xml?>.

License

https://unlicense.org

Misc

I'm receptive to suggestions. If this library almost satisfies you but needs changes, open an issue or chat me up. Contacts: https://mitranim.com/#contacts

Documentation

Overview

goh = Go Http Handlers.

Utility types that represent a not-yet-sent HTTP response as a value (status, header, body) with NO added abstractions. All types implement `http.Hander`.

See `readme.md` for examples.

Index

Constants

View Source
const (
	HeadType  = `Content-Type`
	TypeJson  = `application/json`
	TypeXml   = `application/xml`
	TypeForm  = `application/x-www-form-urlencoded`
	TypeMulti = `multipart/form-data`
)

Variables

View Source
var HandleErr = WriteErr

Default error handler, used by various `http.Handler` types in this package when no `.ErrFunc` was provided. May be overridden globally.

Functions

func Handler added in v0.1.3

func Handler(fun func() http.Handler) (out http.Handler)

Runs the provided function, returning the resulting `http.Handler`. Catches panics and converts them to a simple error responder via `Err`.

func MutateHeader added in v0.1.12

func MutateHeader(tar, src http.Header)

func Respond added in v0.1.3

func Respond(rew http.ResponseWriter, req *http.Request, fun func() http.Handler)

Shortcut for serving the response generated by the provided function. Catches panics, serving the resulting errors as plain text via `Err`.

func WriteErr added in v0.1.9

func WriteErr(rew http.ResponseWriter, _ *http.Request, err error, wrote bool)

Default implementation of `goh.ErrFunc`. Used by `http.Handler` types, such as `goh.String`, when no `goh.ErrFunc` was provided by user code. If possible, writes the error to the response writer as plain text. If not, logs the error to the standard error stream. When implementing a custom error handler, use this function's source as an example.

Types

type AllowDirs added in v0.1.7

type AllowDirs []string

Implements `goh.Filter` by requiring that the input path is contained within one of the given directories. "Contained" means it begins with the directory path followed by a path separator.

func (AllowDirs) Allow added in v0.1.7

func (self AllowDirs) Allow(val string) bool

Implement `goh.Filter`.

type Bytes

type Bytes struct {
	Status  int
	Header  http.Header
	ErrFunc ErrFunc
	Body    []byte
}

HTTP handler that writes bytes. Note: for sending a string, use `goh.String`, avoiding a bytes-to-string conversion.

func BytesOk

func BytesOk(body []byte) Bytes

Shortcut for `goh.BytesWith(http.StatusOK, body)`.

func BytesWith

func BytesWith(status int, body []byte) Bytes

Shortcut for `goh.Bytes` with specific status and body.

func TryJsonBytes added in v0.1.8

func TryJsonBytes(val interface{}) Bytes

Shortcut for `goh.JsonOk(val).TryBytes()`. Should be used for pre-encoded handlers defined as global variables. Should NOT be used for dynamically-generated responses.

func (Bytes) Han added in v0.1.8

func (self Bytes) Han(*http.Request) http.Handler

Conforms to `goh.Han`.

func (Bytes) ServeHTTP

func (self Bytes) ServeHTTP(rew http.ResponseWriter, req *http.Request)

Implement `http.Handler`.

type Dir added in v0.1.7

type Dir struct {
	Status  int
	Header  http.Header
	ErrFunc ErrFunc
	Path    string
	Filter  Filter
}

HTTP handler that serves files out of a given directory. Similar to `http.FileServer`, but without its undesirable "smarts". This will serve only individual files, without directory listings or redirects. In addition, the method `goh.Dir.HanOpt` supports "try file" functionality, allowing you to fall back on serving something else when a requested file is not found.

The status, header, and err func are copied to each `goh.File` used for each response. Because this uses `goh.File` for each request, it doesn't support automatically adding headers such as `Content-Type`. See the comment on `goh.File`.

func (Dir) Allow added in v0.1.11

func (self Dir) Allow(path string) bool

func (Dir) File added in v0.1.11

func (self Dir) File(path string) File

func (Dir) Han added in v0.1.8

func (self Dir) Han(req *http.Request) http.Handler

Conforms to `goh.Han`. Always returns non-nil.

func (Dir) HanOpt added in v0.1.11

func (self Dir) HanOpt(req *http.Request) http.Handler

Conforms to `goh.Han`. Returns nil if the requested file is not found.

func (Dir) Resolve added in v0.1.11

func (self Dir) Resolve(req *http.Request) File

func (Dir) ServeHTTP added in v0.1.7

func (self Dir) ServeHTTP(rew http.ResponseWriter, req *http.Request)

Implement `http.Handler`.

func (Dir) ServedHTTP added in v0.1.11

func (self Dir) ServedHTTP(rew http.ResponseWriter, req *http.Request) bool

Implement `HttpHandlerOpt`. If possible, serves the requested file and returns true. Otherwise returns false.

type ErrFunc

type ErrFunc = func(rew http.ResponseWriter, req *http.Request, err error, wrote bool)

Signature of an error handler function provided by user code to the various `http.Handler` types in this library, such as `String`. When nil, `goh.HandleErr` is used.

The `wrote` parameter indicates whether the response writer has been written to. If `false`, the handler should write an error response. If `true`, or if sending the error response has failed, the handler should log the resulting error to the server log.

type File added in v0.1.7

type File struct {
	Status  int
	Header  http.Header
	ErrFunc ErrFunc
	Path    string
}

HTTP handler that always serves a file at a specific FS path. For each request, it verifies that the file exists and delegates to `http.ServeFile`. If the file doesn't exist, this responds with 404 without calling `http.ServeFile`, avoiding its undesirable "smarts".

Unlike `http.ServeFile` and `http.FileServer`, this does not automatically add headers such as `Content-Type`, `Last-Modified`, `Etag`, and so on. This tool is intended mostly for development or as a lower-level building block. For serving files in production, you're expected to use a dedicated file server, or a higher-level tool.

Unlike `http.ServeFile` and `http.FileServer`, responding with 404 is optional. `goh.File.HanOpt` returns a nil handler if the file is not found. You can use this to "try" serving a file, and fall back on something else.

func (File) Existing added in v0.1.11

func (self File) Existing() (_ File)

If `.Exists()`, returns itself as-is. Otherwise returns zero. Example usage: `File{...}.Existing().Path`.

func (File) Exists added in v0.1.11

func (self File) Exists() bool

True if a file exists at `.Path`.

func (File) Han added in v0.1.8

func (self File) Han(*http.Request) http.Handler

Conforms to `goh.Han`. Always returns non-nil.

func (File) HanOpt added in v0.1.11

func (self File) HanOpt(*http.Request) http.Handler

Conforms to `goh.Han`. Returns self if file exists, otherwise returns nil. Can be used to "try" serving a file.

func (File) ServeHTTP added in v0.1.7

func (self File) ServeHTTP(rew http.ResponseWriter, req *http.Request)

Implement `http.Handler`.

func (File) ServedHTTP added in v0.1.11

func (self File) ServedHTTP(rew http.ResponseWriter, req *http.Request) bool

Implement `HttpHandlerOpt`. If `.Exists()`, uses `.ServeHTTP` to serve the file and returns true. Otherwise returns false.

type Filter added in v0.1.7

type Filter interface {
	Allow(string) bool
}

Used by `goh.Dir` to allow or deny serving specific paths. The input to `.Allow` is a normalized filesystem path that uses Unix-style forward slashes on both Unix and Windows. The path starts with `goh.Dir.Path`. For example:

dir := goh.Dir{Path: `static`}
req := &http.Request{URL: &url.URL{Path: `/some_file`}}
dir.Han(req)
->
dir.Filter.Allow(`static/some_file`)

type FilterFunc added in v0.1.7

type FilterFunc func(string) bool

Function type that implements `goh.Filter`. Example usage:

goh.Dir{Path: `.`, Filter: goh.FilterFunc(regexp.MustCompile(`^status/`))}

func (FilterFunc) Allow added in v0.1.7

func (self FilterFunc) Allow(val string) bool

Implement `goh.Filter` by calling itself.

type Han added in v0.1.8

type Han = func(*http.Request) http.Handler

Signature of a "request->response" function. All Goh handler types have a method `.Han` that conforms to this signature.

type HttpHandlerOpt added in v0.1.11

type HttpHandlerOpt interface {
	ServedHTTP(http.ResponseWriter, *http.Request) bool
}

Variant of `http.Handler` that may or may not serve the request. If capable of serving the request, must serve it and return true. Otherwise, must return false without any side effects in the given response writer and request. This interface is implemented by some types in this module.

type Json

type Json struct {
	Status  int
	Header  http.Header
	ErrFunc ErrFunc
	Indent  string
	Body    interface{}
}

HTTP handler that automatically sets the appropriate JSON headers and encodes its body as JSON. The field `.Indent` is passed to the JSON encoder.

func JsonOk

func JsonOk(body interface{}) Json

Shortcut for `goh.JsonWith(http.StatusOK, body)`.

func JsonWith

func JsonWith(status int, body interface{}) Json

Shortcut for `goh.Json` with specific status and body.

func (Json) Han added in v0.1.8

func (self Json) Han(*http.Request) http.Handler

Conforms to `goh.Han`.

func (Json) ServeHTTP

func (self Json) ServeHTTP(rew http.ResponseWriter, req *http.Request)

Implement `http.Handler`.

func (Json) TryBytes added in v0.1.5

func (self Json) TryBytes() Bytes

Converts to `goh.Bytes` by encoding the body and adding the appropriate content type header. Panics on encoding errors. Should be used in root scope to pre-encode a static response:

import "github.com/mitranim/goh"

var someHan = goh.JsonOk(someValue).TryBytes()

type NotFound added in v0.1.7

type NotFound struct{}

Zero-sized handler that returns with 404 without any additional headers or body content. Used internally by `goh.File`.

func (NotFound) Han added in v0.1.8

func (self NotFound) Han(req *http.Request) http.Handler

Conforms to `goh.Han`, returning self.

func (NotFound) ServeHTTP added in v0.1.7

func (NotFound) ServeHTTP(rew http.ResponseWriter, _ *http.Request)

type Reader

type Reader struct {
	Status  int
	Header  http.Header
	ErrFunc ErrFunc
	Body    io.Reader
}

HTTP handler that copies a response from a reader.

Caution: if the reader is also `io.Closer`, it must be closed in your code. This type does NOT attempt that.

func (Reader) Han added in v0.1.8

func (self Reader) Han(*http.Request) http.Handler

Conforms to `goh.Han`.

func (Reader) ServeHTTP

func (self Reader) ServeHTTP(rew http.ResponseWriter, req *http.Request)

Implement `http.Handler`.

type Redirect

type Redirect struct {
	Status  int
	Header  http.Header
	ErrFunc ErrFunc
	Link    string
}

HTTP handler that performs an HTTP redirect.

func RedirectWith

func RedirectWith(status int, link string) Redirect

Shortcut for `goh.Redirect` with specific status and body.

func (Redirect) Han added in v0.1.8

func (self Redirect) Han(*http.Request) http.Handler

Conforms to `goh.Han`.

func (Redirect) ServeHTTP

func (self Redirect) ServeHTTP(rew http.ResponseWriter, req *http.Request)

Implement `http.Handler`.

type String

type String struct {
	Status  int
	Header  http.Header
	ErrFunc ErrFunc
	Body    string
}

HTTP handler that writes a string. Note: for sending bytes, use `goh.Bytes`, avoiding a string-to-bytes conversion.

func Err added in v0.1.3

func Err(err error) String

Makes an extremely simple `http.Handler` that serves the error's message as plain text. The status is always 500.

func StringOk

func StringOk(body string) String

Shortcut for `goh.StringWith(http.StatusOK, body)`.

func StringWith

func StringWith(status int, body string) String

Shortcut for `goh.String` with specific status and body.

func (String) Han added in v0.1.8

func (self String) Han(*http.Request) http.Handler

Conforms to `goh.Han`.

func (String) ServeHTTP

func (self String) ServeHTTP(rew http.ResponseWriter, req *http.Request)

Implement `http.Handler`.

type Xml

type Xml Json

HTTP handler that automatically sets the appropriate XML headers and encodes its body as XML. The field `.Indent` is passed to the JSON encoder.

Caution: this does NOT prepend the processing instruction `<?xml?>`. When you don't need to specify the encoding, this instruction is entirely skippable. When you need to specify the encoding, wrap `.Body` in the utility type `goh.XmlDoc` provided by this package.

func XmlOk

func XmlOk(body interface{}) Xml

Shortcut for `goh.XmlWith(http.StatusOK, body)`.

func XmlWith

func XmlWith(status int, body interface{}) Xml

Shortcut for `goh.Xml` with specific status and body.

func (Xml) Han added in v0.1.8

func (self Xml) Han(*http.Request) http.Handler

Conforms to `goh.Han`.

func (Xml) ServeHTTP

func (self Xml) ServeHTTP(rew http.ResponseWriter, req *http.Request)

Implement `http.Handler`.

func (Xml) TryBytes added in v0.1.5

func (self Xml) TryBytes() Bytes

Converts to `goh.Bytes` by encoding the body and adding the appropriate content type header. Panics on encoding errors. Should be used in root scope to pre-encode a static response:

import "github.com/mitranim/goh"

var someHan = goh.XmlOk(someValue).TryBytes()

type XmlDoc added in v0.1.1

type XmlDoc struct {
	Encoding string
	Val      interface{}
}

Utility type for use together with `goh.Xml`. When encoded as XML, this prepends the `<?xml?>` header with version 1.0 and the specified encoding, if any. Example usage:

myXmlDoc := SomeType{SomeField: someValue}

res := goh.XmlOk(goh.XmlDoc{
	Encoding: "utf-8",
	Val: myXmlDoc,
})

Eventual output:

<?xml version="1.0" encoding="utf-8"?>
<SomeType ...>

func (XmlDoc) MarshalXML added in v0.1.1

func (self XmlDoc) MarshalXML(enc *xml.Encoder, _ xml.StartElement) error

Implement `encoding/xml.Marshaler`, prepending the `<?xml?>` processing instruction, with the specified encoding if available.

Jump to

Keyboard shortcuts

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