Go (hay còn gọi là Golang) là một ngôn ngữ đang khá mới và hot trong cộng đồng lập trình viên tại Việt Nam. Với performance cao (chạy nhanh hơn Node.JS, PHP, Ruby, ... và chỉ chậm hơn C/C++ một chút) cộng với cú pháp đơn giản, sáng sủa, Golang giúp các lập trình viên có thể xây dựng các hệ thống backend hiệu năng cao dễ dàng hơn. Nghe đâu, một trung tâm dạy lập trình có tiếng ở Hà Nội đã chuyển đổi phần backend của toàn bộ trang web từ Node.JS sang Golang. Hôm nay, chúng ta cùng đi tìm hiểu cơ bản về cách viết server với Golang.
1. Kiến trúc
Lập trình web server với Golang thuần, chúng ta sẽ sử dụng package net/http để tạo server (listen port, xử lý request, tạo response...). Ai đã lập trình Node.JS thì thấy nó có sự tương đồng với cách chúng ta sử dụng module http bên Node.JS vậy.
Thư viện net/http cung cấp cho chúng ta một luồng sử lý dữ liệu như hình dưới đây.
Request từ client đến server ban đầu sẽ phải đi qua một thành phần gọi là Multiplexer (tạm dịch là bộ ghép kênh). Bộ ghép kênh sẽ phân tích request (tùy vào url hay là method) và chuyển đến cho Handler thích hợp để xử lý tiếp. Các Handler sẽ là các hàm gọi Database, xử lý logic request tùy theo yêu cầu và trả lại dữ liệu cho Template để hiển thị ra (Template engine như trong Node.JS là ejs hay jade ...). Cuối cùng Multiplexer sẽ trả lại response cho client.
2. Hello world
// hello.go
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, you've requested: %s\n", r.URL.Path)
})
http.ListenAndServe(":3000", nil)
}
- 2 dòng đầu tiên với những bạn đã học Golang sẽ chẳng xa lạ gì. Đó là việc include package main và import 2 thư viện là fmt và net/http.
- Hàm
HandleFunc
của thư việnnet/http
nhận vào 2 đối số là đường dẫn request đến và một function chứa 2 tham số.w http.ResponseWriter
là nơi mà bạn có thể chỉnh sửa response khi trả về client,r *http.Request
chứa các thông tin của request mà client gửi lên server. Đoạn code này nhìn khá tương đồng với bên Express JS.
app.get('/', func (req, res) {
res.send('Hello World!')
});
- Dòng cuối truyền 2 tham số với hàm
ListenAndServe
để mở port, nơi client có thể request tới server. Tham số đầu tiên là port muốn mở, tham số thứ 2 nếu lànil
thì server sẽ sử dụng Multiplexer mặc định trongnet/http
. Nếu bạn muốn sử dụng Multiplexer của bên thứ 3 thì có thể tham khảo gorilla/mux hoặc http/router.
Chạy file
go run hello.go
Mở trình duyệt và truy cập vào địa chỉ http://localhost:3000
3. Sử dụng Multiplexer của bên thứ 3
Như đã đề cập ở phần trước, chúng ta có thể sử dụng Multiplexer của bên thứ 3 cho server Golang thay vì Multiplexer mặc định trong thư viện net/http
. Multiplexer mặc định có thể có thiếu hoặc hạn chế ở một số điểm so với bên thứ 3. Ví dụ sự phức tạp để lấy được các request params trên các url.
Grollia/mux
Instal package grollia/mux
:
go get -u github.com/gorilla/mux
package main
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
)
func main() {
r := mux.NewRouter()
r.HandleFunc("/books/{title}/page/{page}", func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
title := vars["title"]
page := vars["page"]
fmt.Fprintf(w, "You've requested the book: %s on page %s\n", title, page)
})
http.ListenAndServe(":3000", r)
}
- Dòng đầu của hàm main là tạo một instance multiplexer
gorilla/mux
, hàm HandleFunc để xử lý request cũng sẽ gọi qua multiplexer mới thay vìhttp
như dùng multiplexer mặc định - Tham số thứ 2 của hàm
ListenAndServe
cũng truyền vàor
thay vìnil
như phần trên.
http/router
Instal package http/router
:
go get -u github.com/julienschmidt/httprouter
package main
import (
"fmt"
"net/http"
"github.com/julienschmidt/httprouter"
)
func main() {
router := httprouter.New()
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
fmt.Fprint(w, "Welcome!\n")
})
router.GET("/hello/:name", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name"))
})
http.ListenAndServe(":8080", router)
}
- Hơi khác một chút so với 2 multiplexer ở trên.
http/router
xử lý các các request theo các hàm ứng với tên gọi các http method (rất giống với express js). Tham số truyền vào trong callback function cũng là 3 tham số chứ không còn là 2 với dạng. Các lấy params từ request ra cũng khá là "ngọt".
func(http.ResponseWriter, *http.Request, Params)
Chi tiết hơn về các hàm như POST, PUT, DELETE, ... các bạn có thể tham khảo tại đây
4. Thao tác với cơ sở dữ liệu
Chúng ta sẽ demo nho nhỏ về việc Golang thao tác với hệ quản trị cơ sở dữ liệu MongoDB.
Trước tiên, cần cài đặt package mongodb
go get go.mongodb.org/mongo-driver
Kết nối với mongodb
// database.go
package main
import (
"context"
"fmt"
"log"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
type User struct {
username string
password string
}
func main() {
clientOptions := options.Client().ApplyURI("mongodb://localhost:27017/demo")
client, err := mongo.Connect(context.TODO(), clientOptions)
if err != nil {
log.Fatal(err)
}
// Check the connection
err = client.Ping(context.TODO(), nil)
if err != nil {
log.Fatal(err)
}
fmt.Println("Connected to MongoDB!")
}
Cơ bản việc kết nối đến cơ sở dữ liệu với Golang cũng không khác các ngôn ngữ khác là bao, chúng ta đều cần truyền MONGO_URI
vào và sử dụng hàm connect trong thư viện mongo để connect đến database cần tương tác. Đoạn code trên ta có model User
gồm 2 trường username
và password
thể hiện cấu trúc dữ liệu sẽ được lưu vào collection
.
Chạy go run database.go
và kết quả sẽ hiện ra nếu connect thành công
Thao tác 1 chút với dữ liệu
// insert.go
package main
import (
"context"
"fmt"
"log"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
type User struct {
username string
password string
}
func main() {
clientOptions := options.Client().ApplyURI("mongodb://localhost:27017/demo")
client, err := mongo.Connect(context.TODO(), clientOptions)
if err != nil {
log.Fatal(err)
}
// Check the connection
err = client.Ping(context.TODO(), nil)
if err != nil {
log.Fatal(err)
}
fmt.Println("Connected to MongoDB!")
collection := client.Database("demo").Collection("users")
user := User{"conglt", "sdajah1kj2h3hu23h"}
insertResult, err := collection.InsertOne(context.TODO(), user)
if err != nil {
log.Fatal(err)
}
fmt.Println("Inserted a Single Document: ", insertResult.InsertedID)
}
- Sau khi đã kết nối thành công đến mongodb, chúng ta sẽ chỉ định
database
vàcollection
sẽ tương tác trực tiếp với câu lệnh. Nếudatabase
vàcollection
chưa tồn tại thì mongo sẽ tạo mới cho chúng ta.
collection := client.Database("demo").Collection("users")
- 2 dòng tiếp theo sẽ gọi tạo 1 instance của struct User và sau đó gọi hàm
InsertOne
trong thư viện mongo để thêm mới bản ghi vàocollection
.
Thư viện mongo
cung cấp rất nhiều hàm tương tác với cơ sở dữ liệu như thêm, sửa, xóa, ... Chi tiết về cách sử dụng cũng như prototype của các hàm các bạn có thể xem chi tiết ở GoDoc.
Tài liệu tham khảo
Nguồn: https://viblo.asia/p/web-programing-co-ban-voi-golang-jvElarBD5kw