Browse Source

Implemented Controllers using https://astaxie.gitbooks.io/build-web-application-with-golang/en/13.5.html and other as guidance.

master
Till Wegmüller 4 years ago
parent
commit
fa30c1b770
15 changed files with 347 additions and 91 deletions
  1. +1
    -1
      auth/main.go
  2. +0
    -0
      config/dev.yaml
  3. +0
    -0
      config/prod.yaml
  4. +178
    -0
      controller/controller.go
  5. +20
    -49
      mozaik.go
  6. +8
    -0
      router/routes.go
  7. +3
    -0
      templates/base.html
  8. +12
    -0
      templates/index.html
  9. +0
    -37
      templates/index.html.tpl
  10. +2
    -3
      templates/notfound.html
  11. +15
    -0
      templates/page_form.html
  12. +7
    -0
      templates/page_view.html
  13. +0
    -1
      util.go
  14. +24
    -0
      util/util.go
  15. +77
    -0
      views.go

+ 1
- 1
auth/main.go View File

@@ -27,7 +27,7 @@ func SetupAuthboss() {

Ab.LayoutDataMaker = layoutData

b, err := ioutil.ReadFile(filepath.Join("templates", "base.html.tpl"))
b, err := ioutil.ReadFile(filepath.Join("templates", "base.html"))
if err != nil {
panic(err)
}


+ 0
- 0
config/dev.yaml View File


+ 0
- 0
config/prod.yaml View File


+ 178
- 0
controller/controller.go View File

@@ -0,0 +1,178 @@
package controller

import (
"html/template"
"net/http"
"errors"
"github.com/gorilla/mux"
"github.com/toasterson/mozaik/router"
"mime/multipart"
"fmt"
"github.com/dannyvankooten/grender"
)

var (
globTplCache = map[string]*template.Template{}
grend *grender.Grender
)

type Controller struct {
postFormParsed bool
isInitialized bool
Routes []router.Route
Data map[interface{}]interface{}
Name string
TplName string
TplExt string
W http.ResponseWriter
R *http.Request
}

type ControllerInterface interface {
Init() //method = Initializes the Controller
Prepare(w http.ResponseWriter, r *http.Request) //method = SetUp All Local Variables needed for the Functions
Get() error //method = GET processing
Post() error //method = POST processing
Delete() error //method = DELETE processing
Put() error //method = PUT handling
Head() error //method = HEAD processing
Patch() error //method = PATCH treatment
Options() error //method = OPTIONS processing
Finish() //method = Used to clear Temporary Variables and Cleanup
GetRoutes() []router.Route //Get the Routes of the Controller
}

type Handler struct {
ControllerInterface
}

func (this *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
this.Prepare(w, r)
defer this.Finish()
var err error = nil
switch r.Method {
case http.MethodGet:
err = this.Get()
case http.MethodPost:
err = this.Post()
case http.MethodPatch:
err = this.Patch()
case http.MethodPut:
err = this.Put()
case http.MethodHead:
err = this.Head()
case http.MethodDelete:
err = this.Delete()
case http.MethodOptions:
err = this.Options()
default:
err = errors.New("No Method")
}
//TODO Better Handling Should an error Occur Maybe Custom error Class?
if err != nil {
fmt.Println(err)
http.Error(w, err.Error(), http.StatusMethodNotAllowed)
}
}

func MakeHandler(controllerInterface ControllerInterface) *Handler{
return &Handler{controllerInterface}
}

func SetUpRouting(mux_router *mux.Router, controllers []ControllerInterface){
for _, controller := range controllers {
controller.Init()
for _, route := range controller.GetRoutes() {
mux_router.Handle(route.Path, MakeHandler(controller)).Methods(route.Method)
}
}
}

func (this *Controller) GetRoutes() []router.Route{
return this.Routes
}

func (this *Controller) Prepare(w http.ResponseWriter, r *http.Request){
this.W = w
this.R = r
}

func (this *Controller) Finish(){
this.W = nil
this.R = nil
}

func (this *Controller) Init() {}

func (this *Controller) SuperInit() {
if !this.isInitialized {
this.postFormParsed = false
this.Data = make(map[interface{}]interface{})
this.TplName = ""
this.TplExt = "html"
this.isInitialized = true
}
}

func (this *Controller) Get() error {
return errors.New("Not Allowed")
}

func (this *Controller) Post() error {
return errors.New("Not Allowed")
}

func (this *Controller) Delete() error {
return errors.New("Not Allowed")
}

func (this *Controller) Put() error {
return errors.New("Not Allowed")
}

func (this *Controller) Head() error {
return errors.New("Not Allowed")
}

func (this *Controller) Patch() error {
return errors.New("Not Allowed")
}

func (this *Controller) Options() error {
return errors.New("Not Allowed")
}

func (this *Controller) Render() error {
return grend.HTML(this.W, http.StatusOK, this.TplName+"."+this.TplExt, this.Data)
}

func (this *Controller) ParseForm(maxMem int64) error {
return this.R.ParseMultipartForm(maxMem)
}

func (this *Controller) PostFormValue(key string) string{
return this.R.PostFormValue(key)
}

func (this *Controller) FormFile(key string) (multipart.File, *multipart.FileHeader, error){
return this.R.FormFile(key)
}

//Initialize Global Server stuff
func Init(tplGlob string, debug bool, partialGlob string, funcs template.FuncMap) {
grend = grender.New(grender.Options{
Debug: debug, // If true, templates will be recompiled before each render call
TemplatesGlob: tplGlob, // Glob to your template files
PartialsGlob: partialGlob, // Glob to your patials or global templates
Funcs: funcs, // Your template FuncMap
Charset: "UTF-8", // Charset to use for Content-Type header values
})
}

func Forbidden(w http.ResponseWriter, r *http.Request) {
grend.HTML(w, http.StatusForbidden, "forbidden.html", nil)
}

func NotFound(w http.ResponseWriter, r *http.Request) {
grend.HTML(w, http.StatusNotFound, "notfound.html", nil)
}

+ 20
- 49
mozaik.go View File

@@ -4,16 +4,14 @@ import (
"net/http"
"github.com/gorilla/securecookie"
"github.com/gorilla/sessions"
"io"
"os"
"log"
"github.com/toasterson/mozaik/auth"
"github.com/gorilla/schema"
"encoding/base64"
"github.com/gorilla/mux"
"github.com/toasterson/mozaik/controller"
"github.com/justinas/alice"
"fmt"
"html/template"
)

var schemaDec = schema.NewDecoder()
@@ -21,6 +19,12 @@ var pages = map[string]Page{
"testing": {"Testing", []byte("Testing")},
}

var (
mainCont = MainController{}
newPageCont = NewPageController{}
pageDetailCont = PageDetailController{}
)

type Pages map[string]Page

type Page struct {
@@ -28,26 +32,6 @@ type Page struct {
Body []byte
}

func loadPage(title string) (Page, error) {
return pages[title], nil
}

func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
t, _ := template.ParseFiles(tmpl + ".html")
t.Execute(w, p)
}

func viewHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/view/"):]
p, err := loadPage(title)
if err != nil {
http.NotFound(w, r)
return
}
renderTemplate(w, "templates/view", &p)
}


