Added custom renderers

This commit is contained in:
Alexander Kiryukhin 2022-01-09 08:03:03 +03:00
parent b3c48f8067
commit 6d99712a30
No known key found for this signature in database
GPG key ID: 6DF7A2910D0699E9
8 changed files with 130 additions and 12 deletions

View file

@ -1,4 +1,5 @@
# API
Generic api functions
## Usage

View file

@ -1,5 +1,5 @@
### Request JSON:
http://localhost:3000/hello
POST http://localhost:3000/hello
Content-Type: application/json
{
@ -12,7 +12,7 @@ Content-Type: application/json
#{"message":"Hello, Alex!"}
### Request Form:
http://localhost:3000/hello
POST http://localhost:3000/hello
Content-Type: application/x-www-form-urlencoded
name=Alex

54
_examples/html/main.go Normal file
View file

@ -0,0 +1,54 @@
package main
import (
"bytes"
"context"
"html/template"
"log"
"net/http"
"github.com/gogeneric/api"
)
var tpl *template.Template
func main() {
h := &http.Server{Addr: "0.0.0.0:3000"}
mux := http.NewServeMux()
h.Handler = mux
var err error
tpl, err = template.ParseGlob("./*.gohtml")
if err != nil {
log.Fatalln(err)
}
// Here is magic!
mux.Handle("/hello", api.Wrap(handleHello))
if err := h.ListenAndServe(); err != http.ErrServerClosed {
log.Fatalln(err)
}
}
func handleHello(ctx context.Context, req *helloRequest) (*helloResponse, error) {
return &helloResponse{
Message: "Hello, " + req.Name,
template: "tpl.gohtml",
}, nil
}
type helloRequest struct {
Name string `json:"name"`
}
type helloResponse struct {
template string
Message string
}
func (r *helloResponse) Render() ([]byte, error) {
buf := bytes.NewBuffer([]byte{})
if err := tpl.ExecuteTemplate(buf, r.template, r); err != nil {
return nil, err
}
return buf.Bytes(), nil
}

13
_examples/html/test.http Normal file
View file

@ -0,0 +1,13 @@
### Request HTML:
POST http://localhost:3000/hello
Content-Type: application/json
{
"name": "Alex"
}
### Response:
# http://localhost:3000/hello
#
#{"message":"Hello, Alex!"}

View file

@ -0,0 +1,5 @@
<html>
<body>
<h1>Message: {{.Message}}</h1>
</body>
</html>

View file

@ -1,5 +1,5 @@
### Request JSON:
http://localhost:3000/hello
POST http://localhost:3000/hello
Content-Type: application/json
{
@ -12,7 +12,7 @@ Content-Type: application/json
#{"message":"Hello, Alex!"}
### Request Form:
http://localhost:3000/hello
POST http://localhost:3000/hello
Content-Type: application/x-www-form-urlencoded
name=Alex

32
contracts.go Normal file
View file

@ -0,0 +1,32 @@
package api
import "net/http"
// Optional interfaces for request type
//WithHeader sets headers to request
type WithHeader interface {
WithHeader(header http.Header)
}
//WithMethod sets method to request
type WithMethod interface {
WithMethod(method string)
}
// Optional interfaces for response type
//Renderer renders response to byte slice
type Renderer interface {
Render() ([]byte, error)
}
//WithContentType returns custom content type for response
type WithContentType interface {
ContentType() string
}
//WithHTTPStatus returns custom status code
type WithHTTPStatus interface {
Status() int
}

29
wrap.go
View file

@ -25,11 +25,30 @@ func Wrap[RQ any, RS any](handler func(ctx context.Context, request *RQ) (RS, er
_, _ = w.Write([]byte(err.Error()))
return
}
if err := json.NewEncoder(w).Encode(resp); err != nil {
statusCode := http.StatusOK
contentType := "application/json"
var body []byte
if v, ok := (any)(resp).(WithContentType); ok {
contentType = v.ContentType()
}
if v, ok := (any)(resp).(WithHTTPStatus); ok {
statusCode = v.Status()
}
if v, ok := (any)(resp).(Renderer); ok {
body, err = v.Render()
} else {
body, err = json.Marshal(resp)
}
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(err.Error()))
return
}
w.WriteHeader(statusCode)
w.Header().Set("Content-Type", contentType)
w.Write(body)
}
}
@ -42,10 +61,4 @@ func richifyRequest[RQ any](req *RQ, baseRequest *http.Request) {
}
}
type WithHeader interface {
WithHeader(header http.Header)
}
type WithMethod interface {
WithMethod(method string)
}
type NilRequest struct{}