Restful风格和Go实践

不忘初心,砥砺前行 

作者 | 陌无崖

转载请联系授权 

定义

RESTFUL是一种网络应用程序的设计风格和开发方式,基于HTTP,可以使用XML或者JSON格式定义。主要适用于移动互联网业务接口的场景。主要动作类型为新增、变更、删除所使用的资源。

REST描述了一个架构样式的网络系统,倾向于用更加简单轻量的方法设计和实现。没有明确的标准,更像一种设计风格。


REST本身不是架构,只是一种架构风格

原则

1、客户端和服务器之间的交互在请求之间是无状态的。
2、服务端公开资源(应用程序对象、数据库记录、算法等),每个资源都使用URI进行标识,得到一个唯一的地址。所有的资源都共享统一的接口。便于 在客户端和服务器之间传输。

注:
所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。你可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或独一无二的识别符。

资源是一中信息实体,可以有很多表现形式。URI只是代表资源的实体,不代表它的形式,有些网址后的.html是一种表现形式,URI只代表资源的位置,它的具体表现形式应该是在请求头信息中用Accept和Content-Type字段指定。

RESTful特点

1、每个URI代表一种资源;
2、客户端使用GET、POST、PUT、DELETE4个表示操作方式的动词对服务端资源进行操作;
3、客户端与服务端之间的交互在请求之间是无状态的;

使用方式

传统
http://127.0.0.1/item/queryItem.action?id=1 查询,GET
http://127.0.0.1/item/saveItem.action 新增,POST
http://127.0.0.1/item/updateItem.action 更新,POST
http://127.0.0.1/item/deleteItem.action?id=1 删除,GET或POST
RESTful
http://127.0.0.1/item/1 查询,GET
http://127.0.0.1/item 新增,POST
http://127.0.0.1/item 更新,PUT
http://127.0.0.1/item/1 删除,DELETE

注:
这就变成了使用同一个 URL ,通过约定不同的 HTTP 方法来实施不同的业务

基于Go设计我们的Restful

入门案例(创建待办事项)
package main

import (
    "fmt"
    "log"
    "net/http"

    "github.com/gorilla/mux"
)

func main() {
    router := mux.NewRouter().StrictSlash(true)
    router.HandleFunc("/", Index)
    router.HandleFunc("/todos", TodoIndex)
    router.HandleFunc("/todos/{todoId}", TodoShow)

    log.Fatal(http.ListenAndServe(":8080", router))
}

func Index(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Welcome!")
}

func TodoIndex(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Todo Index!")
}

func TodoShow(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    todoId := vars["todoId"]
    fmt.Fprintln(w, "Todo Show:", todoId)
}

注:
vars := mux.Vars(r)用来解析我们的r。

创建待办结构体
type Todo struct {
    Name      string    `json:"name"`
    Completed bool      `json:"completed"`
    Due       time.Time `json:"due"`
}

type Todos []Todo
返回JSON数据

我们需要在函数中模拟静态的数据列表

func TodoIndex(w http.ResponseWriter, r *http.Request) {
    todos := Todos{
        Todo{Name: "Write presentation"},
        Todo{Name: "Host meetup"},
    }

    json.NewEncoder(w).Encode(todos)
}
拆分代码

handler.go

func Index(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Welcome!")
}

func TodoIndex(w http.ResponseWriter, r *http.Request) {
    todos := Todos{
        Todo{Name: "Write presentation"},
        Todo{Name: "Host meetup"},
    }

    if err := json.NewEncoder(w).Encode(todos); err != nil {
        panic(err)
    }
}

func TodoShow(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    todoId := vars["todoId"]
    fmt.Fprintln(w, "Todo show:", todoId)
}

route.go

type Route struct {
    Name        string
    Method      string
    Pattern     string
    HandlerFunc http.HandlerFunc
}

type Routes []Route

func NewRouter() *mux.Router {

    router := mux.NewRouter().StrictSlash(true)
    for _, route := range routes {
        router.
            Methods(route.Method).
            Path(route.Pattern).
            Name(route.Name).
            Handler(route.HandlerFunc)
    }

    return router
}

var routes = Routes{
    Route{
        "Index",
        "GET",
        "/",
        Index,
    },
    Route{
        "TodoIndex",
        "GET",
        "/todos",
        TodoIndex,
    },
    Route{
        "TodoShow",
        "GET",
        "/todos/{todoId}",
        TodoShow,
    },
}

todo.go

type Todo struct {
    Name      string    `json:"name"`
    Completed bool      `json:"completed"`
    Due       time.Time `json:"due"`
}

type Todos []Todo

main.go

func main() {

    router := NewRouter()

    log.Fatal(http.ListenAndServe(":8080", router))
}
POST JSON
func TodoCreate(w http.ResponseWriter, r *http.Request) {
    var todo Todo
    //io.LimitReader。这样是保护服务器免受恶意攻击的好方法
    body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1048576))
    if err != nil {
        panic(err)
    }
    if err := r.Body.Close(); err != nil {
        panic(err)
    }
    if err := json.Unmarshal(body, &todo); err != nil {
        w.Header().Set("Content-Type", "application/json; charset=UTF-8")
        w.WriteHeader(422) // unprocessable entity
        if err := json.NewEncoder(w).Encode(err); err != nil {
            panic(err)
        }
    }
    //创建新纪录
    t := RepoCreateTodo(todo)
    w.Header().Set("Content-Type", "application/json; charset=UTF-8")
    w.WriteHeader(http.StatusCreated)
    if err := json.NewEncoder(w).Encode(t); err != nil {
        panic(err)
    }
}

误区

1、URI包含动词。因为"资源"表示一种实体,所以应该是名词,URI不应该有动词,动词应该放在HTTP协议中;
2、URI中不应该加入版本号;

本文参考以下文章:

  1.  https://baike.baidu.com/item/RESTful

  2. http://www.ruanyifeng.com/blog/2014/05/restful_api.html

  3. http://www.ruanyifeng.com/blog/2011/09/restful.html

  4. https://segmentfault.com/a/1190000014875956#articleHeader17

关注我试试回复以下关键词

go 微服务 ppt

大数据 书籍 资料 


END

今日推荐阅读

RabbitMQ系列笔记广播模式和路由模式 
RabbitMQ系列笔记入门篇

RabbitMQ系列笔记work模式

RabbitMQ系列笔记work模式

protoc语法详解及结合grpc定义服务

Golang中Model的使用

基于Nginx和Consul构建高可用及自动发现的Docker服务架构

▼关注我,一起成长

    主要分享 学习心得、笔记、随笔▼

    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值