Categorygithub.com/Jille/convreq
modulepackage
1.7.1
Repository: https://github.com/jille/convreq.git
Documentation: pkg.go.dev

# README

convreq

GoDoc Build Status

Experimental project to make writing webservers more convenient.

The core principle of the library is that while the func(w http.ResponseWriter, r *http.Request) signature is very powerful, often a more convenient interface would be preferable.

Example usage

func main() {
	http.Handle("/", convreq.Wrap(homePageHandler))
	srv := &http.Server{
		Addr: ":8080",
	}
	log.Fatal(srv.ListenAndServe())
}

type homePageGet struct {
	Name string
}

type homePagePost struct {
	Password string
}

func homePageHandler(ctx context.Context, r *http.Request, get homePageGet, post *homePagePost) convreq.HttpResponse {
	if get.Name == "" {
		return respond.BadRequest("Who are you?")
	}
	if post != nil && post.Password == "secret" {
		return respond.Redirect(302, "/secrets/")
	}
	t, err := template.New("tpl").Parse("How do you do, {{.}}?")
	if err != nil {
		return respond.Error(err)
	}
	return respond.RenderTemplate(t, get.Name)
}

Signature

I propose a signature that:

  • passes a context directly (as the first parameter)
  • simplifies error handling by allowing a return value
  • passes in type casted input (GET+POST) to the request handler
  • defers responding until the function has returned so later errors can still be handled and you're not caught with errors halfway through your response

So I arrived at func MyHandler(ctx context.Context, r *http.Request, get MyHandlerGet, post *MyHandlerPost) convreq.HttpResponse.

get and post are structs defined by the application that hold input fields. github.com/gorilla/schema is used to decode the GET/POST values into these structs.

get also contains any URL parameters for github.com/gorilla/mux.

Dispatchers

I have implemented two different dispatchers: one with code generation, and one with reflect. Advantages of each:

  • The reflect dispatcher doesn't enforce an argument order. It looks at the types of each parameter and constructs the value required. This will be convenient for e.g. github.com/gorilla/sessions as handlers that require sessions can trivially add a parameter to receive a session.
  • The codegen dispatcher does enforce an argument order, thus promoting consistency.
  • The codegen dispatcher is most likely much faster as it does far less magic at runtime.
  • The codegen dispatcher will catch more problems at compile time. The reflect dispatcher tries its best to catch errors at initialization time, but some might only be noticed at request time.
  • The reflect dispatcher is easier to use, doesn't depend on go-generate or requiring the programmer to know when to regenerate.

Maybe I'll make the codegen dispatcher smarter to also allow for more freedom in method signature.

I'm considering to keep both dispatchers, encourage the reflect dispatcher for development and the codegen dispatcher for prod deployments.

Responding

Rather than allowing the code to respond pieces whenever it wants, I enforce that all responding happens after returning from the request handler. An interface HttpResponse can be returned and one can implement it to do anything you want.

I'll implement a couple of basic responders:

  • Rendering errors easily. Setting the correct HTTP response code and optionally allowing to configure an error page renderer.
  • Template rendering. This should be as easy as return convreq.RenderTemplate(myTemplate, myData). This'll make it possible to automatically reload templates for development servers too.
  • Redirection is as easy as return convreq.Redirect(302, "/home").
  • Setting response headers.

Footer

This is not an officially supported Google product.

# Packages

No description provided by the author
go:generate sh -c "go run ../cmd/generate/generate.go -- example.go > example_generated.go".
Library genapi provides an internal API only to be used by code generated by convreq.
Library respond creates convreq.HttpResponse objects.

# Functions

ContextWithErrorHandler returns a new context within which all errors are rendered with ErrorHandler.
No description provided by the author
WithContextWrapper allows you to replace the context for the request.
WithErrorHandler can be passed on Wrap() to set an ErrorHandler for requests.
WithParameterType makes Wrap() understand an extra type for request handler signatures.
WithReturnType makes Wrap() understand an extra return type for request handler signatures.
Wrap takes a request handler function and returns a http.HandlerFunc for use with net/http.

# Type aliases

ErrorHandler is a callback type that you can register with ContextWithErrorHandler or WithErrorHandler to have your own callback called to render errors.
HttpResponse is what is to be returned from request handlers.
WrapOption can be given to Wrap to modify behavior.