web

package module
v1.6.0 Latest Latest
Warning

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

Go to latest
Published: May 11, 2021 License: MIT Imports: 14 Imported by: 0

README

goioc/web: Web Framework for Go, based on goioc/di

goioc

Go go.dev reference CodeFactor Go Report Card codecov Quality Gate Status DeepSource

How is this framework different from others?

  1. First of all, goioc/web is working using Dependency Injection and is based on goioc/di, which is the IoC Container.
  2. Secondly - and this is the most exciting part - web-endpoints in goioc/web can have (almost) arbitrary signature! No more func(w http.ResponseWriter, r *http.Request) handlers, if your endpoint receives a string and produces a binary stream, just declare it as is:
...
func (e *endpoint) Hello(name string) io.Reader {
	return bytes.NewBufferString("Hello, " + name + "!")
}
...

Cool, huh? 🤠 Of course, you can still directly use http.ResponseWriter and *http.Request, if you like.

Basic concepts

The main entity in goioc/web is the Endpoint, which is represented by the interface of the same name. Here's the example implementation:

type endpoint struct {
}

func (e endpoint) HandlerFuncName() string {
	return "Hello"
}

func (e *endpoint) Hello(name string) io.Reader {
	return bytes.NewBufferString("Hello, " + name + "!")
}

Endpoint interface has one method that returns the name of the method that will be used as an endpoint.

In order for goioc/web to pick up this endpoint, it should be registered in the DI Container:

_, _ = di.RegisterBean("endpoint", reflect.TypeOf((*endpoint)(nil)))

Then the container should be initialized (please, refer to the goioc/di documentation for more details):

_ = di.InitializeContainer()

Finally, the web-server can be started, either using the built-in function:

_ = web.ListenAndServe(":8080")

... or using returned Router

router, _ := web.CreateRouter()
_ = http.ListenAndServe(":8080", router)

Routing

So, how does the framework know where to bind this endpoint to? For the routing functionality goioc/web leverages gorilla/mux library. Don't worry: you don't have to cope with this library directly: goioc/web provides a set of convenient wrappers around it. The wrappers are implemented as tags in the endpoint-structure. Let's slightly update our previous example:

...
type endpoint struct {
	method interface{} `web.methods:"GET"`
	path   interface{} `web.path:"/hello"`
}
...

Now our endpoint is bound to a GET requests at the /hello path. Yes, it's that simple! 🙂

goioc/web tags
Tag Value Example
web.methods List of HTTP-methods. web.methods:"POST,PATCH"
web.path URL sub-path. Can contain path variables. web.path:"/articles/{category}/{id:[0-9]+}"
web.queries Key-value paris of the URL query part. web.queries:"foo,bar,id,{id:[0-9]+}"
web.headers Key-value paris of the request headers. web.headers:"Content-Type,application/octet-stream"
web.matcher ID of the bean of type *mux.MatcherFunc. web.matcher:"matcher"

In and Out types

As was mentioned above, with goioc/web you get a lot of freedom in terms of defining the signature of your endpoint's method. Just look at these examples:

...
func (e *endpoint) Error() (int, string) {
	return 505, "Something bad happened :("
}
...
...
func (e *endpoint) KeyValue(ctx context.Context) string {
	return ctx.Value(di.BeanKey("key")).(string)
}
...
...
func (e *endpoint) Hello(pathParams map[string]string) (http.Header, int) {
	return map[string][]string{
    		"Content-Type": {"application/octet-stream"},
    	}, []byte("Hello, " + pathParams["name"] + "!")
}
...
Supported argument types
  • http.ResponseWriter
  • *http.Request
  • context.Context
  • http.Header
  • io.Reader
  • io.ReadCloser
  • []byte
  • string
  • map[string]string
  • url.Values
  • struct implementing encoding.BinaryUnmarshaler or encoding.TextUnmarshaler
  • interface{} (GoiocSerializer bean is used to deserialize such arguments)
Supported return types
  • http.Header (response headers, must be first return argument, if used)
  • int (status code, must be first argument after response headers, if used)
  • io.Reader
  • io.ReadCloser
  • []byte
  • string
  • struct implementing encoding.BinaryMarshaler or encoding.TextMarshaler
  • interface{} (GoiocSerializer bean is used to serialize such returned object)
