Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

การพัฒนาเว็บใน Go:Middleware, Templating, Databases &Beyond

ในบทความก่อนหน้านี้ในชุดนี้ เราได้พูดคุยกันอย่างกว้างขวางเกี่ยวกับ Gonet/http แพ็คเกจและวิธีการใช้สำหรับเว็บแอปพลิเคชันที่พร้อมสำหรับการผลิต เราเน้นที่ด้านการกำหนดเส้นทางและลักษณะเฉพาะอื่นๆ ของhttp.ServeMuxเป็นส่วนใหญ่ ชนิด

บทความนี้จะปิดการสนทนาใน ServeMux โดยการสาธิตวิธีใช้งานฟังก์ชันมิดเดิลแวร์กับเราเตอร์เริ่มต้นและแนะนำแพ็คเกจไลบรารีมาตรฐานอื่นๆ ที่แน่ใจว่าจะมีประโยชน์เมื่อพัฒนาบริการเว็บด้วย Go

มิดเดิลแวร์ใน Go

วิธีปฏิบัติในการตั้งค่าฟังก์ชันที่ใช้ร่วมกันซึ่งต้องเรียกใช้สำหรับคำขอ HTTP แบบปากเปล่าจำนวนมากเรียกว่า มิดเดิลแวร์ . การดำเนินการบางอย่าง เช่น การตรวจสอบสิทธิ์ การบันทึก และการตรวจสอบคุกกี้ มักถูกนำไปใช้เป็นฟังก์ชันมิดเดิลแวร์ ซึ่งดำเนินการตามคำขออย่างเป็นอิสระก่อนหรือหลังตัวจัดการเส้นทางปกติ

ในการใช้งานมิดเดิลแวร์ใน Go คุณต้องแน่ใจว่าคุณมีประเภทที่ตรงกับอินเทอร์เฟซ http.Handler โดยปกติหมายความว่าคุณต้องแนบเมธอดที่มีลายเซ็นServeHTTP(http.ResponseWriter, *http.Request) กับประเภท เมื่อใช้เมธอดนี้ ทุกประเภทจะตอบสนอง http.Handler อินเทอร์เฟซ

นี่เป็นตัวอย่างง่ายๆ:

package main

import "net/http"

type helloHandler struct {
    name string
}

func (h helloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello " + h.name))
}

func main() {
    mux := http.NewServeMux()

    helloJohn := helloHandler{name: "John"}
    mux.Handle("/john", helloJohn)
    http.ListenAndServe(":8080", mux)
}

คำขอใด ๆ ที่ส่งไปยัง /john เส้นทางจะถูกส่งตรงไปยังhelloHandler.ServeHTTP กระบวนการ. คุณสามารถสังเกตการทำงานนี้ได้โดยเริ่มต้นเซิร์ฟเวอร์และไปที่ https://localhost:8080/john

ต้องเพิ่ม ServeHTTP เมธอดเป็นประเภทกำหนดเองทุกครั้งที่คุณต้องการปรับใช้ http.Handler จะค่อนข้างน่าเบื่อ ดังนั้น net/http packageprovides http.HandlerFunc ซึ่งอนุญาตให้ใช้ฟังก์ชันทั่วไปเป็นตัวจัดการ HTTP

สิ่งที่คุณต้องทำคือตรวจสอบให้แน่ใจว่าฟังก์ชันของคุณมีลายเซ็นต่อไปนี้:func(http.ResponseWriter, *http.Request); จากนั้นแปลงเป็น http.HandlerFunc ชนิด

package main

import "net/http"

func helloJohnHandler(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello John"))
}

func main() {
    mux := http.NewServeMux()
    mux.Handle("/john", http.HandlerFunc(helloJohnHandler))
    http.ListenAndServe(":8080", mux)
}

คุณยังสามารถแทนที่ mux.Handle บรรทัดใน main ฟังก์ชั่นด้านบนด้วยmux.HandleFunc และส่งฟังก์ชันไปที่มันโดยตรง เราใช้รูปแบบนี้โดยเฉพาะในบทความที่แล้ว

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/john", helloJohnHandler)
    http.ListenAndServe(":8080", mux)
}

ณ จุดนี้ ชื่อถูกฮาร์ดโค้ดลงในสตริง ไม่เหมือนเมื่อก่อนเมื่อเราตั้งชื่อใน main ก่อนเรียกตัวจัดการ หากต้องการลบข้อจำกัดนี้ เราสามารถปิดตรรกะของตัวจัดการได้ดังที่แสดงด้านล่าง:

