編程基礎 0004_Web_beego開發

beego 開始 2

文章的添加與刪除

  • 創建 TopicController
// controllers中添加topic.go
package controllers

import "github.com/astaxie/beego"

type TopicController struct {
    beego.Controller
}

func (c *TopicController) Get() {
    c.Data["Title"] = "文章"
    c.Data["IsTopic "] = true
    c.TplName = "topic.html"
}
//路由routers中添加
beego.Router("/topic", &controllers.TopicController{})
// beego有一個自動路由功能
beego.AutoRouter(&controllers.TopicController{})

beego 就會通過反射獲取結構體中所有實現的方法,你就可以通過如下的方式訪問到對應的方法。

  • /object/login 調用 ObjectController 中的 Login 方法
  • /object/logout 調用 ObjectController 中的 Logout 方法

除了前綴兩個/:controller/:method的匹配之外,剩下的 url beego 會幫你自動化解析為參數,保存在 this.Ctx.Input.Params 中,如下

/object/blog/2013/09/12 調用 ObjectController 中的 Blog 方法,參數是 map[0:2013,1:09,2:12]

現在已經可以通過自動識別出來下面類似的所有 url,都會把請求分發到 controller 的 simple 方法, 可以通過c.Ctx.Input.Param(":ext")獲取後綴名

/controller/simple
/controller/simple.html
/controller/simple.json
/controller/simple.xml

文章添類別添加時有個小坑(chrome 瀏覽器)chrome 有防止表單重復提交,如果你在該頁面上之前添加過一個類別,刪掉後再添加後出現,無法響應添加的問題,這是因為 chrome 認為你在重復提交。解決方法就是加個 token。為了不污染接口可以加錨點

go 原生表單處理

package main

import (
    "fmt"
    "html/template"
    "net/http"
)
const tpl = `
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<form action="/" method="post">
  name <input type="text" name="username"><br/>
  pass <input type="password" name="pwd">
  <button type="submit">提交</button>
</form>
</body>
</html>
`
func main() {
    http.HandleFunc("/", SimpleForm)
    http.ListenAndServe(":9898", nil)
}

func SimpleForm(w http.ResponseWriter, r *http.Request) {
  if r.Method == "GET" {
      fmt.Println(r.Method)
      t:=template.New("hey")
      t.Parse(tpl)
      t.Execute(w,nil)
  } else {
      fmt.Println(r.FormValue("username"))
      fmt.Println(r.FormValue("pwd"))
  }
}

beego 文件上傳

附件上傳時注意表單的enctype="multipart/form-data",AddTopic 和 ModifyTopic 兩個 controller 中添加相關字段

func (c *TopicController) Post() {
    if !checkAccount(c.Ctx) {
        c.Redirect("/login", 302)
        return
    }
    //添加還是修改
    title := c.Input().Get("title")
    content := c.Input().Get("content")
    tid := c.Input().Get("tid")
    cate := c.Input().Get("cate")
    labels := c.Input().Get("labels")
    // ========上傳================
    //是否文件上傳
    _, fh, err := c.GetFile("attachment") //fh filehandler
    if err != nil {
        beego.Error(err)
    }
    var attachment string
    if fh != nil {
        //
        attachment = fh.Filename
        beego.Info(attachment)
        err=c.SaveToFile("attachment",path.Join("attachment",attachment))
    }
    //=========上傳結束(注意修改時要如果上傳新的,要把舊的刪除)==================
    if len(tid) == 0 {
        err = models.AddTopic(title, cate, labels, content,attachment)
    } else {
        err = models.ModifyTopic(tid, cate, title, labels, content,attachment)
    }

    if err != nil {
        beego.Error(err)
        return
    }
    c.Redirect("/topic", 302)
}

原生寫法