Templates

goioc/web supports templates!

todo.html

<h1>{{.PageTitle}}</h1>
<ul>
    {{range .Todos}}
        {{if .Done}}
            <li class="done">{{.Title}}</li>
        {{else}}
            <li>{{.Title}}</li>
        {{end}}
    {{end}}
</ul>

endpoint.go

type todo struct {
	Title string
	Done  bool
}
type todoPageData struct {
	PageTitle string
	Todos     []todo
}

type todoEndpoint struct {
	method interface{} `web.methods:"GET"`
	path   interface{} `web.path:"/todo"`
}

func (e todoEndpoint) HandlerFuncName() string {
	return "TodoList"
}

func (e *todoEndpoint) TodoList() (template.Template, interface{}) {
	tmpl := template.Must(template.ParseFiles("todo.html"))
	return *tmpl, todoPageData{
		PageTitle: "My TODO list",
		Todos: []todo{
			{Title: "Task 1", Done: false},
			{Title: "Task 2", Done: true},
			{Title: "Task 3", Done: true},
		},
	}
}

Note that in case of using templates, the next returned object after template.Template must be the actual structure that will be used to fill in the template 💡

Custom matchers

If functionality of web.methods, web.path, web.queries and web.headers is not enough for you, you can use custom matcher, based on Gorilla's mux.MatcherFunc:

...
_, _ = di.RegisterBeanFactory("matcher", di.Singleton, func(context.Context) (interface{}, error) {
		matcherFunc := mux.MatcherFunc(func(request *http.Request, match *mux.RouteMatch) bool {
			return strings.HasSuffix(request.URL.Path, "bar")
		})
		return &matcherFunc, nil
	})

...

type endpoint struct {
	method  interface{} `web.methods:"GET"`
	path    interface{} `web.path:"/endpoint/{key}/{*?}"`
	matcher interface{} `web.matcher:"matcher"`
}

func (e endpoint) HandlerFuncName() string {
	return "Match"
}

func (e *endpoint) Match() string {
	return "It's a match! :)"
}
...
$ curl localhost:8080/endpoint/foo/bar
It's a match! :)                

Middleware

Of course, custom middleware is also supported by the framework:

web.Use(func(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), di.BeanKey("key"), "value")))
	})
})

Documentation

Index

Constants

View Source
const GoiocSerializer = "goiocSerializer"

GoiocSerializer is an ID for Serializer bean. By default, points to JsonSerializer, but can be overwritten.

Variables

This section is empty.

Functions

func CreateRouter

func CreateRouter() (*mux.Router, error)

CreateRouter function creates *mux.Router (which implements http.Handler interface).

func ListenAndServe

func ListenAndServe(addr string) error

ListenAndServe function wraps http.ListenAndServe(...), automatically creating endpoints from registered beans.

func ListenAndServeTLS

func ListenAndServeTLS(addr, certFile, keyFile string) error

ListenAndServeTLS function wraps http.ListenAndServeTLS(...), automatically creating endpoints from registered beans.

func Use

func Use(middlewareFunctions ...mux.MiddlewareFunc)

Use function registers middleware.

Types

type Endpoint

type Endpoint interface {
	// HandlerFuncName should return a method name that is going to be used to create http handler.
	HandlerFuncName() string
}

Endpoint is an interface representing web endpoint.

type JsonSerializer

type JsonSerializer struct {
}

JsonSerializer is a default implementation of Serializer interface.

func (JsonSerializer) Deserialize

func (js JsonSerializer) Deserialize(data []byte, v interface{}) error

Deserialize method deserializes object from JSON.

func (JsonSerializer) Serialize

func (js JsonSerializer) Serialize(v interface{}) ([]byte, error)

Serialize method serializes object to JSON.

type Serializer

type Serializer interface {
	// Serialize method serializes object to byte array.
	Serialize(interface{}) ([]byte, error)
	// Deserialize method deserializes object from byte array.
	Deserialize([]byte, interface{}) error
}

Serializer interface is used by web library to serialize/deserialize objects. Default implementation: JsonSerializer.

Jump to

Keyboard shortcuts

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