12/08/2018, 15:03

GoLang: Writing Web Applications

Golang hay còn được biết đến với cái tên khác là Go , là một ngôn ngữ lập trình được Google phát triển nội bộ vào năm 2007 và được công bố vào năm 2009. Một số ưu điểm nổi bật của Go so với những ngôn ngữ còn lại có thể kể đến như: Go có thể biên dịch ra nhiều nên tảng. Go biên dịch ...

Golang hay còn được biết đến với cái tên khác là Go, là một ngôn ngữ lập trình được Google phát triển nội bộ vào năm 2007 và được công bố vào năm 2009. Một số ưu điểm nổi bật của Go so với những ngôn ngữ còn lại có thể kể đến như:

  • Go có thể biên dịch ra nhiều nên tảng. Go biên dịch trực tiếp ra machine code. Vì vậy, nó có thể chạy ngay với hệ điều hành nó biên dịch mà không cần cài đặt gì thêm.
  • Concurrency: đây là tính năng chủ lực của Go để tận dụng năng lực xử lý của CPU. ...

Trong bài viết này, tôi sẽ giới thiệu với bạn đọc cách viết một ứng dụng web trong Go. Để hiểu hơn về bài viết, bạn cũng cần đọc qua A Tour of Go đã nhé. ^^

Làm quen với Go web

Trước hết, hãy build một web đơn giản với Go. Hãy xét ví dụ lưu thông tin ra file .txt dưới đây. Tạo thư mục chứa source code của bạn và hãy tạo file main.go.

$ mkdir go
$ cd go
$ touch main.go

Mở file main.go bằng editor ưa thích của mình, thêm vào đoạn code:

package main

import (
    "fmt"
    "io/ioutil"
)
Chúng ta đã import package _fmt_ và _io/ioutil_ từ thư viện **Go**. Nếu muốn sử dụng thêm bất kỳ package nào khác từ thư viện của **Go**, bạn chỉ cần khai báo nó vào trong import như trên. Tiếp theo, hãy định nghĩa một struct gồm 2 trường là title và body như sau:
type Page struct {
    Title string
    Body []byte
}

Kiểu giá trị []byte là "a byte slice", để hiểu thêm vấn đề này, bạn cần đọc qua mục Slices. Sở dĩ ở đây chúng ta dùng []byte mà không phải là string là vì []byte là kiểu dữ liệu mà thư viện io sử dụng.

Bây giờ, tôi muốn lưu thông tin của trường body ra file, mặc định lên trường title làm tên file. Các làm như sau:

func (p *Page) save() error {
    filename := p.Title + ".txt"
    return ioutil.WriteFile(filename, p.Body, 0600)
}

Tạo một function có tên là save, là thuộc tính của con trỏ Page, không có parameters và giá trị trả về có type là error. Method này trả về giá trị error bởi vì đây là kiểu dữ liệu trả về của function WriteFile. Nếu không có bất kỳ lỗi nào trong quá trình ghi file thì giá Method save() sẽ trả về error là nil. Để ý rằng parameter thứ 3 của function WriteFile là một số nguyên 0600. Đây cũng là permissions khi tạo file.

Sau khi ghi file thành công, tất nhiên là chúng ta muốn đọc file vừa ghi được để xem quá trình ghi file có đúng hay không?

func loadPage(title string) *Page {
    filename := title + ".txt"
    body, _ := ioutil.ReadFile(filename)
    return &Page{Title: title, Body: body}
}

Function này sẽ xây dựng filename từ parameter được truyền vào. Đọc nội dung của file đó và ghi vào biến body. và trả về con trỏ của Page với title và body. Câu hỏi đặt ra là, với đoạn code như trên thì nếu có lỗi sảy ra trong quá trình đọc file thì sẽ được xử lý như thế nào? Ví dụ như là file đó không tồn tại. Vì vậy cần chỉnh lại function loadPage() như sau:

func loadPage(title string) (*Page, error) {
    filename := title + ".txt"
    body, err := ioutil.ReadFile(filename)
    if err != nil {
        return nil, err
    }
    return &Page{Title: title, Body: body}, nil
}

Function này sẽ trả về error trong khi đọc file. Nếu giá trị này bằng nil thì quá trình đọc file thành công. Tiếp đến, để chạy được chương trình bên trên, cần có function main() để gọi đến function save() và loadPage() chúng ta vừa viết.

func main() {
    p1 := &Page{Title: "TestPage", Body: []byte("This is a sample Page.")}
    p1.save()
    p2, _ := loadPage("TestPage")
    fmt.Println(string(p2.Body))
}

Lưa lại code, mở Terminal và chạy lệnh:

$ go run main.go

Giới thiệu package net/http

Hãy xem một ví dụ của web server sau đây:

package main

import (
    "fmt"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

Mở đầu của function main() bằng việc gọi tới http.HandleFunc. Việc này sẽ thông báo cho package http handle tất cả request tới root ("/") với handler. Kết thúc hàm là việc gọi http.ListenAndServe, để dịnh nghĩa cho việc listen ở port 8080. Bây giờ hãy chạy file .go bên trên bằng Terminal. Sau đó vào địa chỉ http://localhost:8080/money. Kết quả thu được trên màn hình Terminal của bạn sẽ là:

Hi there, I love monkeys!

Bạn còn giữ source code của ví dụ đầu bài chứ. Chúng ta sẽ sử dụng package net/http vào ví dụ đó. Trước hết, để sử dụng được package đó thì chúng ta cần import.

import (
	"fmt"
	"io/ioutil"
	"net/http"
)

Tiếp theo, hãy tạo function viewHandler, giúp cho phép user có thể view 1 trang web. router mặc định sẽ là "/view/".

func viewHandler(w http.ResponseWriter, r *http.Request) {
    title := r.URL.Path[len("/view/"):]
    p, _ := loadPage(title)
    fmt.Fprintf(w, "<h1>%s</h1><div>%s</div>", p.Title, p.Body)
}

Giải thích functions: Đầu tiên, hàm này sẽ lấy giá trị title được truyền vào từ url. giá trị title này truyền đằng sau /view/. Tiếp đến gọi đến hàm loadPage() với parameter là biến title vừa lấy đc. Cuối cùng là in thông tin lên màn hình web của bạn. Để sử dụng được function handle trên. cần viết lại function main() như sau:

func main() {
    http.HandleFunc("/view/", viewHandler)
    http.ListenAndServe(":8080", nil)
}

Mọi việc đã hoàn tất. Để chạy chương trình, trước hết hãy tạo 1 file test.txt với nội dung tùy ý nằm trong thư mục chứa file main.go của bạn. Trên Terminal gõ

$ go run main.go

Truy cập địa chỉ http://localhost:8080/view/test để xem thành quả nhé.             </div>
            
            <div class=

0