func (c *Controller) SaveToFile(fromfile, tofile string) error {
    file, _, err := c.Ctx.Request.FormFile(fromfile)
    if err != nil {
        return err
    }
    defer file.Close()
    f, err := os.OpenFile(tofile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
    if err != nil {
        return err
    }
    defer f.Close()
    io.Copy(f, file)
    return nil
}

國際化支持

bee new beegoi18n
cd beegoi18n && bee run
go get github.com/beego/i18n
dep ensure -add github.com/beego/i18n
//嵌套路由
package controllers

import (
    "github.com/astaxie/beego"
    "github.com/beego/i18n"
)

//創建base 替代beego.Controller
type baseController struct {
    beego.Controller
    i18n.Locale
}

//自己實現了Prepare()
//指定語言
//註冊本地化文件 local_en-US.ini 或 local_zh-CN.ini
func (this *baseController) Prepare() {
    //重寫
    lang := this.GetString("lang")
    if lang == "zh-CN" {
        this.Lang = lang
    } else {
        this.Lang = "en-US"
    }
}

type MainController struct {
    baseController
}

func (c *MainController) Get() {
    c.Data["Website"] = "beego.me"
    c.Data["Email"] = "astaxie@gmail.com"
    c.Data["Hi"] = "hi"
    c.Data["Bye"]="bye"
    c.TplName = "index.tpl"
}
// main.go中註冊

func main() {
    i18n.SetMessage("zh-CN","conf/local_zh-CN.ini")
    i18n.SetMessage("en-US","conf/locale_en-US.ini")
    //然後可以在控制器中Tr或在模板中Tr
    // 在模板中使用要註冊一下模板函數
    beego.Run()
}

分區功能

about=About
[about]
about=About Us
{{i18n .Lang .About}} <br />
{{i18n .Lang .about.about}}

文檔

自建 HTTP 中間件

  • 以類型形式
  • 以函數形式
  • 追回響應內容
  • 自定義響應內容
// 以類型的形式
package main

import "net/http"

type SingleHost struct {
    handler     http.Handler
    allowedHost string
}

func (this *SingleHost) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if r.Host == this.allowedHost {
        this.handler.ServeHTTP(w, r)
    } else {
        w.WriteHeader(403)
    }
}

func myHandler(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("hello World!"))
}

func main() {
    single := &SingleHost{
        handler:     http.HandlerFunc(myHandler),
        allowedHost: "example.com",
    }
    http.ListenAndServe(":8080", single)
}
// 以函數的形式
package main

import "net/http"

func SingleHost(handler http.Handler, allowedHost string) http.Handler {
    fn := func(w http.ResponseWriter, r *http.Request) {
        if r.Host == allowedHost {
            handler.ServeHTTP(w, r)
        } else {
            w.WriteHeader(403)
        }
    }
    return http.HandlerFunc(fn)
}

//正常處理的handler
func myHandler(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("hello world"))
}

func main() {
    single := SingleHost(http.HandlerFunc(myHandler), "localhost:8080")
    http.ListenAndServe(":8080", single)
}
package main

import "net/http"

type AppendMiddleware struct {
    handler http.Handler
}

func (this *AppendMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    this.handler.ServeHTTP(w, r) //正常響應
    w.Write([]byte("Hey, this is middleware!"))
}

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

func main() {
    mid := &AppendMiddleware{http.HandlerFunc(myHandler)}
    http.ListenAndServe(":8080", mid)
}
package main

import (
    "net/http"
    "net/http/httptest"
)

type ModifierMiddleware struct {
    handler http.Handler
}

func (this *ModifierMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    recoder := httptest.NewRecorder()  //它的作用是虛擬響應,不是真正響應,獲取正常的響應結果,
    this.handler.ServeHTTP(recoder, r) //記錄到recoder
    for k, v := range recoder.Header() {
        w.Header()[k] = v
    }
    //    自定義的一些操作
    w.Header().Set("go-web-foundation", "vip")
    w.WriteHeader(418)
    w.Write([]byte("hey,this is middleware! "))
    //
    w.Write(recoder.Body.Bytes())
}

func myHandler(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("hello world"))
}

func main() {
    mid := ModifierMiddleware{http.HandlerFunc(myHandler)}
    http.ListenAndServe(":8080", &mid)
}

//curl --head "localhost:8080"

簡易 RPC 實現

  • 介紹一個 go--go 的通信
  • http rpc
  • tcp rpc
  • json rpc
// 基於http
// server.go
package main

import (
    "fmt"
    "github.com/kataras/iris/core/errors"
    "net/http"
    "net/rpc"
)

type Args struct {
    A, B int //首字母要大寫
}

type Math int

func (m *Math) Multiply(args *Args, reply *int) error {
    *reply = args.A * args.B
    return nil
}

//除法
type Quotient struct {
    Quo, Rem int
}

func (m *Math) Divide(args *Args, quo *Quotient) error {
    if args.B == 0 {
        return errors.New("divide by zero")
    }
    quo.Quo = args.A / args.B
    quo.Rem = args.A % args.B
    return nil
}

func main() {
    math := new(Math)
    rpc.Register(math)
    rpc.HandleHTTP()
    err := http.ListenAndServe(":1234", nil)
    if err != nil {
        fmt.Println(err.Error())
    }
}
//client.og 類型這裡可以抽成公共引用
package main