package main

import "net/http"

func helloHandler(name string) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello " + name))
    })
}

func main() {
    mux := http.NewServeMux()
    mux.Handle("/john", helloHandler("John"))
    http.ListenAndServe(":8080", mux)
}

helloHandler ฟังก์ชันเองไม่เป็นไปตาม http.Handler ส่วนต่อประสาน แต่สร้างและส่งคืนฟังก์ชันที่ไม่ระบุชื่อที่ทำ ฟังก์ชันนี้ปิดทับ name พารามิเตอร์ ซึ่งหมายความว่าสามารถเข้าถึงได้เมื่อถูกเรียก ณ จุดนี้ helloHandler สามารถใช้ซ้ำได้หลายชื่อตามความจำเป็น

ทั้งหมดนี้เกี่ยวข้องกับมิดเดิลแวร์อย่างไร การสร้างฟังก์ชันมิดเดิลแวร์ทำได้ในลักษณะเดียวกับที่เราได้เห็นด้านบน แทนที่จะส่งสตริงไปยังส่วนปิด (ดังในตัวอย่าง) เราสามารถส่งตัวจัดการถัดไปในสายโซ่เป็นอาร์กิวเมนต์ได้

นี่คือรูปแบบที่สมบูรณ์:

func middleware(next http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    // Middleware logic goes here...
    next.ServeHTTP(w, r)
  })
}

middleware ฟังก์ชันด้านบนยอมรับตัวจัดการและส่งคืนตัวจัดการ โปรดสังเกตว่าเราสามารถทำให้ฟังก์ชันที่ไม่ระบุตัวตนตอบสนอง http.Handler ได้อย่างไร โดยการแคสต์ไปที่ http.HandlerFunc พิมพ์. เมื่อสิ้นสุดฟังก์ชันนิรนาม การควบคุมจะถูกโอนไปยัง next ตัวจัดการโดยเรียกใช้ServeHTTP() กระบวนการ. หากคุณต้องการส่งค่าระหว่างตัวจัดการ เช่น ID ของผู้ใช้ที่ตรวจสอบสิทธิ์ คุณสามารถใช้ http.Request.Context() วิธีการแนะนำใน Go 1.7

มาเขียนฟังก์ชันมิดเดิลแวร์ที่แสดงให้เห็นรูปแบบนี้ง่ายๆ กัน ฟังก์ชันนี้เพิ่มคุณสมบัติที่เรียกว่า requestTime ไปยังออบเจกต์คำขอซึ่งใช้โดย helloHandler เพื่อแสดงการประทับเวลาของคำขอ

package main

import (
    "context"
    "net/http"
    "time"
)

func requestTime(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        ctx := r.Context()
        ctx = context.WithValue(ctx, "requestTime", time.Now().Format(time.RFC3339))
        r = r.WithContext(ctx)
        next.ServeHTTP(w, r)
    })
}

func helloHandler(name string) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        responseText := "<h1>Hello " + name + "</h1>"

        if requestTime := r.Context().Value("requestTime"); requestTime != nil {
            if str, ok := requestTime.(string); ok {
                responseText = responseText + "\n<small>Generated at: " + str + "</small>"
            }
        }
        w.Write([]byte(responseText))
    })
}

func main() {
    mux := http.NewServeMux()
    mux.Handle("/john", requestTime(helloHandler("John")))
    http.ListenAndServe(":8080", mux)
}

การพัฒนาเว็บใน Go:Middleware, Templating, Databases &Beyond

เนื่องจากฟังก์ชันมิดเดิลแวร์ของเรายอมรับและส่งกลับ http.Handler พิมพ์ มันเป็นไปได้ที่จะสร้างห่วงโซ่ที่ไม่มีที่สิ้นสุดของฟังก์ชันมิดเดิลแวร์ที่ซ้อนกันอยู่ภายในกันและกัน

ตัวอย่างเช่น

mux := http.NewServeMux()
mux.Handle("/", middleware1(middleware2(appHandler)))

คุณสามารถใช้ห้องสมุดเช่น Alice เพื่อแปลงโครงสร้างด้านบนให้อยู่ในรูปแบบที่อ่านง่ายยิ่งขึ้น เช่น:

alice.New(middleware1, middleware2).Then(appHandler)

เทมเพลต

