Share current html response(as is) with user input intact - html

I am trying to achieve something similar to the Go Playground's share button.
When a button called share is clicked, the current HTML response is saved into a file. That also includes everything a user can see.
What I can do so far
- I can successfully save an HTML page into a file.
- I can get and save a whole page from a URL using HTTP Get.
Caveats
- I can't save a current webpage I am working on.
This is my code so far for getting remote URLs:
func HTTPGet(url string, timeout time.Duration) (content []byte, err error) {
request, err := http.NewRequest("POST", url, nil)
if err != nil {
return
}
ctx, cancel_func := context.WithTimeout(context.Background(), timeout)
request = request.WithContext(ctx)
response, err := http.DefaultClient.Do(request)
if err != nil {
return
}
defer response.Body.Close()
if response.StatusCode != 200 {
cancel_func()
return nil, fmt.Errorf("INVALID RESPONSE; status: %s", response.Status)
}
return ioutil.ReadAll(response.Body)
}
Hopefully this will not involve messing with the DOM manually.

There is a few ways this can be done, with some cooperation from the client.
If you want to save the current web page as it is displayed to the client (with the current form values, etc.), you have to do that with Javascript on the client side. You can get the document, serialize it in some form and send it to the server to save it.
Alternative is to generate the HTML page on the server side again, but that will not contain data user entered, or any dynamically generated HTML on the client side.
Another way is to use Javascript on the client side to collect page state including form values and data representing any dynamically generated content, send it to the server to save, and reconstruct the page as needed.

Related

How to organise Function Maps for specific endpoints in Gin (GoLang)?

TL;DR
I'm wondering how to organise function maps for go templates that are dependant on the data being obtained in the specific endpoint being called.
Issue
I have a main frontend endpoint (for a CMS) which will grab the slug of the page, and go and obtain the post data it from the model. I also have a templates package that takes in models, the post data and gin context.
GoView is currently being used. But I am setting HTMLRender to equal a new ginview evertime the page is hit presumably this isn't great for performance and I'm wondering if there is a neater way of going about it.
// Serve the front end website
func (c *FrontendController) Serve(g *gin.Context) {
path := g.Request.URL.Path
post, err := c.models.Posts.GetBySlug(path)
if err != nil {
NoPageFound(g)
return
}
tf := templates.NewFunctions(g, c.models, &post)
c.server.HTMLRender = ginview.New(goview.Config{
Root: paths.Theme(),
Extension: config.Template["file_extension"],
Master: "/layouts/main",
Partials: []string{},
DisableCache: true,
Funcs: tf.GetFunctions(),
})
r := ResourceData{
Post: post,
}
g.HTML(200, config.Template["template_dir"] + "/" + post.PageTemplate, r)
}
Templates
Some template functions include getting the field & author of the post, and seeing if the user is logged in, for example.
func (t *TemplateFunctions) isAuth() bool {
...
}
There are also template functions that aren't tied to gin.context such as getting the assets path.
func (t *TemplateFunctions) assetsPath() string {
...
}
Questions
Is there a way to merge the data dependant functions with global ones?
Is there a way to set the template functions without redeclaring the HTMLRender every time a page is hit? Or is it necessary?

What is the default browser form behavior for POST method?