import (
    "fmt"
    "log"
    "net/rpc"
    "os"
)


type Args struct {
    A, B int //首字母要大寫
}

//除法
type Quotient struct {
    Quo, Rem int
}

func main() {
    if len(os.Args) != 2 {
        fmt.Println("Usage:", os.Args[0], "server")
        os.Exit(1)
    }
    serverAddr := os.Args[1]
    client, err := rpc.DialHTTP("tcp", serverAddr+":1234")
    if err != nil {
        log.Fatal("dialog:", err)
    }
    args := Args{17, 8}
    var reply int
    err = client.Call("Math.Multiply", args, &reply)
    if err != nil {
        log.Fatal("Math error:", err)
    }
    fmt.Println("Math:%d*%d=%d\n", args.A, args.B, reply)

    var quo Quotient
    err = client.Call("Math.Divide", args, &quo)
    if err != nil {
        log.Fatal("Math error:", err)
    }
    fmt.Println("Math:%d/%d=%d\n", args.A, args.B, quo.Quo, quo.Rem)

}

命令行下運行go run http_rpc_client.go localhost

基於 tcp 的

// server
package main

import (
    "fmt"
    "github.com/kataras/iris/core/errors"
    "net"
    "net/rpc"
    "os"
)

type Args struct {
    A, B int //首字母要大寫
}

type Math int

func (m *Math) Multiply(args *Args, reply *int) error {
    *reply = args.A * args.B
    return nil
}

//除法
type Quotient struct {
    Quo, Rem int
}

func (m *Math) Divide(args *Args, quo *Quotient) error {
    if args.B == 0 {
        return errors.New("divide by zero")
    }
    quo.Quo = args.A / args.B
    quo.Rem = args.A % args.B
    return nil
}

func main() {
    math := new(Math)
    rpc.Register(math)
    //rpc.HandleHTTP()
    //err := http.ListenAndServe(":1234", nil)
    //if err != nil {
    //    fmt.Println(err.Error())
    //}
    /*
       tpc的rpc
    */
    tcpAddr, err := net.ResolveTCPAddr("tcp", ":1234")
    if err != nil {
        fmt.Println("Fatal error:", err)
        os.Exit(2)
    }
    listener, err := net.ListenTCP("tcp", tcpAddr)
    if err != nil {
        fmt.Println("Fatal error:", err)
        os.Exit(2)
    }
    for {
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("conn error:", err)
            continue
        }
        rpc.ServeConn(conn)
    }
}
// client 只改了一行
package main

import (
    "fmt"
    "log"
    "net/rpc"
    "os"
)


type Args struct {
    A, B int //首字母要大寫
}

//除法
type Quotient struct {
    Quo, Rem int
}

func main() {
    if len(os.Args) != 2 {
        fmt.Println("Usage:", os.Args[0], "server")
        os.Exit(1)
    }
    serverAddr := os.Args[1]
    //只改一行DialHTTP改成Dial
    client, err := rpc.Dial("tcp", serverAddr+":1234")
    if err != nil {
        log.Fatal("dialog:", err)
    }
    args := Args{17, 8}
    var reply int
    err = client.Call("Math.Multiply", args, &reply)
    if err != nil {
        log.Fatal("Math error:", err)
    }
    fmt.Println("Math:%d*%d=%d\n", args.A, args.B, reply)

    var quo Quotient
    err = client.Call("Math.Divide", args, &quo)
    if err != nil {
        log.Fatal("Math error:", err)
    }
    fmt.Println("Math:%d/%d=%d\n", args.A, args.B, quo.Quo, quo.Rem)

}

jsonrpc

// server
package main

import (
    "fmt"
    "github.com/kataras/iris/core/errors"
    "net"
    "net/rpc"
    "net/rpc/jsonrpc"
    "os"
)

type Args struct {
    A, B int //首字母要大寫
}

type Math int

func (m *Math) Multiply(args *Args, reply *int) error {
    *reply = args.A * args.B
    return nil
}

//除法
type Quotient struct {
    Quo, Rem int
}

func (m *Math) Divide(args *Args, quo *Quotient) error {
    if args.B == 0 {
        return errors.New("divide by zero")
    }
    quo.Quo = args.A / args.B
    quo.Rem = args.A % args.B
    return nil
}

