12/08/2018, 16:40

[GO] Làm một trang web đơn giản dùng Go

Chào các bạn, ở 2 phần trước mình đã giới thiệu qua cho các bạn về việc sử dụng Go, hôm nay mình xin được trình bày về việc tạo một trang web đơn giản dùng Go. 1: Tạo project B1: Tạo một thư mục mới để lưu file mkdir golang cd golang B2: Tạo tệp tin có tên hello.go, mở nó trong ...

Chào các bạn, ở 2 phần trước mình đã giới thiệu qua cho các bạn về việc sử dụng Go, hôm nay mình xin được trình bày về việc tạo một trang web đơn giản dùng Go.

1: Tạo project

B1: Tạo một thư mục mới để lưu file

mkdir golang cd golang

B2: Tạo tệp tin có tên hello.go, mở nó trong trình soạn thảo ưa thích của bạn và thêm các dòng sau:

package main

import (
	"fmt"
	"io/ioutil"
)

Chúng ta nhập gói fmt và ioutil từ thư viện chuẩn Go. Sau đó, khi chúng ta thực hiện các chức năng bổ sung, chúng ta sẽ thêm nhiều gói vào đây để sử dụng nếu cần.

2: Cấu trúc dữ liệu

Hãy bắt đầu bằng cách xác định cấu trúc dữ liệu. Một web bao gồm một loạt các Page, mỗi Page có Title và Body (nội dung page). Ở đây, chúng ta xác định Page là một cấu trúc với hai trường đại diện là Title và Body.

type Page struct {
    Title string
    Body  []byte
}

Cấu phần Page mô tả cách dữ liệu trang sẽ được lưu trữ trong bộ nhớ. Nhưng lưu trữ dữ liệu như thế nào? Chúng ta có thể giải quyết vấn đề đó bằng cách tạo một phương thức lưu trên trang:

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

Phương pháp này được nói như sau: "Đây là một phương pháp có tên là save mà nó như là bộ tiếp nhận của p, một con trỏ tới Page. Nó không có tham số và trả về một giá trị của kiểu lỗi." Phương pháp này sẽ lưu Body của trang vào một tập tin văn bản. Để đơn giản, chúng ta sẽ sử dụng Tiêu đề như tên tệp. Số nguyên hệ số 8 0600, được truyền như là tham số thứ ba cho WriteFile, chỉ ra rằng tệp này phải được tạo với quyền read-write cho người dùng hiện tại.

Ngoài các trang save, chúng ta cũng muốn load các trang:

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

Các hàm có thể trả về nhiều giá trị. Các thư viện tiêu chuẩn hàm io.ReadFile trả về []byte và lỗi. Trong loadPage, lỗi vẫn chưa được xử lý; "blank identifier" được biểu thị bằng ký hiệu gạch dưới (_) được sử dụng để bỏ đi giá trị trả về lỗi (về bản chất, gán giá trị value cho nothing). Vậy điều gì xảy ra nếu ReadFile gặp một lỗi? Ví dụ: tệp có thể không tồn tại. Chúng ta không nên bỏ qua những lỗi như vậy. Hãy sửa đổi các chức năng để trở về * Page và lỗi.

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
}

Khi đó, kiểm tra biến thứ 2 được trả ra, nếu là nil thì không có lỗi, khác nil tức là đã có lỗi xảy ra. Hiện giờ chúng ta đã có một cấu trúc dữ liệu đơn giản và khả năng lưu và tải từ một tệp. Chúng ta hãy viết một hàm chính để kiểm tra những gì chúng ta đã viết:

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

Sau đó biên dịch và chạy như sau:

go build hello.go ./hello ->This is a TestPage

3: Giới thiệu gói net / http

Dưới đây là một ví dụ đầy đủ của một máy chủ web đơn giản:

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)
}

Chức năng chính bắt đầu khi call tới http.HandleFunc, nó cho gói http để xử lý tất cả các yêu cầu tới web root ("/") Sau đó nó gọi http.ListenAndServe, xác định rằng nó nên lắng nghe trên cổng 8080 trên bất kỳ giao diện nào ("8080"). (Bây giờ đừng lo lắng về tham số thứ hai của nónil) Chức năng này sẽ chặn cho đến khi chương trình được chấm dứt. r.URL.Path[1:] là giá trị đầu tiên sau localhost:8080(VD: http://localhost:8080/ecec thì r.URL.Path[1:] là ecec) Cách khởi chạy tương tự như ở phần 2:

go build testhttp.go (file template của chúng ta tạm đặt tên là testhttp.go) ./testhttp

-> kết quả:

4: Sử dụng net / http để viết page Hello

Quay trở lại với mục đích ban đầu của chúng ta. chúng ta cần thêm như sau để có thể kết nối http:

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

Hãy tạo một hàm xử lý, viewHandler cho phép người dùng xem một trang của page. Nó sẽ xử lý URL với tiền tố /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)
}

Đầu tiên, chức năng này lấy ra tiêu đề trang từ r.URL.Path, thành path của request URL. Đường dẫn được cắt lát lại với [len("/view/"):] để bỏ /view/ thành component của request path. Để sử dụng, chúng ta viết lại chức năng chính để khởi tạo http bằng cách sử dụng viewHandler để xử lý bất kỳ yêu cầu nào dưới đường dẫn /view/.

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

Hãy tạo một số dữ liệu (VD: test.txt), biên dịch mã của chúng ta, và thử chạy 1 trang. Mở tệp test.txt trong trình soạn thảo của bạn, và lưu chuỗi Ec ec. Sau đó chạy thử:

go build hello.go ./hello

Sau đó run url http://localhost:8080/view/test (test là file test.txt các bạn mới tạo) và kết quả là:

Kết luận

Trên đây mình đã giới thiệu và giải thích chi tiết về việc tạo ra một trang web đơn giản dùng Go. Nếu có bất kì thắc mắc nào hoặc thiếu sót của bài vui lòng để lại comment để mình có thể rút kinh nghiệm hơn. Cảm ơn các bạn đã đọc.

0