Added custom renderers
This commit is contained in:
parent
b3c48f8067
commit
6d99712a30
8 changed files with 130 additions and 12 deletions
|
@ -1,4 +1,5 @@
|
|||
# API
|
||||
|
||||
Generic api functions
|
||||
|
||||
## Usage
|
||||
|
|
|
@ -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
54
_examples/html/main.go
Normal 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
13
_examples/html/test.http
Normal 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!"}
|
||||
|
5
_examples/html/tpl.gohtml
Normal file
5
_examples/html/tpl.gohtml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<html>
|
||||
<body>
|
||||
<h1>Message: {{.Message}}</h1>
|
||||
</body>
|
||||
</html>
|
|
@ -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
32
contracts.go
Normal 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
29
wrap.go
|
@ -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{}
|
||||
|
|
Loading…
Reference in a new issue