แม้ว่าการใช้เทมเพลตจะลดลงเมื่อมีแอปพลิเคชันหน้าเดียว แต่ยังคงเป็นส่วนสำคัญของโซลูชันการพัฒนาเว็บที่สมบูรณ์

Go มีแพ็คเกจสองชุดสำหรับทุกความต้องการในการสร้างแม่แบบของคุณ:text/template และhtml/template . ทั้งคู่มีอินเทอร์เฟซเดียวกัน แต่ส่วนหลังจะทำการเข้ารหัสลับเบื้องหลังเพื่อป้องกันการเจาะระบบโค้ดปลอม

แม้ว่าเทมเพลต Go จะไม่ได้สื่อถึงความรู้สึกได้มากที่สุด แต่ก็สามารถทำงานให้เสร็จลุล่วงได้ดีและสามารถใช้สำหรับแอปพลิเคชันที่ใช้งานจริงได้ อันที่จริงแล้ว Hugo ซึ่งเป็นเครื่องมือสร้างไซต์แบบสแตติกยอดนิยมนั้นใช้ระบบการสร้างเทมเพลตเป็นหลัก

เรามาดูกันว่า html/template . เป็นอย่างไร แพ็กเกจอาจใช้เพื่อส่งเอาต์พุต HTML เพื่อตอบสนองต่อคำขอของเว็บ

การสร้างเทมเพลต

สร้าง index.html ไฟล์ในไดเร็กทอรีเดียวกับ main.go . ของคุณ file และเพิ่มรหัสต่อไปนี้ลงในไฟล์:

<ul>
  {{ range .TodoItems }}
  <li>{{ . }}</li>
  {{ end }}
</ul>

ถัดไป เพิ่มรหัสต่อไปนี้ใน main.go . ของคุณ ไฟล์:

package main

import (
    "html/template"
    "log"
    "os"
)

func main() {
    t, err := template.ParseFiles("index.html")
    if err != nil {
        log.Fatal(err)
    }

    todos := []string{"Watch TV", "Do homework", "Play games", "Read"}

    err = t.Execute(os.Stdout, todos)
    if err != nil {
        log.Fatal(err)
    }
}

หากคุณรันโปรแกรมด้านบนด้วย go run main.go . คุณควรเห็นผลลัพธ์ต่อไปนี้:

<ul>
  <li>Watch TV</li>
  <li>Do homework</li>
  <li>Play games</li>
  <li>Read</li>
</ul>

ยินดีด้วย! คุณเพิ่งสร้างเทมเพลต Go แรกของคุณ นี่คือคำอธิบายสั้น ๆ ของไวยากรณ์ที่เราใช้ในไฟล์เทมเพลต:

  • Go ใช้วงเล็บปีกกาคู่ ({{ และ }} ) เพื่อกำหนดขอบเขตการประเมินข้อมูลและโครงสร้างการควบคุม (เรียกว่า การกระทำ ) ในเทมเพลต
  • ช่วง range การกระทำคือวิธีที่เราสามารถทำซ้ำโครงสร้างข้อมูล เช่น สไลซ์
  • . แสดงถึงบริบทปัจจุบัน ใน range การกระทำ บริบทปัจจุบันคือส่วนของ todos . ภายในบล็อก {{ . }} หมายถึงแต่ละองค์ประกอบในสไลซ์

ใน main.go ไฟล์ template.ParseFiles เมธอดใช้ในการสร้างเทมเพลตใหม่จากไฟล์ตั้งแต่หนึ่งไฟล์ขึ้นไป เทมเพลตนี้จะดำเนินการในภายหลังโดยใช้ template.Execute กระบวนการ; ต้องใช้ io.Writer และข้อมูลที่จะนำไปใช้กับเทมเพลต

ในตัวอย่างข้างต้น เทมเพลตถูกเรียกใช้งานไปยังเอาต์พุตมาตรฐาน แต่เราสามารถรันไปยังปลายทางใดก็ได้ ตราบใดที่ตรงตาม io.Writer อินเตอร์เฟซ. ตัวอย่างเช่น หากคุณต้องการส่งคืนผลลัพธ์โดยเป็นส่วนหนึ่งของคำขอเว็บ สิ่งที่คุณต้องทำคือเรียกใช้เทมเพลตไปยัง ResponseWriter อินเทอร์เฟซดังที่แสดงด้านล่าง

package main

import (
    "html/template"
    "log"
    "net/http"
)