Normally I use JSON API and SPA for when I make web apps, but I am trying to learn how to use the more simple method server-side templating and default form behavior with Go.
I am doing something very similar to what is seen here. The only difference is that I changed the action to "/" in the form for simplicity and handle the GET and POST from the same Handler (filtering by method type).
Server-Side Template
<form action="/" method="post" >
...
<div class="form-group">
<label for="myname">Dev Name</label>
<input type="text" class="form-control" id="myname" name="myname" value="{{.MyDname}}">
</div>
<button type="submit" class="btn btn-primary">Update</button>
</form>
Server side
func MainPage(w http.ResponseWriter, r *http.Request) {
fmt.Println("method:", r.Method) //get request method
if r.Method == "GET" {
t, err := template.New("RTL Page").Parse(page)
if err != nil {
log.Fatal(err)
}
err = t.Execute(w, State)
if err != nil {
log.Fatal(err)
}
}
if r.Method == http.MethodPost {
r.ParseForm()
...
}
}
The initial web page renders fine, the data gets sent correctly in a POST, I answer the POST with a just a 200 but then my webpage goes blank.
How do I tell the browser to keep or rerender the original page? Do I need to send an HTML response to the POST? Something other than a 200?
What you are asking is the basic operation of HTTP. The client sends a request, and the server responds to the request. If the client is a browser, the browser displays the response.
For GET request, browser sends the GET request to the server, the server responds with an HTML page, and browser displays it.
For POST request, browser sends the POST request along with the values submitted in the form, and the server should respond with an HTML page, so the browser can dispay it. The response displayed will be a POST response, so if you reload that page, it will try to resubmit the form.
#Volker pointed out what needed to change. The POST needed to be answered by the HTML I wanted displayed, so the real simple way to fix this is:
func MainPage(w http.ResponseWriter, r *http.Request) {
fmt.Println("method:", r.Method) //get request method
if r.Method == http.MethodPost {
r.ParseForm()
...
}
if r.Method == "GET" || r.Method == "POST" {
t, err := template.New("RTL Page").Parse(page)
if err != nil {
log.Fatal(err)
}
err = t.Execute(w, State)
if err != nil {
log.Fatal(err)
}
}
}

Best way to integrate database into Go Web application

