Text/template: "can't call method/function with 0 results." - go

How can I execute function in templates that returns no value? Here is example:
func main() {
u, err := url.Parse("http://example.com/test?param1=true&param2=true")
if err != nil {
log.Fatal(err)
}
m := u.Query()
m.Del("param1") // param1 successful deleted!
u.RawQuery = m.Encode()
fmt.Println(u.RawQuery)
const tmpl = `
{{$m := .Query}}
{{$m.Del "param2"}} <!-- failed to delete param2! -->
{{.RawQuery}}
`
t := template.Must(template.New("").Parse(tmpl))
err = t.Execute(os.Stdout, u)
if err != nil {
log.Println("executing template:", err)
}
}
see this code in play.golang.org
I know that in templates shouldn't be much logic, but ignorance of running function that returns no value seems to me interesting issue.

Templates in Go are not like those in other languages (e.g. PHP). Use template.FuncMap to create custom functions for your templates.
package main
import (
"fmt"
"log"
"net/url"
"os"
"text/template"
)
func main() {
funcMap := template.FuncMap{
"delete": deleteMap,
}
u, err := url.Parse("http://example.com/test?param1=true&param2=true")
if err != nil {
log.Fatal(err)
}
u = deleteMap(u, "param1") // works in regular code and templates
fmt.Println(u.RawQuery)
const tmpl = `
{{$m := delete . "param2"}} <!-- WORKS! -->
{{$m.RawQuery}}
`
t := template.New("").Funcs(funcMap)
t = template.Must(t.Parse(tmpl))
err = t.Execute(os.Stdout, u)
if err != nil {
log.Println("executing template:", err)
}
}
func deleteMap(u *url.URL, key string) *url.URL {
m := u.Query()
m.Del(key) // key successful deleted!
u.RawQuery = m.Encode()
return u
}
Or, try the playground version.

Related

How to convert go type Cookie to type string

