欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Go Web:RESTful web service示例

程序员文章站 2023-10-15 14:11:29
RESTful架构的简介 web服务的架构模式主要有2种:SOAP和REST。SOAP和REST都回答了同一个问题:如何访问web服务。 SOAP风格的程序是功能驱动的,要借助xml来传递数据,明确表示要做什么动作,访问什么资源,但使用xml是非常繁琐复杂的事情。 RESTful风格的Web服务是资 ......

restful架构的简介

web服务的架构模式主要有2种:soap和rest。soap和rest都回答了同一个问题:如何访问web服务。

soap风格的程序是功能驱动的,要借助xml来传递数据,明确表示要做什么动作,访问什么资源,但使用xml是非常繁琐复杂的事情。

restful风格的web服务是资源驱动的,通过资源(名词)和http方法get/post/delete/put来实现增删改查的逻辑,偶尔也用patch/head方法。注意,post不是幂等的,而put是幂等的,所以put常用来更新资源,post常用来创建资源。

关于restful风格的web服务,重点体现在uri上。restful风格的web服务的uri不能包含动词,而是只包含名词(资源)。动词或其它相关的意思可以通过http request的header/body或者通过url的query来传递。

例如:
1.获取id=1的文章:/posts/show/1,其中show是动词,这个uri就设计错了,正确的写法应该是/posts/1,然后用get方法表示获取,即show
2.用户1转账500给2:post /accounts/1/transfer/500/to/2,transfer是动词,是错误的,应该设计成名词,并将动词逻辑相关的参数通过url的query或者请求报文传递

post /transaction http/1.1
host: 127.0.0.1
  
from=1&to=2&amount=500.00

关于restful的web,参见阮一峰的两篇文章:

restful风格的web服务示例

本示例是描述如何操作博客文章的web服务,对于restful的规范来说,功能并不完善,但对于理解restful来说足够了。

两个源码文件server.go和data.go位于同个目录下,都属于main包,server.go是运行入口,data.go是数据操作逻辑的代码。

以下是server.go文件内容,定义handler。注意处理资源的方式,这是restful风格的web服务核心:uri中不能包含动词,只通过资源来完成动作逻辑

package main

import (
    "encoding/json"
    "net/http"
    "path"
    "strconv"
)

func main() {
    server := http.server{
        addr: "127.0.0.1:8080",
    }
    http.handlefunc("/posts/", handlerequest)
    server.listenandserve()
}

func handlerequest(w http.responsewriter, r *http.request) {
    var err error
    switch r.method {
    case "get":
        err = handleget(w, r)
    case "post":
        err = handlepost(w, r)
    case "put":
        err = handleput(w, r)
    case "delete":
        err = handledelete(w, r)
    }
    if err != nil {
        http.error(w, err.error(), http.statusinternalservererror)
        return
    }
}

func handleget(w http.responsewriter, r *http.request) (err error) {
    id, err := strconv.atoi(path.base(r.url.path))
    if err != nil {
        return
    }
    post, err := retrieve(id)
    if err != nil {
        return
    }
    output, err := json.marshalindent(&post, "", "\t\t")
    if err != nil {
        return
    }
    w.header().set("content-type", "application/json")
    w.write(output)
    return
}

func handlepost(w http.responsewriter, r *http.request) (err error) {
    len := r.contentlength
    body := make([]byte, len)
    r.body.read(body)
    var post post
    json.unmarshal(body, &post)
    err = post.create()
    if err != nil {
        return
    }
    w.writeheader(200)
    return
}

func handleput(w http.responsewriter, r *http.request) (err error) {
    id, err := strconv.atoi(path.base(r.url.path))
    if err != nil {
        return
    }
    post, err := retrieve(id)
    if err != nil {
        return
    }
    len := r.contentlength
    body := make([]byte, len)
    r.body.read(body)
    json.unmarshal(body, &post)
    err = post.update()
    if err != nil {
        return
    }
    w.writeheader(200)
    return
}

func handledelete(w http.responsewriter, r *http.request) (err error) {
    id, err := strconv.atoi(path.base(r.url.path))
    if err != nil {
        return
    }
    post, err := retrieve(id)
    if err != nil {
        return
    }
    err = post.delete()
    if err != nil {
        return
    }
    w.writeheader(200)
    return
}

以下是操作数据库的data.go文件内容:

package main

import (
    "database/sql"

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

type post struct {
    id      int    `json:"id"`
    content string `json:"content"`
    author  string `json:"author"`
}

var db *sql.db

func init() {
    var err error
    db, err = sql.open("mysql", "root:p@ssword1!@tcp(192.168.100.21:3306)/blog")
    if err != nil {
        panic(err)
    }
}

func retrieve(id int) (post post, err error) {
    post = post{}
    err = db.queryrow("select id, content, author from posts where id = ?", id).scan(&post.id, &post.content, &post.author)
    return
}

func (post *post) create() (err error) {
    statement := "insert into posts (content, author) values (?, ?)"
    stmt, err := db.prepare(statement)
    if err != nil {
        return
    }
    defer stmt.close()
    res, err := stmt.exec(post.content, post.author)
    if err != nil {
        return
    }
    lastid, err := res.lastinsertid()
    if err != nil {
        return
    }
    post.id = int(lastid)
    return
}

func (post *post) update() (err error) {
    _, err = db.exec("update posts set content = ?, author = ? where id = ?", post.content, post.author, post.id)
    return
}

func (post *post) delete() (err error) {
    _, err = db.exec("delete from posts where id = ?", post.id)
    return
}