I'm new to developing web applications in Go.
I'm looking for the best way to integrate a MySQL database into my web application.
I was thinking of doing something like this:
type Context struct {
Database *sql.DB
}
// Some database methods like Close() and Query() for Context struct here
In the main function for my web application I would have something like this:
db := sql.Open(...)
ctx := Context{db}
I would then pass then pass my Context structure into various handlers that require a database connection. Would this be a good design decision or is there a better way to integrate a SQL database into my web application?
I typically do something like this:
package main
func main(){
db,err := sql.Open(...)
if err != nil {
log.Fatal(err)
}
defer db.Close()
http.HandleFunc("/feed", server.FeedHandler(db))
http.HandleFunc("/gui", server.GuiHandler(db))
...
log.Fatal(http.ListenAndServe(":8000", nil))
}
Where server is a separate package where I define, implement and test all my http handlers.
This is basically what you were thinking of but skipping the step of wrapping the db in a struct which isn't necessary. I would not recommend making the db a global variable. Having global dependencies will completely destroy any possibility of testing your http handlers in a reliable fashion.
Dependency injecting the db, as in the example above, costs you two extra characters to type every time you call the function but it allows you to test your http handlers easily using the go-sqlmock package which you definitely want to do as your web app grows.
package server
func TestFeedHandler(t *testing.T){
mockdb, err := sqlmock.New()
if err != nil {
t.Errorf("An error '%s' was not expected when opening a stub database connection", err)
}
columns := []string{"id", "name"}
sqlmock.ExpectQuery("SELECT id,name from persons;").
WillReturnRows(sqlmock.NewRows(columns).
AddRow(1234, "bob")
handler := FeedHandler(mockdb)
// test that handler returns expected result
}

Conditional rendering of HTML in Golang layout.tpl by session variable

I use Gorilla sessions (via negroni-sessions) to store my user sessions in cookies. I also use github.com/unrolled/render for my HTML template rendering:
main.go:
package main
import (
...
"github.com/codegangsta/negroni"
"github.com/goincremental/negroni-sessions"
"github.com/goincremental/negroni-sessions/cookiestore"
"github.com/julienschmidt/httprouter"
"github.com/unrolled/render"
...
)
func init() {
...
ren = render.New(render.Options{
Directory: "templates",
Layout: "layout",
Extensions: []string{".html"},
Funcs: []template.FuncMap{TemplateHelpers},
IsDevelopment: false,
})
...
}
func main() {
...
router := httprouter.New()
router.GET("/", HomeHandler)
// Add session store
store := cookiestore.New([]byte("my password"))
store.Options(sessions.Options{
//MaxAge: 1200,
Domain: "",
Path: "/",
})
n := negroni.New(
negroni.NewRecovery(),
sessions.Sessions("cssession", store),
negroni.NewStatic(http.Dir("../static")),
)
n.UseHandler(router)
n.Run(":9000")
}
As you can see above, I use a layout.html master HTML template which is included when any page renders, like my home page:
package main
import (
"html/template"
"github.com/julienschmidt/httprouter"
)
func HomeHandler(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
var model = struct {
CatalogPicks []PromotionalModelList
ClearanceItems []Model
}{
CatalogPicks: GetCatalogPicks(),
ClearanceItems: GetClearanceItems(),
}
ren.HTML(w, http.StatusOK, "home", model)
}
In my layout.html master HTML template, I want to render an admin menu but only if the current user is an admin:
layout.html:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>{{ template "title" . }}</title>
...
</head>
<body>
...
<!--Main Menu-->
<nav class="menu">
<ul class="catalog">
<li class="has-submenu">
{{ RenderMenuCategories }}
</li>
<li>Blog</li>
<li>Company</li>
{{ RenderAdminMenu }}
</ul>
</nav>
...
My issue is that the above template helper function RenderAdminMenu() doesn't have access to the HTTP Request object and therefore cannot access the User session object to determine if the user is admin.
I can pass the User object into the template context via the Home page handler, and use an if statement RenderAdminMenu() function, like this
{{ if .User.IsAdmin }}
{{ RenderAdminMenu }}
{{ end }}
...but since I am using a master template, I would have to do that from every web page on the site. Is there a more efficient way?
I was thinking perhaps there might be a way to access some kind of global Context object from within RenderAdminMenu() (or layout.html) which contains the Request details (like you can in ASP.NET)
There's a few things you need to do to tie this together. I'm not going to show a complete example as it would be both fairly lengthy and may not match your code (which you haven't posted). It will contain the basic building blocks though: if you get stuck, come back with a direct question and a code snippet and you'll get a more direct answer :)
Write some middleware or logic in a [login] handler that saves the user data in the session when a user logs in. A userID, email and admin boolean value would be sufficient. e.g.
// In your login handler, once you've retrieved the user &
// matched their password hash (scrypt, of course!) from the DB.
session.Values["user"] = &youruserobject
err := session.Save(r, w)
if err != nil {
// Throw a HTTP 500
}
Note: remember that you need to gob.Register(&youruserobject{}) as per the gorilla/sessions docs if you want to store your own types.
Write a helper to type-assert your type when you pull it out of the session, e.g.
var ErrInvalidUser= errors.New("invalid user stored in session")
func GetUser(session *sessions.Session) (*User, error) {
// You can make the map key a constant to avoid typos/errors
user, ok := session.Values["user"].(*User)
if !ok || user == nil {
return nil, ErrInvalidUser
}
return user, nil
}
// Use it like this in a handler that serves user content
session, err := store.Get("yoursessionname", r)
if err != nil {
// Throw a HTTP 500
}
user, err := GetUser(session)
if err != nil {
// Re-direct back to the login page or
// show a HTTP 403 Forbidden, etc.
}
Write something to check if the returned user is an admin:
func IsAdmin(user *User) bool {
if user.Admin == true && user.ID != "" && user.Email != "" {
return true
}
return false
}
Pass that to the template:
err := template.Execute(w, "sometemplate.html", map[string]interface{}{
"admin": IsAdmin(user),
"someotherdata": someStructWithData,
}
// In your template...
{{ if .admin }}{{ template "admin_menu" }}{{ end }}
Also make sure you're setting an authentication key for your session cookies (read the gorilla docs), preferably an encryption key, and that you're serving your site over HTTPS with the Secure: true flag set as well.
Keep in mind that the above method is also simplified: if you de-flag a user as admin in your DB, the application will continue to detect them as an administrator for as long as their session lasts. By default this can be 7 days, so if you're in a risky environment where admin churn is a real problem, it may pay to have really short sessions OR hit the DB inside the IsAdmin function just to be safe. If it's a personal blog and it's just you, not so much.
Added: If you want to pass the User object directly to the template, you can do that too. Note that it's more performant to do it in your handler/middleware than it is in the template logic. You also get the flexibility of more error handling, and the option of "bailing out" earlier - i.e. if the session contains nothing, you can fire up a HTTP 500 error rather than rendering half a template or having to put lots of logic in your template to handle nil data.
You still need to store your User object (or equivalent) in the session, and retrieve it from session.Values before you can pass it to the template.
func GetUser(r *http.Request) *User {
session, err := store.Get("yoursessionname", r)
if err != nil {
// Throw a HTTP 500
}
if user, ok := session.Values["user"].(*User); ok {
return user
}
return nil
}
// In the handler itself
err := template.Execute(w, "sometemplate.html", map[string]interface{}{
"user": GetUser(r),
"someotherdata": someStructWithData,
}
// In your template...
{{ if .User.admin }}{{ template "admin_menu" }}{{ end }}
It seems you cannot access the Request context from a template or a template helper function afterall (so I accepted the answer above). My solution was to create a Page struct that I pass as the context for every template. It contains the Content as a generic interface, the User object, as well as other useful parameters:
//Page holds the model to be rendered for every HTTP handler.
type Page struct {
MetaTitle string
User User
HeaderStyles string
HeaderScripts string
FooterScripts string
Content interface{}
}
func (pg *Page) Init(r *http.Request) {
if pg.MetaTitle == "" {
pg.MetaTitle = "This is the default <title> content for the page!"
}
user, _ := GetUserFromSession(r)
pg.User = *user
if user.IsAdmin() {
pg.HeaderStyles += `<link href="/css/libs/summernote/summernote.css" rel="stylesheet">`
pg.FooterScripts += `<script src="/js/libs/summernote/summernote.min.js"></script>`
}
}
The Init method allows me to set defaults and use the Page struct more easily, by specifying only the Content for the page, if that's all I need:
package main
import (
"html/template"
"github.com/julienschmidt/httprouter"
)
func HomeHandler(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
var model = struct {
CatalogPicks []PromotionalModelList
ClearanceItems []Model
}{
CatalogPicks: GetCatalogPicks(),
ClearanceItems: GetClearanceItems(),
}
pg := &Page{Content: model}
pg.Init(r)
ren.HTML(w, http.StatusOK, "home", pg)
}

Rendering CSS in a Go Web Application

I wrote a very basic web application, following this tutorial. I want to add css rules in an external stylesheet, but it's not working - when the HTML templates are rendered, something goes wrong and the CSS is completely ignored. If I put the rules in tags, it works, but I don't want to have to deal with that.
In a Go web application, how do I render an external CSS stylesheet?
Add a handler to handle serving static files from a specified directory.
eg. create {server.go directory}/resources/ and use
http.Handle("/resources/", http.StripPrefix("/resources/", http.FileServer(http.Dir("resources"))))
along with /resources/somethingsomething.css
The reason for using StripPrefix is that you can change the served directory as you please, but keep the reference in HTML the same.
eg. to serve from /home/www/
http.Handle("/resources/", http.StripPrefix("/resources/", http.FileServer(http.Dir("/home/www/"))))
Note that this will leave the resources directory listing open.
If you want to prevent that, there is a good snippet on the go snippet blog:
http://gosnip.posterous.com/disable-directory-listing-with-httpfileserver
Edit: Posterous is now gone, so I just pulled the code off of the golang mailing list and will post it here.
type justFilesFilesystem struct {
fs http.FileSystem
}
func (fs justFilesFilesystem) Open(name string) (http.File, error) {
f, err := fs.fs.Open(name)
if err != nil {
return nil, err
}
return neuteredReaddirFile{f}, nil
}
type neuteredReaddirFile struct {
http.File
}
func (f neuteredReaddirFile) Readdir(count int) ([]os.FileInfo, error) {
return nil, nil
}
Original post discussing it: https://groups.google.com/forum/#!topic/golang-nuts/bStLPdIVM6w
And use it like this in place of the line above:
fs := justFilesFilesystem{http.Dir("resources/")}
http.Handle("/resources/", http.StripPrefix("/resources/", http.FileServer(fs)))