func main() {
// Initialize Sessions and Cookies
// Typically gorilla securecookie and sessions packages require
@@ -74,27 +58,26 @@ func main() {
auth.SetSessionStore(sessions.NewCookieStore(sessionStoreKey))

// Initialize ab.
auth.SetupAuthboss()
//auth.SetupAuthboss()

//Load Templates to cache
controller.Init("./templates/*.html", true, "", nil)

// Set up our router
schemaDec.IgnoreUnknownKeys(true)
router := mux.NewRouter()

// Routes
gets := router.Methods("GET").Subrouter()

router.PathPrefix("/auth").Handler(auth.Ab.NewRouter())

gets.HandleFunc("/view/{title}", viewHandler)

//Routes
mux_router := mux.NewRouter()

router.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
io.WriteString(w, "Not found")
controller.SetUpRouting(mux_router, []controller.ControllerInterface{
&mainCont,
&newPageCont,
&pageDetailCont,
})
mux_router.NotFoundHandler = http.HandlerFunc(controller.NotFound)

// Set up our middleware chain
stack := alice.New(auth.Logger, auth.Nosurfing, auth.Ab.ExpireMiddleware).Then(router)
stack := alice.New(auth.Logger).Then(mux_router)

// Start the server
port := os.Getenv("PORT")
@@ -109,16 +92,4 @@ func index(w http.ResponseWriter, r *http.Request) {
data := layoutData(w, r).MergeKV("posts", blogs)
mustRender(w, r, "index", data)
}
*/

func badRequest(w http.ResponseWriter, err error) bool {
if err == nil {
return false
}

w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintln(w, "Bad request:", err)

return true
}
*/

+ 8
- 0
router/routes.go View File

@@ -0,0 +1,8 @@
package router

type Route struct {
Method string
Path string
}



templates/base.html.tpl → templates/base.html View File

@@ -39,6 +39,9 @@
</ul>
</div>
</div>
<div>
{{template "content" .}}
</div>
</nav>

{{with .flash_success}}<div class="alert alert-success">{{.}}</div>{{end}}

+ 12
- 0
templates/index.html View File

@@ -0,0 +1,12 @@
{{/* extends "base.html" */}}

{{define "content"}}
{{$loggedin := .loggedin}}
{{if $loggedin}}
<div class="row" style="margin-bottom: 20px;">
<div class="col-md-offset-9 col-md-2 text-right">
<a class="btn btn-primary" href="/blogs/new"><i class="fa fa-plus"></i> New Post</a>
</div>
</div>
{{end}}
{{end}}

+ 0
- 37
templates/index.html.tpl View File

@@ -1,37 +0,0 @@
{{define "pagetitle"}}Blogs - Index{{end}}

{{$loggedin := .loggedin}}
{{if $loggedin}}
<div class="row" style="margin-bottom: 20px;">
<div class="col-md-offset-9 col-md-2 text-right">
<a class="btn btn-primary" href="/blogs/new"><i class="fa fa-plus"></i> New Post</a>
</div>
</div>
{{end}}

<div class="row">
<div class="col-md-offset-1 col-md-10">
{{range .posts}}
<div class="panel panel-info">
<div class="panel-heading">
<div class="row">
<div class="col-md-6">{{.Title}}</div>
<div class="col-md-6 text-right">
{{if $loggedin}}
<a class="btn btn-xs btn-link" href="/blogs/{{.ID}}/edit">Edit</a>
<a class="btn btn-xs btn-link" href="/blogs/{{.ID}}/destroy">Delete</a>
{{end}}
</div>
</div>
</div>
<div class="panel-body">{{.Content}}</div>
<div class="panel-footer">
<div class="row">
<div class="col-md-6">By {{.AuthorID}}</div>
<div class="col-md-6 text-right">Posted on {{formatDate .Date}}</div>
</div>
</div>
</div>
{{end}}
</div>
</div>

templates/view.html → templates/notfound.html View File

@@ -2,10 +2,9 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<title>404 Not Found</title>
</head>
<body>
<h1>{{.Title}}</h1>
<div>{{printf "%s" .Body}}</div>
<h1>404 Not Found</h1>
</body>
</html>

+ 15
- 0
templates/page_form.html View File

@@ -0,0 +1,15 @@
{{/* extends "base.html" */}}

{{define "content"}}
<form action="/pages/new" method="post">
<label>
Title
<input type="text" name="title"/>
</label>
<label>
Body
<input type="text" name="body" />
</label>
<button type="submit">Submit</button>
</form>
{{end}}

+ 7
- 0
templates/page_view.html View File

@@ -0,0 +1,7 @@
{{/* extends "base.html" */}}

{{define "content"}}
{{$page := index . "page"}}
<h1>{{$page.Title}}</h1>
<div>{{printf "%s" $page.Body}}</div>
{{end}}

+ 0
- 1
util.go View File

@@ -1 +0,0 @@
package main

+ 24
- 0
util/util.go View File

@@ -0,0 +1,24 @@
package util

import (
"net/http"
"fmt"
)

func Must(err error){
if err != nil {
panic(err)
}
}

func HTTPBadRequest(w http.ResponseWriter, err error) bool {
if err == nil {
return false
}

w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintln(w, "Bad request:", err)

return true
}

+ 77
- 0
views.go View File

@@ -1 +1,78 @@
package main

import (
"github.com/toasterson/mozaik/router"
"github.com/toasterson/mozaik/controller"
"net/http"
"github.com/gorilla/mux"
"fmt"
"errors"
)

type MainController struct {
controller.Controller
}

func (c *MainController) Get() error {
return c.Render()
}

func (c *MainController) Init() {
c.SuperInit()
c.TplName = "index"
c.Routes = []router.Route{
{http.MethodGet, "/"},
}
}

//TODO See if we can make one ListController with different Parameters instead of one per Object
type PageDetailController struct {
controller.Controller
}

func (c *PageDetailController) Init() {
c.SuperInit()
c.TplName = "page_view"
c.Routes = []router.Route{
{http.MethodGet, "/pages/{title}"},
}
}

func (c *PageDetailController) Get() error {
vars := mux.Vars(c.R)
title := vars["title"]
//TODO Load from DB Here
var ok bool
c.Data["page"], ok = pages[title]
if !ok {
return errors.New("Does Not Exist yet Maybe create it?")
}
return c.Render()
}

type NewPageController struct {
controller.Controller
}

func (c *NewPageController) Init() {
c.SuperInit()
c.TplName = "page_form"
c.Routes = []router.Route{
{http.MethodGet, "/pages/new"},
{http.MethodPost, "/pages/new"},
}
}

func (c *NewPageController) Get() error {
return c.Render()
}

func (c *NewPageController) Post() error{
title := c.PostFormValue("title")
p := Page{title, []byte(c.PostFormValue("body"))}
//TODO Database Saving Here
pages[title] = p
fmt.Println(pages)
http.Redirect(c.W, c.R, title, http.StatusSeeOther)
return nil
}

Loading…
Cancel
Save