When I thought about writing a Web application using Go, I was only pursuing a desire to try something new for myself. Later, I realized that the Web shell can be used as a cross-platform GUI library, which I used in my project [
1 ].
Introduction
The difference from this lesson from the others is that I will consider only standard Go packages. For my task, this was more than enough, and I had no desire to understand any frameworks (for example, Revel [
2 ]). So I will touch on the topic of requests. For example, there is no clear example on uploading a file to a server on the Internet at the moment. Therefore, I decided to write an article that will allow you to create a skeleton for further development.
In the program, I will not handle errors due to a decrease in the amount of code. Also, I will not describe trivial things, such as the obvious arguments of functions (for more details, see the official documentation [
3 ] [
4 ]). Some points, for clarity, can be described not strictly formal language, or not quite right.
')
Start
Let's start with a simple “Hello World” program. To do this, we need to describe the handler function:
func index(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-type", "text/plain") w.Write([]byte("Hello World!!!")) }
The function arguments are
ResponseWriter and
Request .
ResponseWriter is an interface that is used to form an HTTP response,
Request is an HTTP request received by the server or intended to be sent to the client. In this function, in the header, set the content type to “plain text”, and then send our string to the client.
Next, we need to “bind” this handler to the pattern:
func main() { http.HandleFunc("/", index) http.ListenAndServe(":80", nil) }
With
HandleFunc, we bind the handler function to the pattern, and with the help of
ListenAndServe we start the server directly. The pattern can be any: "/", "/ page0", "/ page0 / page01". But everything after the pattern will apply to it. For example, we registered two patterns: "/" and "/ page0". If in the address bar to write "/ page", then the appeal will be to "/", and if "/ page0 / page", then to "/ page0". The complete code looks like this:
package main import ( "net/http" ) func index(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-type", "text/plain") w.Write([]byte("Hello World!!!")) } func main() { http.HandleFunc("/", index) http.ListenAndServe(":80", nil) }
We compile, run (or immediately launch), in the browser, go to the address where the server is running (on the local machine it is localhost - 127.0.0.1) and see our line.
We use templates
The "embedded" html-code in the program looks extremely ugly and extremely inconvenient for editing (especially in large projects):
fmt.Fprint(w, "<form target=\"_blank\" action=\"/exec/\" enctype=\"multipart/form-data\" method=\"post\">"+ "<input type=\"file\" name=\"imgfile\" />"+ "<input type=\"submit\" name=\"button\" value=\"Execute\" />"+ "</form>")
Therefore, we will use the
template package to improve readability and ease of editing. To begin, let's write the “backbone” of our page (I will be guided by the HTML5 standard):
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>{{.Title}}</title> </head> <body> <p>{{.Msg}}</p> </body> </html>
Title and
Msg later turn into text, but more on that later. First we need to describe the data structure, which we later pass to the function:
type page struct { Title string
Now we will write the handler function:
func index(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-type", "text/html") t, _ := template.ParseFiles(“index.html”) t.Execute(w, &page{Title: "Just page", Msg: "Hello World"}) }
ParseFiles - loads the specified file (or files),
Execute processes the template, substituting the data, and writes them to
w .
Event handling
The last thing left is to make a page with some button, file download, etc. In the example, the server will accept the image file, convert it to JPEG with a quality of 0 (this will clearly show that everything works), and returns it.
Let's change our html-template by adding a form with the POST method to it:
<form target="_blank" action="/exec/" enctype="multipart/form-data" method="post"> <input type="file" name="imgfile" /> <input type="submit" name="button" value="Execute" /> </form>
Let's write the handler function:
func index(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-type", "text/html") title := r.URL.Path[len("/"):] if title != "exec/" { t, _ := template.ParseFiles("index.html") t.Execute(w, &page{Title: "Convert Image"}) } else { imgfile, fhead, _ := r.FormFile("imgfile") img, ext, _ := image.Decode(imgfile) w.Header().Set("Content-type", "image/jpeg") w.Header().Set("Content-Disposition", "filename=\"" + fhead.Filename + "." + ext + "\"") jpeg.Encode(w, img, &jpeg.Options{0}) } }
The variable
title will be what is after the pattern. For example, if the address is “http: // localhost / qwe /”, then the
title will be “qwe /”. Next we look, if we are not on “exec /”, then we load, process and output a regular page, but if we are on “exec /”, then we process the input file.
FormFile - returns a file with the specified key (if there are several such forms, then the first one) and some information about the file. We set the content type (as “image / jpeg”), the file name (the server will send the answer to the browser with this name) and write the image.
The full text of the exampleindex.html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>{{.Title}}</title> </head> <body> <form target="_blank" action="/exec/" enctype="multipart/form-data" method="post"> <input type="file" name="imgfile" /> <input type="submit" name="button" value="Execute" /> </form> </body> </html>
lesson.go package main import ( "net/http" "html/template" "image" "image/jpeg" _"image/png" _"image/gif" ) type page struct { Title string Msg string } func index(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-type", "text/html") title := r.URL.Path[len("/"):] if title != "exec/" { t, _ := template.ParseFiles("index.html") t.Execute(w, &page{Title: "Convert Image"}) } else { imgfile, fhead, _ := r.FormFile("imgfile") img, ext, _ := image.Decode(imgfile) w.Header().Set("Content-type", "image/jpeg") w.Header().Set("Content-Disposition", "filename=\"" + fhead.Filename + "." + ext + "\"") jpeg.Encode(w, img, &jpeg.Options{0}) } } func main() { http.HandleFunc("/", index) http.ListenAndServe(":80", nil) }
And one more thing, if you use some local file in your template (for example, an image file), then you will have to write a handler or use
FileServer from the
http package. In the first case, you will have something like this:
func logo(w http.ResponseWriter, r *http.Request) { file, _ := ioutil.ReadFile("img/logo.png") w.Write(file) } <...> http.HandleFunc("/img/logo.png", logo) <...>
And in the second everything is easier, because no need to describe every single file:
<...> http.Handle("/img/", http.StripPrefix("/img/", http.FileServer(http.Dir("./img/")))) <...>
Conclusion
Thus, I showed how simple and clear Go can be to write my own Web application. The advantages of Go are the simplicity of the language itself, the simplicity of the packages for working with the HTTP protocol, the possibility of using templates, as well as the independence of the code written on pure Go from the architecture and the operating system. Go currently supports: FreeBSD (x86, amd64, arm), NetBSD (x86, amd64), GNU / Linux (x86, amd64, arm), MacOS X (x86, amd64), Windows (x86, amd64).
[1]
mapitemeditor.sourceforge.net[2]
habrahabr.ru/post/162115[3]
golang.org/doc[4]
golang.org/pkg