func main() {
    math := new(Math)
    rpc.Register(math)

    tcpAddr, err := net.ResolveTCPAddr("tcp", ":1234")
    if err != nil {
        fmt.Println("Fatal error:", err)
        os.Exit(2)
    }
    listener, err := net.ListenTCP("tcp", tcpAddr)
    if err != nil {
        fmt.Println("Fatal error:", err)
        os.Exit(2)
    }
    for {
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("conn error:", err)
            continue
        }
        jsonrpc.ServeConn(conn)
        //rpc.ServeConn(conn)
    }
}
// client
package main

import (
    "fmt"
    "log"
    "net/rpc"
    "net/rpc/jsonrpc"
    "os"
)


type Args struct {
    A, B int //首字母要大寫
}

//除法
type Quotient struct {
    Quo, Rem int
}

func main() {
    if len(os.Args) != 2 {
        fmt.Println("Usage:", os.Args[0], "server")
        os.Exit(1)
    }
    serverAddr := os.Args[1]
    //只改jsonrpc
    client, err := jsonrpc.Dial("tcp", serverAddr+":1234")
    if err != nil {
        log.Fatal("dialog:", err)
    }
    args := Args{17, 8}
    var reply int
    err = client.Call("Math.Multiply", args, &reply)
    if err != nil {
        log.Fatal("Math error:", err)
    }
    fmt.Println("Math:%d*%d=%d\n", args.A, args.B, reply)

    var quo Quotient
    err = client.Call("Math.Divide", args, &quo)
    if err != nil {
        log.Fatal("Math error:", err)
    }
    fmt.Println("Math:%d/%d=%d\n", args.A, args.B, quo.Quo, quo.Rem)

}

擴展學習

  • REST 簡介 (GET/POST/PUT/DELETE/PATCH)
  • WebSocket
  • Gorilla WebSocket 包

主題測試文章,只做測試使用。發佈者:Walker,轉轉請注明出處:https://walker-learn.xyz/archives/6718

(0)
Walker的頭像Walker
上一篇 2026年3月8日 15:11
下一篇 2026年3月9日 12:56

相關推薦

  • 編程基礎 0012_Go_Web與網絡編程精華

    Go Web 與網絡編程精華 知識來源:- 《Building Web Apps with Go》- 《Go API 編程》- 《Go Web 編程》(Go Web Programming, Sau Sheong Chang)- 《Go 網絡編程》(Network Programming with Go)- 《Mastering Go Web Service…

    後端開發 2026年3月6日
    5400
  • Go工程師體系課 016

    Kubernetes 入門 —— Go 微服務部署與編排 一、Kubernetes 核心概念 1.1 甚麼是 Kubernetes Kubernetes(簡稱 K8s)是 Google 開源的容器編排平台,用於自動化部署、擴展和管理容器化應用。如果說 Docker 解決了"如何打包和運行單個容器"的問題,那麼 K8s 解決的是"如何管理成百上千個容器"的問題…

    後端開發 2026年3月7日
    6600
  • Go工程師體系課 011

    查詢的倒排索引 1. 甚麼是倒排索引? 倒排索引(Inverted Index)是一種數據結構,用於快速查找包含特定詞彙的文檔。它是搜索引擎的核心技術之一。 1.1 基本概念 正排索引:文檔 ID → 文檔內容(詞列表) 倒排索引:詞 → 包含該詞的文檔 ID 列表 1.2 為甚麼叫"倒排"? 倒排索引將傳統的"文檔包含哪些詞"的關係倒轉為"詞出現在哪些文檔…

    後端開發 2026年3月7日
    5500
  • Go工程師體系課 009

    其它一些功能 個人中心 收藏 管理收貨地址(增刪改查) 留言 拷貝inventory_srv--> userop_srv 查詢替換所有的inventory Elasticsearch 深度解析文檔 1. 甚麼是Elasticsearch Elasticsearch是一個基於Apache Lucene構建的分布式、RESTful搜索和分析引擎,能夠快速地…

    後端開發 2026年3月7日
    5900
  • 編程基礎 0005_錯誤處理進階

    Go 錯誤處理進階 目錄 Go 錯誤處理哲學 error 接口本質 自定義錯誤類型 fmt.Errorf 與 %w 包裝錯誤 errors.Is 和 errors.As 哨兵錯誤模式 錯誤處理最佳實踐 實際項目中的錯誤處理模式 1. Go 錯誤處理哲學 1.1 與 try-catch 的根本區別 在 Java、Python、C++ 等語言中,異常處理依賴 t…

    後端開發 2026年3月6日
    6100
簡體中文 繁體中文 English