-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #212 from rebuy-de/go-view
add new interface for HTML viewer
- Loading branch information
Showing
4 changed files
with
179 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package webutil | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
|
||
"github.com/pkg/errors" | ||
"github.com/sirupsen/logrus" | ||
) | ||
|
||
func WrapView(fn func(*http.Request) Response) http.HandlerFunc { | ||
return func(w http.ResponseWriter, r *http.Request) { | ||
fn(r)(w, r) | ||
} | ||
} | ||
|
||
func ViewError(status int, err error) http.HandlerFunc { | ||
return func(w http.ResponseWriter, r *http.Request) { | ||
l := logrus. | ||
WithField("stacktrace", fmt.Sprintf("%+v", err)). | ||
WithError(errors.WithStack(err)) | ||
|
||
if errors.Is(err, context.Canceled) { | ||
l.Debugf("request cancelled: %s", err) | ||
|
||
// The code is copied from nginx, where it means that the client | ||
// closed the connection. It is necessary to alter the status code, | ||
// because DataDog will report errors, if the code is >=500, | ||
// regardless of the connection state. | ||
status = 499 | ||
} else if status >= 500 { | ||
l.Errorf("request failed: %s", err) | ||
} else { | ||
l.Warnf("request failed: %s", err) | ||
} | ||
|
||
w.WriteHeader(status) | ||
fmt.Fprint(w, err.Error()) | ||
} | ||
} | ||
|
||
func ViewErrorf(status int, text string, a ...interface{}) http.HandlerFunc { | ||
return ViewError(status, fmt.Errorf(text, a...)) | ||
} | ||
|
||
func ViewRedirect(status int, location string, args ...interface{}) http.HandlerFunc { | ||
return func(w http.ResponseWriter, r *http.Request) { | ||
url := fmt.Sprintf(location, args...) | ||
http.Redirect(w, r, url, status) | ||
} | ||
} | ||
|
||
func ViewJSON(status int, data any) http.HandlerFunc { | ||
return func(w http.ResponseWriter, r *http.Request) { | ||
buf := new(bytes.Buffer) | ||
enc := json.NewEncoder(buf) | ||
enc.SetIndent("", " ") | ||
|
||
err := enc.Encode(data) | ||
if err != nil { | ||
ViewError(http.StatusInternalServerError, err)(w, r) | ||
return | ||
} | ||
|
||
w.Header().Set("Content-Type", "application/json; charset=utf-8") | ||
w.WriteHeader(status) | ||
buf.WriteTo(w) | ||
} | ||
} | ||
|
||
func ViewInlineHTML(status int, data string, a ...any) http.HandlerFunc { | ||
return func(w http.ResponseWriter, r *http.Request) { | ||
w.Header().Set("Content-Type", "text/html; charset=utf-8") | ||
w.WriteHeader(status) | ||
fmt.Fprintf(w, data, a...) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package webutil | ||
|
||
import ( | ||
"bytes" | ||
"html/template" | ||
"io/fs" | ||
"net/http" | ||
|
||
"github.com/sirupsen/logrus" | ||
) | ||
|
||
type GoTemplateViewer struct { | ||
fs fs.FS | ||
funcMaps []TemplateFuncMap | ||
} | ||
|
||
func NewGoTemplateViewer(fs fs.FS, fms ...TemplateFuncMap) *GoTemplateViewer { | ||
return &GoTemplateViewer{ | ||
fs: fs, | ||
funcMaps: fms, | ||
} | ||
} | ||
|
||
func (v *GoTemplateViewer) prepare(filename string, r *http.Request) (*template.Template, error) { | ||
t := template.New(filename) | ||
|
||
for _, fm := range v.funcMaps { | ||
t = t.Funcs(fm(r)) | ||
} | ||
|
||
return t.ParseFS(v.fs, "*") | ||
} | ||
|
||
func (v *GoTemplateViewer) HTML(status int, filename string, data any) http.HandlerFunc { | ||
return func(w http.ResponseWriter, r *http.Request) { | ||
w.Header().Set("Content-Type", "text/html; charset=utf-8") | ||
|
||
t, err := v.prepare(filename, r) | ||
if err != nil { | ||
ViewError(http.StatusInternalServerError, err)(w, r) | ||
return | ||
} | ||
|
||
w.WriteHeader(status) | ||
|
||
err = t.Execute(w, data) | ||
if err != nil { | ||
// It is possible that we already sent the header, but we try again anyways. | ||
w.WriteHeader(http.StatusInternalServerError) | ||
|
||
// We do not send the actual error to the client, since we don't know what we already sent. | ||
logrus.Errorf("failed to render: %v", err.Error()) | ||
} | ||
} | ||
} | ||
|
||
func (v *GoTemplateViewer) Render(filename string, r *http.Request, data any) (*bytes.Buffer, error) { | ||
t, err := v.prepare(filename, r) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
buf := new(bytes.Buffer) | ||
err = t.Execute(buf, data) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return buf, nil | ||
} |