Programming Fundamentals 0004_Web_beego Development

beego Getting Started 2

Adding and Deleting Articles

  • Create 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 will use reflection to get all implemented methods in the struct, and you can access the corresponding methods in the following ways.

  • /object/login calls the Login method in ObjectController
  • /object/logout calls the Logout method in ObjectController

Besides matching the two prefixes /:controller/:method, beego will automatically parse the remaining URLs as parameters and save them in this.Ctx.Input.Params, as follows

/object/blog/2013/09/12 calls the Blog method in ObjectController, with parameters map[0:2013,1:09,2:12]

Now, all similar URLs below can be automatically recognized, and requests will be dispatched to the `simple` method of the controller. The suffix can be obtained via c.Ctx.Input.Param(":ext")

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

There's a small pitfall when adding article categories (Chrome browser): Chrome prevents duplicate form submissions. If you previously added a category on that page, deleted it, and then tried to add it again, you might encounter an issue where the addition doesn't respond. This is because Chrome thinks you are submitting repeatedly. The solution is to add a token. To avoid polluting the interface, you can add an anchor.

Go Native Form Handling

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 File Upload

When uploading attachments, pay attention to the form's enctype="multipart/form-data". Add relevant fields in both AddTopic and ModifyTopic controllers.

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

Native Way

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
}

Internationalization Support

bee new beegoi18n
cd beegoi18n && bee run
go get github.com/beego/i18n
dep ensure -add github.com/beego/i18n
// Nested routing
package controllers

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

// Create base to replace beego.Controller
type baseController struct {
    beego.Controller
    i18n.Locale
}

// Implemented Prepare() myself
// Specify language
// Register localization files local_en-US.ini or local_zh-CN.ini
func (this *baseController) Prepare() {
    // Override
    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"
}
// Register in main.go

func main() {
    i18n.SetMessage("zh-CN","conf/local_zh-CN.ini")
    i18n.SetMessage("en-US","conf/locale_en-US.ini")
    // Then you can use Tr in the controller or Tr in the template
    // To use in templates, you need to register the template function
    beego.Run()
}

Sectioning Feature

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

Documentation

Custom HTTP Middleware

  • As a type
  • As a function
  • Append response content
  • Customize response content
// As a type
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)
}
// As a function
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)
}

// Normal 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) // Normal response
    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()  // Its purpose is virtual response, not actual response, to get normal response results,
    this.handler.ServeHTTP(recoder, r) // recorded to recoder
    for k, v := range recoder.Header() {
        w.Header()[k] = v
    }
    //    Custom operations
    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"

Simple RPC Implementation

  • Introduce Go-to-Go communication
  • HTTP RPC
  • TCP RPC
  • JSON-RPC
// Based on HTTP
// server.go
package main

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

type Args struct {
    A, B int // First letter must be capitalized
}

type Math int

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

// Division
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.go The types here can be extracted into a common reference
package main

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


type Args struct {
    A, B int // First letter must be capitalized
}

// Division
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)

}

Run go run http_rpc_client.go localhost in the command line

Based on TCP

// server
package main

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

type Args struct {
    A, B int // First letter must be capitalized
}

type Math int

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

// Division
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())
    //}
    /*
       TCP 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 only changed one line
package main

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


type Args struct {
    A, B int // First letter must be capitalized
}

// Division
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]
    // Only changed one line: DialHTTP to 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)

}

JSON-RPC

// server
package main

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

type Args struct {
    A, B int // First letter must be capitalized
}

type Math int

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

// Division
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 // First letter must be capitalized
}

// Division
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]
    // Only changed JSON-RPC
    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)

}

Further Learning

  • Introduction to REST (GET/POST/PUT/DELETE/PATCH)
  • WebSocket
  • Gorilla WebSocket Package

主题测试文章,只做测试使用。发布者:Walker,转转请注明出处:https://walker-learn.xyz/archives/6718

(0)
Walker的头像Walker
上一篇 13 hours ago
下一篇 1 day ago

Related Posts

EN
简体中文 繁體中文 English