In go I have a function:
func UrlGET(url string, headers string) string { // inputs are url and headers for a http request
...
req, err := http.NewRequest("GET", url, nil)
...
resp, err := client.Do(req)
defer resp.Body.Close()
if rc := resp.Cookies(); len(rc) > 0 {
return string(rc)
}
return ""
}
However, you cannot convert type Cookie ([]*http.Cookie to type string (cannot convert rc (type []*http.Cookie) to type string). What would be an alternative or another way to convert to type string, ideally I would still return type string. I'm relatively new to go so at a bit of a wall as to what else to try.
Ideally, it would return like cookie=some_cookie_value as a string.
If you just want one big string, you can do:
package main
import "net/http"
func main() {
r, e := http.Get("https://stackoverflow.com")
if e != nil {
panic(e)
}
defer r.Body.Close()
s := r.Header.Get("Set-Cookie")
println(s)
}
Or you could build a map:
package main
import (
"fmt"
"net/http"
)
func main() {
r, e := http.Get("https://stackoverflow.com")
if e != nil {
panic(e)
}
defer r.Body.Close()
m := make(map[string]string)
for _, c := range r.Cookies() {
m[c.Name] = c.Value
}
fmt.Println(m)
}
https://golang.org/pkg/net/http#Response.Cookies
https://golang.org/pkg/net/http#Response.Header

Redirect from one page to another not working

I am trying to Insert the scan results using the Scan func and once done I want to redirect to show results which are nothing but a select query to the inserts done ever. Both functions are working properly when tested separately. Is this not the correct way to redirect from the first page to the next?
package main
import (
"bytes"
"database/sql"
"fmt"
"html/template"
"log"
"net/http"
"os/exec"
"regexp"
"strconv"
"strings"
"time"
_ "github.com/go-sql-driver/mysql"
)
func dbConn() (db *sql.DB) {
dbDriver := "mysql"
dbUser := "root"
dbPass := "root"
dbName := "test"
db, err := sql.Open(dbDriver, dbUser+":"+dbPass+"#/"+dbName+"?parseTime=true")
if err != nil {
log.Fatal(err)
}
return db
}
var tmpl = template.Must(template.ParseGlob("form/*"))
var hostnameInput string
func main() {
http.HandleFunc("/login", login)
http.HandleFunc("/show", Show)
lerr := http.ListenAndServe(":9090", nil) // setting listening port
if lerr != nil {
log.Fatal("ListenAndServe: ", lerr)
}
}
func Insert(hostname string, ports []int) {
db := dbConn()
// INSERT INTO DB
// prepare
stmt, e := db.Prepare("insert into nmap_reports(reported_at, host_address) values ( ?, ?)")
if e != nil {
log.Fatal(e)
}
//execute
res, er := stmt.Exec(time.Now(), hostname)
if er != nil {
log.Fatal(er)
}
for _, v := range ports {
insForm, err := db.Prepare("INSERT INTO nmap_report_ports(nmap_report_id,port) VALUES (?,?)")
if err != nil {
log.Fatal(err)
}
id, fail := res.LastInsertId()
if fail != nil {
log.Fatal(fail)
}
_, er := insForm.Exec(id, v)
if er != nil {
log.Fatal(er)
}
}
defer db.Close()
}
func Select(hostname string) []Result {
db := dbConn()
// query all data
stmt, err := db.Prepare("SELECT nm.nmap_report_id, nm.host_address,nm.reported_at,GROUP_CONCAT(port) AS `Ports` FROM nmap_reports nm left join nmap_report_ports nrp on nm.nmap_report_id = nrp.nmap_report_id where nm.host_address = ? group by nm.nmap_report_id order by nmap_report_id desc")
if err != nil {
log.Fatal(err)
}
rows, er := stmt.Query(hostname)
if er != nil {
log.Fatal(er)
}
// declare empty result variable
var resultI = Result{}
// iterate over rows
resultSet := make([]Result, 0)
for rows.Next() {
e := rows.Scan(&resultI.ReportId, &resultI.Hostname, &resultI.Date, &resultI.Ports)
if e != nil {
log.Fatal(er)
}
resultSet = append(resultSet, resultI)
}
defer db.Close()
return resultSet
}
type Result struct {
ReportId int `json:"reportId"`
Hostname string `json:"hostname"`
Date time.Time `json:"date"`
Ports string `json:"ports"`
}
func Show(w http.ResponseWriter, r *http.Request) {
data := Select(hostnameInput)
temp, temperr := template.ParseFiles("form/show.tmpl")
if temperr != nil {
log.Fatal(temperr)
}
temp.ExecuteTemplate(w, "show", data)
}
func login(w http.ResponseWriter, r *http.Request) {
fmt.Println("method:", r.Method) //get request method
if r.Method == "GET" {
t, _ := template.ParseFiles("form/input.tmpl")
t.Execute(w, nil)
} else {
//if r.Method == "POST" {
r.ParseForm()
fmt.Println("hostname:", r.Form["hname"])
var rxHostIP = regexp.MustCompile("^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$|^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)+([A-Za-z]|[A-Za-z][A-Za-z0-9\\-]*[A-Za-z0-9])$")
hostnameInput = strings.Join(r.Form["hname"], "")
fmt.Println("hnmae is:" + hostnameInput)
if !rxHostIP.MatchString(hostnameInput) {
w.WriteHeader(400)
w.Write([]byte("Invalid Ip/hostname"))
}
command := []string{"nmap", "-p", "1-1000"}
command = append(command, r.Form["hname"]...)
finalcommand := strings.Join(command, " ")
fmt.Println(finalcommand)
cmd := exec.Command(`C:\Windows\System32\cmd.exe`, "/c", finalcommand)
cmdOutput := &bytes.Buffer{}
cmd.Stdout = cmdOutput
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
check := string(cmdOutput.Bytes())
re := regexp.MustCompile(`([0-9]+)./`)
stringArr := re.FindAllString(check, -1)
intArr := make([]int, 0)
for _, v := range stringArr {
n := strings.Replace(v, "/", "", -1)
i, cerr := strconv.Atoi(n)
if cerr != nil {
log.Fatal(cerr)
}
intArr = append(intArr, i)
}
Insert(hostnameInput, intArr)
}
http.Redirect(w, r, "/show", 301)
}
Trying to redirect from login page to show page using
http.Redirect(w, r, "/show", 301)
I think the redirect code is fine. There might be something going inside the login handler that prevents the redirect from happening.
I've annotated and modified some parts of the login handler below, if it might be helpful to you.
func login(w http.ResponseWriter, r *http.Request) {
fmt.Println("method:", r.Method) //get request method
if r.Method == "GET" {
// note#1: don't ignore the error
t, err := template.ParseFiles("form/input.tmpl")
if err != nil {
log.Fatal(err)
}
// note#2: check the error
err = t.Execute(w, nil)
if err != nil {
log.Fatal(err)
}
} else {
var err error
// note#3: check the error
err = r.ParseForm()
if err != nil {
log.Fatal(err)
}
// ...
// other code, omitted for brevity
// ...
}
// note#4: this is fine if you intend to redirect all the request,
// regardless of the method, to the `/show` endpoint
http.Redirect(w, r, "/show", 301)
}

How to parse QueryString

I have a string such as username=Test1234&currency=THB and I need to get value of username or currency
qry, _ := url.Parse(string(qryString))
An example is as follows:
package main
import (
"fmt"
"net/url"
)
func main() {
address := "http://example.com?name=poloxue&age=11"
u, err := url.Parse(address)
if err != nil {
panic(err)
}
fmt.Println(u.RawQuery)
q, err := url.ParseQuery(u.RawQuery)
if err != nil {
panic(err)
}
fmt.Println(q.Get("name"))
}
Output:
name=poloxue&age=11
poloxue

Get permanent MAC address

Is there an easy way to get the permanent MAC Address using Go?
package main
import (
"fmt"
"log"
"net"
)
func getMacAddr() ([]string, error) {
ifas, err := net.Interfaces()
if err != nil {
return nil, err
}
var as []string
for _, ifa := range ifas {
a := ifa.HardwareAddr.String()
if a != "" {
as = append(as, a)
}
}
return as, nil
}
func main() {
as, err := getMacAddr()
if err != nil {
log.Fatal(err)
}
for _, a := range as {
fmt.Println(a)
}
}

Specify names for parsed templates

I am trying to dynamically parse files using walk in a folder and I want to be able to set the path of the file "path/file.html". But my issue is if I have a file in a folder "path/folder/files.html" I can't do it because when I ExecuteTemplate the file name will be the same "files.html". Is it possible to name each template as I ParseFiles?
Im ok with doing a file one at a time if trying to do them all at once wont work.
// Parse file and send to responsewriter
func View(w http.ResponseWriter, path string) {
temp, err := template.ParseFiles("application/views/"+path+".html")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
} else {
temp.ExecuteTemplate(w, path, nil)
}
}
Walk the filesystem using filepath.Walk and a consumer method that will create templates with the full file paths as names:
package main
import (
"fmt"
"html/template"
"os"
"path/filepath"
)
func consumer(p string, i os.FileInfo, e error) error {
t := template.New(p)
fmt.Println(t.Name())
return nil
}
func main() {
filepath.Walk("/path/to/template/root", filepath.WalkFunc(consumer))
}
You can try template.Lookup, the whole process looks like:
var (
templates *template.Template
)
func loadTemplate() {
funcMap := template.FuncMap{
"safe":func(s string) template.HTML {
return template.HTML(s)
},
}
var err error
templates, err = utils.BuildTemplate("/theme/path/", funcMap)
if err != nil {
log.Printf("Can't read template file %v,", err)
}
}
func homeHandler(w http.ResponseWriter, r *http.Request) {
//lookup the theme your want to use
templ = templates.Lookup("theme.html")
err := templ.Execute(w, data)
if err != nil {
log.Println(err)
}
}
func main() {
loadTemplate()
}
BuildTemplate looks like:
func BuildTemplate(dir string, funcMap template.FuncMap) (*template.Template, error) {
fs, err := ioutil.ReadDir(dir)
if err != nil {
fmt.Printf("Can't read template folder: %s\n", dir)
return nil, err
}
files := make([]string, len(fs))
for i, f := range (fs) {
files[i] = path.Join(dir, f.Name())
}
return template.Must(template.New("Template").Funcs(funcMap).ParseFiles(files...)), nil
}

Resources