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