編程基礎 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
上一篇 14小時前
下一篇 1天前

相關推薦

  • Go工程師體系課 013

    訂單事務 先扣庫存 後扣庫存 都會對庫存和訂單都會有影響, 所以要使用分佈式事務 業務(下單不對付)業務問題 支付成功再扣減(下單了,支付時沒庫存了) 訂單扣減,不支付(訂單超時歸還)【常用方式】 事務和分佈式事務 1. 什麼是事務? 事務(Transaction)是數據庫管理系統中的一個重要概念,它是一組數據庫操作的集合,這些操作要麼全部成功執行,要麼全部…

  • Go工程師體系課 015

    Docker 容器化 —— Go 項目實戰指南 一、Docker 核心概念 1.1 什麼是 Docker Docker 是一個開源的容器化平臺,它可以將應用程序及其所有依賴項打包到一個標準化的單元(容器)中,從而實現"一次構建,到處運行"。對於 Go 開發者而言,Docker 解決了以下痛點: 開發環境與生產環境不一致 依賴管理複雜(數據庫、緩存、消息隊列等…

    後端開發 2小時前
    100
  • Go工程師體系課 005

    微服務開發 創建一個微服務項目,所有的項目微服務都在這個項目中進行,創建joyshop_srv,我們無創建用戶登錄註冊服務,所以我們在項目目錄下再創建一個目錄user_srv 及user_srv/global(全局的對象新建和初始化)user_srv/handler(業務邏輯代碼)user_srv/model(用戶相關的 model)user_srv/pro…

  • Go資深工程師講解(慕課) 008_GMP調度器與Go設計哲學

    Go GMP 調度器與設計哲學 對應視頻 9-2 go語言的調度器、18-1 體會Go語言的設計、18-2 課程總結 1. Go 調度器演進 1.0 時代:單線程調度器(Go 0.x) 只有一個線程運行 goroutine 所有 goroutine 排隊等待 無法利用多核 1.1 時代:多線程調度器(Go 1.0) 引入多線程 但全局鎖競爭嚴重,性能瓶頸 1…

  • Go工程師體系課 016

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

簡體中文 繁體中文 English