基于Go语言神速构建RESTful API服务

web服务器至少有以下多少个特性:

In this post, we will not only cover how to use Go to create a RESTful
JSON API, but we will also talk about good RESTful design.

1、24钟头不鸣金收兵的干活,也就是说那多少个过程要常驻在内存中

What is a JSON API?

JSON API
是数额交互规范,用以定义客户端咋样拿到与修改资源,以及服务器怎么样响应对应请求。JSON
API设计用来最小化请求的多少,以及客户端与服务器间传输的数据量。通过遵守共同的预约,可以增进支付效能,利用更常见的工具,基于
JSON API 的客户端仍可以充裕利用缓存,以提升性能。

示例:

{
  "links": {
    "posts.author": {
      "href": "http://example.com/people/{posts.author}",
      "type": "people"
    },
    "posts.comments": {
      "href": "http://example.com/comments/{posts.comments}",
      "type": "comments"
    }
  },
  "posts": [{
    "id": "1",
    "title": "Rails is Omakase",
    "links": {
      "author": "9",
      "comments": [ "5", "12", "17", "20" ]
    }
  }]
}

2、24时辰在某一端口监听,如: http://localhost:8080, www服务器默认端口80

开行一个RESTful服务

$ go run main.go

$ curl http://localhost:8080
Hello,"/"

package main

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

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
    })

    log.Fatal(http.ListenAndServe(":8080", nil))

}

3、要力所能及处理主旨的呼吁:如get, post

日增路径分发功效

途径又称”终点”(endpoint),表示API的现实性网址。在RESTful架构中,每个网址代表一种资源(resource)。
其三方组件(Gorilla Mux package): “github.com/gorilla/mux”

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

做客测试:

$ curl http://localhost:8080/todo
404 page not found
$ curl http://localhost:8080/todos
Todo Index! ,"/todos"
$ curl http://localhost:8080/todos/{123}
TodoShow: ,"123"

在node
js中创立一台服务器非凡的简短,因为node自带http模块,该模块可以帮助我们特别迅猛搭建一台web服务器,来处理一个粗略的请求.

抽象数据模型

开创一个数据模型“Todo”、“Routes”。在其他语言中,使用类(class)实现。
在Go语言中,没有class,必须采取结构(struct)。

Todo.go

package main

import "time"

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

type Todos []Todo

Routes.go

package main

import (
    "net/http"
    "github.com/gorilla/mux"
)

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

type Routes []Route
1 const http = require("http");
2 var server = http.createServer(function(){
3     console.log( '有人访问ghostwu的服务器' );
4 });
5 server.listen( 8080 );

重构:Handlers & Router

Handlers.go

package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "github.com/gorilla/mux"
)

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

Router.go

package main

import (
    "net/http"
    "github.com/gorilla/mux"
)

func NewRouter() *mux.Router {
    router := mux.NewRouter().StrictSlash(true)
    for _, route := range routes {
        var handler http.Handler
        handler = route.HandlerFunc
        handler = Logger(handler, route.Name)

        router.
            Methods(route.Method).
            Path(route.Pattern).
            Name(route.Name).
            Handler(handler)

    }
    return router
}

第1行:导入node js内置的http模块

启航入口是不是满面春风很多!

Main.go

Main.go
package main

import (
    "log"
    "net/http"
)

func main() {
    router := NewRouter()
    log.Fatal(http.ListenAndServe(":8080", router))
}

web
access:http://localhost:8080/todos

Todo Index! ,”/todos”
[
{
“id”:0,
“name”:”Write sth ….”,
“completed”:false,
“due”:”0001-01-01T00:00:00
},
{
“id”:1,
“name”:”Host meetup ….”,
“completed”:false,
“due”:”0001-01-01T00:00:00Z”
}
]

第2行: 利用http模块自带的createServer方法,创造一台服务器

增强功效:持久化

func TodoCreate(w http.ResponseWriter, r *http.Request) {
    var todo Todo
    //add Todo instance
}

第5行: 服务器在8080端口监听

增进效率:日志

2017/05/23 15:57:23 http: multiple response.WriteHeader calls
2017/05/23 15:57:23 GET /todos  TodoIndex   6.945807ms
2017/05/23 16:18:40 http: multiple response.WriteHeader calls
2017/05/23 16:18:40 GET /todos  TodoIndex   2.127435ms