func main() {
    t, err := template.ParseFiles("index.html")
    if err != nil {
        log.Fatal(err)
    }

    todos := []string{"Watch TV", "Do homework", "Play games", "Read"}

    http.HandleFunc("/todos", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "text/html")
        err = t.Execute(w, todos)
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
        }
    })
    http.ListenAndServe(":8080", nil)
}

การพัฒนาเว็บใน Go:Middleware, Templating, Databases &Beyond

ส่วนนี้มีขึ้นเพื่อเป็นการแนะนำสั้นๆ เกี่ยวกับแพ็คเกจเทมเพลตของ Go อย่าลืมตรวจสอบเอกสารประกอบสำหรับข้อความ/เทมเพลต และhtml/เทมเพลต หากคุณสนใจกรณีการใช้งานที่ซับซ้อนมากขึ้น

หากคุณไม่ได้ชื่นชอบวิธีการสร้างเทมเพลตของ Go คุณจะมีตัวเลือกอื่น เช่น ห้องสมุด Plush

การทำงานกับ JSON

หากคุณต้องการทำงานกับออบเจ็กต์ JSON คุณจะยินดีที่ทราบว่าไลบรารีมาตรฐานของ Go มีทุกสิ่งที่จำเป็นในการแยกวิเคราะห์และเข้ารหัส JSON ผ่าน encoding/json แพ็คเกจ

ประเภทเริ่มต้น

เมื่อเข้ารหัสหรือถอดรหัสวัตถุ JSON ใน Go จะมีการใช้ประเภทต่อไปนี้:

  • bool สำหรับบูลีน JSON
  • float64 สำหรับหมายเลข JSON
  • string สำหรับสตริง JSON
  • nil สำหรับ JSON เป็นโมฆะ
  • map[string]interface{} สำหรับวัตถุ JSON และ
  • []interface{} สำหรับอาร์เรย์ JSON

การเข้ารหัส

ในการเข้ารหัสโครงสร้างข้อมูลเป็น JSON json.Marshal มีการใช้ฟังก์ชัน นี่คือตัวอย่าง:

package main

import (
    "encoding/json"
    "fmt"
)

type Person struct {
    FirstName string
    LastName  string
    Age       int
    email     string
}

func main() {
    p := Person{
        FirstName: "Abraham",
        LastName:  "Freeman",
        Age:       100,
        email:     "[email protected]",
    }

    json, err := json.Marshal(p)
    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Println(string(json))
}

ในโปรแกรมด้านบนนี้ เรามี Person struct มีสี่ฟิลด์ที่แตกต่างกัน ใน main ฟังก์ชัน ตัวอย่าง Person ถูกสร้างขึ้นด้วยฟิลด์ทั้งหมดเริ่มต้น json.Marshal จะใช้เมธอดเพื่อแปลง p โครงสร้างเป็น JSON เมธอดนี้ส่งคืนส่วนของไบต์หรือข้อผิดพลาด ซึ่งเราต้องจัดการก่อนเข้าถึงข้อมูล JSON

ในการแปลงส่วนของไบต์เป็นสตริงใน Go เราจำเป็นต้องทำการแปลงประเภทดังที่แสดงไว้ด้านบน การรันโปรแกรมนี้จะสร้างผลลัพธ์ดังต่อไปนี้:

{"FirstName":"Abraham","LastName":"Freeman","Age":100}

อย่างที่คุณเห็น เราได้รับอ็อบเจ็กต์ JSON ที่ถูกต้องซึ่งสามารถใช้ได้ในทุกวิถีทาง โปรดทราบว่า email ฟิลด์ถูกปล่อยออกจากผลลัพธ์ นี่เป็นเพราะไม่ได้ส่งออกจาก Person วัตถุโดยอาศัยการขึ้นต้นด้วยตัวพิมพ์เล็ก

โดยค่าเริ่มต้น Go จะใช้ชื่อคุณสมบัติเดียวกันใน struct เป็นชื่อฟิลด์ในวัตถุ JSON นั้น อย่างไรก็ตาม สิ่งนี้สามารถเปลี่ยนแปลงได้โดยใช้แท็ก structfield

type Person struct {
    FirstName string `json:"first_name"`
    LastName  string `json:"last_name"`
    Age       int    `json:"age"`
    email     string `json:"email"`
}

แท็กฟิลด์ struct ด้านบนระบุว่าตัวเข้ารหัส JSON ควรจับคู่ FirstName คุณสมบัติใน struct เป็น first_name ฟิลด์ในวัตถุ JSON เป็นต้น การเปลี่ยนแปลงในตัวอย่างก่อนหน้านี้ทำให้เกิดผลลัพธ์ดังต่อไปนี้:

