diff --git a/web/api_handler.go b/web/api_handler.go new file mode 100644 index 0000000..f635f2d --- /dev/null +++ b/web/api_handler.go @@ -0,0 +1,69 @@ +package web + +import ( + "encoding/json" + + "github.com/jimeh/ozu.io/shortener" + "github.com/qiangxue/fasthttp-routing" + "github.com/valyala/fasthttp" +) + +// NewAPIHandler creates a new Handler object. +func NewAPIHandler(s shortener.Shortener) *APIHandler { + return &APIHandler{s} +} + +// APIHandler handle HTTP requests. +type APIHandler struct { + shortener shortener.Shortener +} + +// Shorten shortens given URL. +func (h *APIHandler) Shorten(c *routing.Context) error { + uid, url, err := h.shortener.Shorten(c.FormValue("url")) + if err != nil { + return h.respondWithError(c, err) + } + + r := makeURLResponse(c, uid, url) + return h.respond(c, &r) +} + +// Lookup shortened UID. +func (h *APIHandler) Lookup(c *routing.Context) error { + uid := c.FormValue("uid") + url, err := h.shortener.Lookup(uid) + if err != nil { + return h.respondWithError(c, err) + } + + r := makeURLResponse(c, uid, url) + return h.respond(c, &r) +} + +func (h *APIHandler) respond(c *routing.Context, r *URLResponse) error { + resp, err := json.Marshal(r) + if err != nil { + return err + } + + c.SetContentType("application/json") + c.Write(resp) + return nil +} + +func (h *APIHandler) respondWithError(c *routing.Context, err error) error { + r := ErrorResponse{ + Error: err.Error(), + } + + resp, err := json.Marshal(r) + if err != nil { + return err + } + + c.SetStatusCode(fasthttp.StatusNotFound) + c.SetContentType("application/json") + c.Write(resp) + return nil +} diff --git a/web/responses.go b/web/api_responses.go similarity index 73% rename from web/responses.go rename to web/api_responses.go index 6e8e85c..6460edc 100644 --- a/web/responses.go +++ b/web/api_responses.go @@ -1,6 +1,6 @@ package web -//go:generate easyjson -all responses.go +//go:generate easyjson -all api_responses.go // URLResponse contains shortened URL info. type URLResponse struct { @@ -9,7 +9,7 @@ type URLResponse struct { Target string `json:"target"` } -// ErrorJSONResponse contains error info. +// ErrorResponse contains error info. type ErrorResponse struct { Error string `json:"error"` } diff --git a/web/responses_easyjson.go b/web/api_responses_easyjson.go similarity index 72% rename from web/responses_easyjson.go rename to web/api_responses_easyjson.go index e26a960..c5137aa 100644 --- a/web/responses_easyjson.go +++ b/web/api_responses_easyjson.go @@ -10,7 +10,7 @@ import ( var _ = json.RawMessage{} // suppress unused package warning -func easyjson_559270ae_decode_github_com_jimeh_ozu_io_web_ErrorResponse(in *jlexer.Lexer, out *ErrorResponse) { +func easyjson_ab45bbb7_decode_github_com_jimeh_ozu_io_web_ErrorResponse(in *jlexer.Lexer, out *ErrorResponse) { if in.IsNull() { in.Skip() return @@ -34,7 +34,7 @@ func easyjson_559270ae_decode_github_com_jimeh_ozu_io_web_ErrorResponse(in *jlex } in.Delim('}') } -func easyjson_559270ae_encode_github_com_jimeh_ozu_io_web_ErrorResponse(out *jwriter.Writer, in ErrorResponse) { +func easyjson_ab45bbb7_encode_github_com_jimeh_ozu_io_web_ErrorResponse(out *jwriter.Writer, in ErrorResponse) { out.RawByte('{') first := true _ = first @@ -48,21 +48,21 @@ func easyjson_559270ae_encode_github_com_jimeh_ozu_io_web_ErrorResponse(out *jwr } func (v ErrorResponse) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} - easyjson_559270ae_encode_github_com_jimeh_ozu_io_web_ErrorResponse(&w, v) + easyjson_ab45bbb7_encode_github_com_jimeh_ozu_io_web_ErrorResponse(&w, v) return w.Buffer.BuildBytes(), w.Error } func (v ErrorResponse) MarshalEasyJSON(w *jwriter.Writer) { - easyjson_559270ae_encode_github_com_jimeh_ozu_io_web_ErrorResponse(w, v) + easyjson_ab45bbb7_encode_github_com_jimeh_ozu_io_web_ErrorResponse(w, v) } func (v *ErrorResponse) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} - easyjson_559270ae_decode_github_com_jimeh_ozu_io_web_ErrorResponse(&r, v) + easyjson_ab45bbb7_decode_github_com_jimeh_ozu_io_web_ErrorResponse(&r, v) return r.Error() } func (v *ErrorResponse) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjson_559270ae_decode_github_com_jimeh_ozu_io_web_ErrorResponse(l, v) + easyjson_ab45bbb7_decode_github_com_jimeh_ozu_io_web_ErrorResponse(l, v) } -func easyjson_559270ae_decode_github_com_jimeh_ozu_io_web_ShortenedResponse(in *jlexer.Lexer, out *URLResponse) { +func easyjson_ab45bbb7_decode_github_com_jimeh_ozu_io_web_URLResponse(in *jlexer.Lexer, out *URLResponse) { if in.IsNull() { in.Skip() return @@ -90,7 +90,7 @@ func easyjson_559270ae_decode_github_com_jimeh_ozu_io_web_ShortenedResponse(in * } in.Delim('}') } -func easyjson_559270ae_encode_github_com_jimeh_ozu_io_web_ShortenedResponse(out *jwriter.Writer, in URLResponse) { +func easyjson_ab45bbb7_encode_github_com_jimeh_ozu_io_web_URLResponse(out *jwriter.Writer, in URLResponse) { out.RawByte('{') first := true _ = first @@ -116,17 +116,17 @@ func easyjson_559270ae_encode_github_com_jimeh_ozu_io_web_ShortenedResponse(out } func (v URLResponse) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} - easyjson_559270ae_encode_github_com_jimeh_ozu_io_web_ShortenedResponse(&w, v) + easyjson_ab45bbb7_encode_github_com_jimeh_ozu_io_web_URLResponse(&w, v) return w.Buffer.BuildBytes(), w.Error } func (v URLResponse) MarshalEasyJSON(w *jwriter.Writer) { - easyjson_559270ae_encode_github_com_jimeh_ozu_io_web_ShortenedResponse(w, v) + easyjson_ab45bbb7_encode_github_com_jimeh_ozu_io_web_URLResponse(w, v) } func (v *URLResponse) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} - easyjson_559270ae_decode_github_com_jimeh_ozu_io_web_ShortenedResponse(&r, v) + easyjson_ab45bbb7_decode_github_com_jimeh_ozu_io_web_URLResponse(&r, v) return r.Error() } func (v *URLResponse) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjson_559270ae_decode_github_com_jimeh_ozu_io_web_ShortenedResponse(l, v) + easyjson_ab45bbb7_decode_github_com_jimeh_ozu_io_web_URLResponse(l, v) } diff --git a/web/handler.go b/web/handler.go index eea25d8..f4eb4bf 100644 --- a/web/handler.go +++ b/web/handler.go @@ -1,10 +1,8 @@ package web import ( - "encoding/json" "html/template" "mime" - "net/url" "path" "time" @@ -38,13 +36,13 @@ func NewHandler(s shortener.Shortener) *Handler { // Handler handle HTTP requests. type Handler struct { - s shortener.Shortener - t *template.Template + shortener shortener.Shortener + template *template.Template } // Index handles requests for root. func (h *Handler) Index(c *routing.Context) error { - h.template(c, "index.html", nil) + h.respond(c, "index.html", nil) return nil } @@ -74,40 +72,17 @@ func (h *Handler) Static(c *routing.Context) error { return nil } -// Shorten shortens given URL. -func (h *Handler) Shorten(c *routing.Context) error { - uid, url, err := h.s.Shorten(c.FormValue("url")) - if err != nil { - return h.respondWithError(c, err) - } - - r := h.makeURLResponse(c, uid, url) - return h.respond(c, &r) -} - -// Lookup shortened UID. -func (h *Handler) Lookup(c *routing.Context) error { - uid := c.FormValue("uid") - url, err := h.s.Lookup(uid) - if err != nil { - return h.respondWithError(c, err) - } - - r := h.makeURLResponse(c, uid, url) - return h.respond(c, &r) -} - // LookupAndRedirect looks up given UID and redirects to it's URL. func (h *Handler) LookupAndRedirect(c *routing.Context) error { uid := []byte(c.Param("uid")) - url, err := h.s.Lookup(uid) + url, err := h.shortener.Lookup(uid) if err != nil { h.NotFound(c) return nil } - r := h.makeURLResponse(c, uid, url) + r := makeURLResponse(c, uid, url) c.Response.Header.Set("Pragma", "no-cache") c.Response.Header.Set("Expires", "Mon, 01 Jan 1990 00:00:00 GMT") @@ -121,56 +96,11 @@ func (h *Handler) LookupAndRedirect(c *routing.Context) error { c.Response.Header.Set("X-Frame-Options", "SAMEORIGIN") c.Response.Header.Set("Vary", "Accept-Encoding") - h.template(c, "redirect.html", r) + h.respond(c, "redirect.html", r) return nil } -func (h *Handler) template(c *routing.Context, name string, data interface{}) { +func (h *Handler) respond(c *routing.Context, name string, data interface{}) { c.SetContentType("text/html; charset=UTF-8") - h.t.ExecuteTemplate(c, name, data) -} - -func (h *Handler) respond(c *routing.Context, r *URLResponse) error { - resp, err := json.Marshal(r) - if err != nil { - return err - } - - c.SetContentType("application/json") - c.Write(resp) - return nil -} - -func (h *Handler) respondWithError(c *routing.Context, err error) error { - r := ErrorResponse{ - Error: err.Error(), - } - - resp, err := json.Marshal(r) - if err != nil { - return err - } - - c.SetStatusCode(fasthttp.StatusNotFound) - c.SetContentType("application/json") - c.Write(resp) - return nil -} - -func (h *Handler) makeURLResponse(c *routing.Context, uid []byte, url []byte) URLResponse { - return URLResponse{ - UID: string(uid), - URL: h.makeShortURL(c, uid), - Target: string(url), - } -} - -func (h *Handler) makeShortURL(c *routing.Context, uid []byte) string { - shortURL := &url.URL{ - Scheme: "http", - Host: string(c.Host()), - Path: "/" + string(uid), - } - - return shortURL.String() + h.template.ExecuteTemplate(c, name, data) } diff --git a/web/handler_helpers.go b/web/handler_helpers.go new file mode 100644 index 0000000..41c0018 --- /dev/null +++ b/web/handler_helpers.go @@ -0,0 +1,25 @@ +package web + +import ( + "net/url" + + "github.com/qiangxue/fasthttp-routing" +) + +func makeURLResponse(c *routing.Context, uid []byte, url []byte) URLResponse { + return URLResponse{ + UID: string(uid), + URL: makeShortURL(c, uid), + Target: string(url), + } +} + +func makeShortURL(c *routing.Context, uid []byte) string { + shortURL := &url.URL{ + Scheme: "http", + Host: string(c.Host()), + Path: "/" + string(uid), + } + + return shortURL.String() +} diff --git a/web/router.go b/web/router.go index 3b45fcf..efd727f 100644 --- a/web/router.go +++ b/web/router.go @@ -8,14 +8,16 @@ import ( // NewRouter creates a new routing.Router with all handlers registered. func NewRouter(s shortener.Shortener) *routing.Router { r := routing.New() - h := NewHandler(s) - r.Get("/", h.Index) - r.Get("/api/shorten", h.Shorten) - r.Get("/api/lookup", h.Lookup) - r.Get("/static/*", h.Static) - r.Get("/", h.LookupAndRedirect) - r.Get("/*", h.NotFound) + api := NewAPIHandler(s) + r.Get("/api/shorten", api.Shorten) + r.Get("/api/lookup", api.Lookup) + + handler := NewHandler(s) + r.Get("/", handler.Index) + r.Get("/static/*", handler.Static) + r.Get("/", handler.LookupAndRedirect) + r.Get("/*", handler.NotFound) return r }