然后切换来文件所在的门道,用node 运行这个文件( node + 文件名
),然后在浏览器中输入( http://localhost:8080 )就可以望见控制台 打印出
‘有人访问ghostwu的劳务器’

Things We Didn’t Do

1、版本控制
API版本迭代 &
跨版本资源访问。常用做法是将版本号放在URL,较为简单,例如:https://localhost:8080/v1/
另一种做法是将版本号放在HTTP头音信中。

2、授权验证:涉及到OAuth和JWT。
(1)OAuth 2.0,OAuth2 is an authentication
framework
,RFC
6749

OAuth2是一种授权框架,提供了一套详细的、可供实践的指令性解决方案。OAuth
2.0定义了四种授权格局。授权码形式(authorization
code)、简化格局(implicit)、密码情势(resource owner password
credentials)、客户端情势(client credentials)。

(2)JSON web tokens,JWT is an authentication
protocol
,RFC
7519

JWT是一种安全磋商。基本思路就是用户提供用户名和密码给认证服务器,服务器验证用户提交消息音信的合法性;假如证实成功,会发出并赶回一个Token(令牌),用户可以运用这个token访问服务器上受保障的资源。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

header:定义算法(alg:ALGORITHM)和TOKEN TYPE(typ)

{
  "alg": "HS256",
  "typ": "JWT"
}

Data:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

3、 eTags:关于缓存、性能和用户标识和追踪。

图片 1

参考文献

  1. 阮一峰:RESTful API
    设计指南
  2. CORY LANOU:Making a RESTful JSON API in
    Go,2014Nov
  3. InfoQ:使用ETags缩小Web应用带宽和负载
  4. Stackoverflow:jwt vs oauth
    authentication
  5. OAuth 2 VS JSON Web Tokens:How to secure an
    API,20160605
  6. 阮一峰:理解OAuth
    2.0,201405

图片 2

不过这些服务器有个问题,只好在控制台出口音讯,一般的服务器都会向浏览器输出音信,如何是好呢?

const http = require("http");
var server = http.createServer(function( req, res ){
    res.write( 'http://www.cnblogs.com/ghostwu/' );
    res.end();
});
server.listen( 6060 );

createServer的时候,为回调函数传递2个参数,第一个参数是处理请求的,
第二个参数是拍卖响应的,而我们要想把服务器的信息输出到浏览器,就需要调用res对象的write方法,然后调用end方法截止输出。在浏览器输入:
localhost:6060,就能看到 http://www.cnblogs.com/ghostwu/ 

但是那台服务器,依然有个问题,只可以处理http://localhost:6060
这样一个原则性的端口和网址的乞请, 其他的呼吁输出都是
看到同一的事物,而实际的伸手却是:

http://localhost:8080   访问首页

http://localhost/404.html 访问404页面

http://localhost/1.html  访问1.html

。。。。

等等,不同的url请求,应该把相应的文本新闻读出来,再出口到浏览器,我们先来做那一个效率,不同的url显示不同的信息:

 1 const http = require("http");
 2 var server = http.createServer(function( req, res ){
 3     switch( req.url ){
 4         case '/1.html' :
 5             res.write( '1.html' );
 6             break;
 7         case '/2.html':
 8             res.write( '2.html' );
 9             break;
10         default: 
11             res.write( '404' );
12     }
13     res.end();
14 });
15 
16 server.listen( 8080 );

这样就能处理 http://localhost:8080/1.html –> 显示1.html
 http://localhost:8080/2.html —> 展现2.html 其他的哀求都是彰显404

终极,我们只需要,在相应的道岔,把相应的文书内容读取出来就可以了,那么node
js怎么读取文件呢?

1 var fs =require('fs');
2 fs.readFile("1.txt", function( err, data ){
3     if( err ){
4         console.log( err )
5     }else {
6         console.log( data.toString() );
7     }
8 });

需要包含fs模块,那多少个模块就是拍卖文件的

readFile, 第一个参数,具体的公文路径和称号,
第二个参数回调函数有2个参数,一个是不对结果,一个是文本中的数据,
数据需要用toString方法转一下,否则就是字节数据,还有1.txt以此文件要实在存在.

写文件用writeFile这么些api,b.txt尽管不存在会自动创造一个,往b.txt文件中插入内容’
this is b.txt’

1 const fs = require("fs");
2 fs.writeFile( "b.txt", "this is b.txt", function( err ){
3     console.log( err );
4 } );

有了文本读写功用,咱们就可以整合方面的代码,实现一个简便的get请求服务器

文件结构:

图片 3

server.js:

 1 var http = require("http");
 2 var fs = require("fs");
 3 var server = http.createServer(function( req, res ){
 4     var fileName  = './www' + req.url;
 5     fs.readFile( fileName, function( err, data ){
 6         if( err ){
 7             res.write( '404' );
 8         }else {
 9             res.write( data );
10         }
11         res.end();
12     } );
13 }).listen( 8080 );

下一场,在浏览器输入:

http://localhost:8080/index.html   就能读出www目录上面index.html的始末

http://localhost:8080/category.html  
就能读出www目录上面category.html的情节

此外请求,输出404