{"first_name":"Abraham","last_name":"Freeman","age":100}

ถอดรหัส

json.Unmarshal ฟังก์ชั่นใช้สำหรับถอดรหัสวัตถุ JSON เป็น Gostruct โดยมีลายเซ็นดังต่อไปนี้:

func Unmarshal(data []byte, v interface{}) error

ยอมรับข้อมูล JSON หนึ่งส่วนและสถานที่สำหรับจัดเก็บข้อมูลที่ถอดรหัส หากถอดรหัสสำเร็จ ข้อผิดพลาดที่ส่งคืนจะเป็น nil .

สมมติว่าเรามีวัตถุ JSON ต่อไปนี้

json := "{"first_name":"John","last_name":"Smith","age":35, "place_of_birth": "London", gender:"male"}"

เราสามารถถอดรหัสเป็น Person struct ดังที่แสดงด้านล่าง:

func main() {
    b := `{"first_name":"John","last_name":"Smith","age":35, "place_of_birth": "London", "gender":"male", "email": "[email protected]"}`
    var p Person
    err := json.Unmarshal([]byte(b), &p)
    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Printf("%+v\n", p)
}

และคุณจะได้ผลลัพธ์ดังต่อไปนี้:

{FirstName:John LastName:Smith Age:35 email:}

Unmarshal ถอดรหัสเฉพาะฟิลด์ที่พบในประเภทปลายทาง ในกรณีนี้ place_of_birth และ gender จะถูกละเว้นเนื่องจากไม่ได้จับคู่กับฟิลด์ struct ใดๆ ใน Person . ลักษณะการทำงานนี้สามารถใช้ประโยชน์เพื่อเลือกเฉพาะบางฟิลด์เฉพาะจากออบเจ็กต์ JSON ขนาดใหญ่ เช่นเคย ฟิลด์ที่ไม่ถูกเอ็กซ์พอร์ตในโครงสร้างปลายทางจะไม่ได้รับผลกระทบแม้ว่าจะมีฟิลด์ที่สอดคล้องกันในออบเจ็กต์ JSON ก็ตาม นั่นเป็นเหตุผลที่ email ยังคงเป็นสตริงว่างในผลลัพธ์แม้ว่าจะมีอยู่ในวัตถุ JSON

ฐานข้อมูล

database/sql แพ็คเกจมีอินเทอร์เฟซทั่วไปรอบฐานข้อมูล SQL (หรือเหมือน SQL) ต้องใช้ร่วมกับโปรแกรมควบคุมฐานข้อมูล เช่น รายการที่ระบุไว้ที่นี่ เมื่อนำเข้าไดรเวอร์ฐานข้อมูล คุณต้องนำหน้าด้วยเครื่องหมายขีดล่าง _ เพื่อเริ่มต้น

ตัวอย่างเช่น นี่คือวิธีใช้แพ็คเกจ MySQLdriver กับ database/sql :

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
)

ภายใต้ประทุน ไดรเวอร์จะลงทะเบียนตัวเองว่าพร้อมใช้งานสำหรับdatabase/sql แพ็คเกจ แต่จะไม่ถูกใช้โดยตรงในรหัสของเรา วิธีนี้ช่วยลดการพึ่งพาไดรเวอร์เฉพาะเพื่อให้สามารถสลับไปใช้ไดรเวอร์อื่นได้อย่างง่ายดายโดยใช้ความพยายามเพียงเล็กน้อย

การเปิดการเชื่อมต่อฐานข้อมูล

ในการเข้าถึงฐานข้อมูล คุณต้องสร้าง sql.DB วัตถุดังที่แสดงด้านล่าง:

func main() {
    db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/hello")
    if err != nil {
        log.Fatal(err)
    }
}

sql.Open วิธีการเตรียมฐานข้อมูลที่เป็นนามธรรมเพื่อใช้ในภายหลัง มันไม่ได้สร้างการเชื่อมต่อกับฐานข้อมูลหรือตรวจสอบพารามิเตอร์การเชื่อมต่อ หากคุณต้องการให้แน่ใจว่าฐานข้อมูลพร้อมใช้งานและสามารถเข้าถึงได้ทันที ให้ใช้ db.Ping() วิธีการ:

err = db.Ping()
if err != nil {
  log.Fatal(err)
}

การปิดการเชื่อมต่อฐานข้อมูล

หากต้องการปิดการเชื่อมต่อฐานข้อมูล คุณสามารถใช้ db.Close() . โดยปกติคุณต้องการdefer การปิดฐานข้อมูลจนกว่าฟังก์ชันที่เปิดฐานข้อมูลการเชื่อมต่อจะสิ้นสุดลง โดยปกติแล้วจะเป็น main ฟังก์ชัน:

func main() {
    db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/hello")
    if err != nil {
        log.Fatal(err)
    }
  defer db.Close()
}

sql.DB วัตถุถูกออกแบบมาให้มีอายุยืนยาว ดังนั้นคุณจึงไม่ควรเปิดและปิดบ่อยๆ หากคุณทำเช่นนั้น คุณอาจประสบปัญหา เช่น การใช้ซ้ำที่ไม่ดีและการแบ่งปันการเชื่อมต่อ การใช้ทรัพยากรเครือข่ายไม่เพียงพอ ความล้มเหลวเป็นระยะๆ ทางที่ดีควรส่ง sql.DB วิธีการหรือทำให้ใช้ได้ทั่วโลกและปิดเฉพาะเมื่อโปรแกรมเข้าถึงที่เก็บข้อมูลนั้นเสร็จแล้ว

กำลังดึงข้อมูลจากฐานข้อมูล

การสอบถามตารางสามารถทำได้ในสามขั้นตอน ขั้นแรก เรียก db.Query() . จากนั้นวนซ้ำในแถว สุดท้าย ใช้ rows.Scan() เพื่อแยกแต่ละแถวเป็นตัวแปรไม่คงที่ นี่คือตัวอย่าง:

var (
    id int
    name string
)

rows, err := db.Query("select id, name from users where id = ?", 1)
if err != nil {
    log.Fatal(err)
}

defer rows.Close()

for rows.Next() {
    err := rows.Scan(&id, &name)
    if err != nil {
        log.Fatal(err)
    }

    log.Println(id, name)
}

err = rows.Err()
if err != nil {
    log.Fatal(err)
}

หากการสืบค้นส่งคืนแถวเดียว คุณสามารถใช้ db.QueryRow เมธอดแทน db.Query และหลีกเลี่ยงโค้ดสำเร็จรูปที่มีความยาวบางส่วนในโค้ดก่อนหน้า:

var (
    id int
    name string
)

err = db.QueryRow("select id, name from users where id = ?", 1).Scan(&id, &name)
if err != nil {
    log.Fatal(err)
}

fmt.Println(id, name)

ฐานข้อมูล NoSQL

Go ยังรองรับฐานข้อมูล NoSQL เป็นอย่างดี เช่น Redis, MongoDB, Cassandra และอื่นๆ แต่ไม่มีอินเทอร์เฟซมาตรฐานสำหรับการทำงานกับพวกมัน คุณจะต้องพึ่งพาแพ็คเกจไดรเวอร์ทั้งหมดสำหรับฐานข้อมูลเฉพาะ ตัวอย่างบางส่วนอยู่ด้านล่าง

  • https://github.com/go-redis/redis (ไดรเวอร์ Redis)
  • https://github.com/mongodb/mongo-go-driver (ไดรเวอร์ MongoDB)
  • https://github.com/gocql/gocql (ไดรเวอร์ Cassandra)
  • https://github.com/Shopify/sarama (ไดรเวอร์ Apache Kafka)

สรุป

ในบทความนี้ เราได้พูดถึงประเด็นสำคัญบางประการในการสร้างเว็บแอปพลิเคชันด้วย Go ตอนนี้คุณควรจะสามารถเข้าใจได้ว่าทำไม Goprogrammers หลายคนจึงสาบานด้วยไลบรารีมาตรฐาน มีความครอบคลุมมากและจัดเตรียมเครื่องมือที่จำเป็นสำหรับบริการที่พร้อมสำหรับการผลิต

หากคุณต้องการคำชี้แจงเกี่ยวกับสิ่งที่เรากล่าวถึงที่นี่ โปรดส่งข้อความถึงฉันทาง Twitter ในบทความถัดไปและสุดท้ายในชุดนี้ เราจะพูดถึง go เครื่องมือและวิธีใช้เพื่อจัดการกับงานทั่วไปในระหว่างการพัฒนาด้วย Go

ขอบคุณสำหรับการอ่านและขอให้สนุกกับการเขียนโค้ด!