golang restful api
https://medium.com/@petrousov/how-to-build-a-restful-api-in-go-for-phonebook-app-d55f7234a10
----------------------------------------
How to build a RESTful API in Go for phonebook app
TL;DR
In this tutorial I am going to show you how I created a RESTful API for a hypothetical phonebook application and how you can create your own APIs based on this example. All the code is stored on github.
Disclaimer
- This is a project to learn Go myself
- The storage of data (database) and file structure is out of the scope of this tutorial and was not implemented
Phonebook API
A phonebook application stores records of peoples contact information.
The models
In our context, a person’s record includes the first and last names and contact details such as the city, the zipcode and the phone number. To model this in Go we are going to write the following structs and create a slice where we are going to store our records.
package main import (
"encoding/json"
)
type Person struct {
ID string `json:"id,omitempty"`
Firstname string `json:"firstname,omitempty"`
Lastname string `json:"lastname,omitempty"`
Contactinfo `json:"contactinfo,omitempty"`
}
type Contactinfo struct {
City string `json:"city,omitempty"`
Zipcode string `json:"Zipcode,omitempty"`
Phone string `json:"phone,omitempty"`
}
var people []Person
There are a couple of things worth mentioning about the above snippet. The fields of the structs need to begin with an uppercase letter so they can be exported. This is necessary because the JSON library we are going to use to encode/decode our data can only access exported values. See https://blog.golang.org/json-and-go for more details.
The other thing worth mentioning is the parameter next to our field types enclosed in backquotes. This is a special parameter which specifies the name of the key our fields are going to have in the JSON format. For example, the value of the field Firstname in our struct will be referenced with the key firstname in the JSON format. For more information, checkout the Marshal() function from the official documentation https://golang.org/pkg/encoding/json/#Marshal.
The handlers
Our backend needs to be able to perform the following 5 operations on our records.
- retrieve the records of all the people
- retrieve the record of a specific person
- create a new person record in the catalog
- update a person’s record information
- delete a person’s record from the catalog
We are going to analyze the function used to update a person’s information (4) since the rest follow a similar implementation. Given the updated record of a person, this handler will look for this person in our slice and if it finds a matching id, will update the record.
func UpdatePersonEndpoint(w http.ResponseWriter, r *http.Request) {
var person Person
_ = json.NewDecoder(r.Body).Decode(&person)
params := mux.Vars(r)
for i, p := range people {
if p.ID == params["id"] {
people[i] = person
json.NewEncoder(w).Encode(person)
break
}
}
}
The first thing we must notice is the that this function’s name starts with an uppercase letter which means it’s exported. This is not necessary for our example since we store everything in one main.go file. However, if we had a separate file or package called handlers, we would need to be able to call those handlers from a different namespace (main). This is only possible if we have them exported.
All of our functions/handlers accept the same 2 parameters (w, r). These parameters represent data streams which our handlers use to retrieve information from (r) and send information to (w). Consider them as the STDIO (keyboard and monitor) of our backend. It’s not necessary to know the implementation of these interfaces, but if you are curious, check out the official documentation https://golang.org/pkg/net/http/#ResponseWriter
In order to implement communication through these data streams, we use two assistive functions, json.NewDecoder() and json.NewEncoder(). These functions allow us to send and receive our data.
The first function is associated with the data stream we use to read from (r) and returns a decoder element. Using the Decode() function on this element, we retrieve the information from the body of a HTTP request and store it in the person variable we created. This information is in JSON, which is human readable format, so we “decode” it into our struct which is readable by our server. A struct variable is a “pass by value” element, so we need pass the address of the variable person to the Decode() function so it can store the values in it.
The second function is associated with the stream we use to write information to (w) and returns an encoder element. Using the Encode() function on this element, we respond to a HTTP request. So, we transform our person variable into JSON and send it back to the responder.
If needed, checkout the docs for more information on the above functions https://golang.org/pkg/encoding/json/#NewDecoder
The last thing to mention about the update handler is that it identifies the record to update by it’s id which is passed as a parameter through the URL when we make the HTTP request. We extract all the variables from the URL using the mux.Vars() function, which returns a map, and reference them using their keys.
The rest of the handlers use the same components to implement our API’s functionality.
func GetPeopleEndpoint(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(people)
}
func GetPersonEndpoint(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
for _, p := range people {
if p.ID == params["id"] {
json.NewEncoder(w).Encode(p)
return
}
}
json.NewEncoder(w).Encode("Person not found")
}
func CreatePersonEndpoint(w http.ResponseWriter, r *http.Request) {
var person Person
_ = json.NewDecoder(r.Body).Decode(&person)
people = append(people, person)
json.NewEncoder(w).Encode(person)
}
func DeletePersonEndpoint(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
for i, p := range people {
if p.ID == params["id"] {
copy(people[i:], people[i+1:])
people = people[:len(people)-1]
break
}
}
json.NewEncoder(w).Encode(people)
}
The router
We now have our models and handlers which are able to receive and respond to HTTP requests and convert the data from JSON into our models and back. The next thing we need to implement is the mapping which shows the correspondence of a URL and HTTP request type to our handlers.
- /people (GET) -> GetPeopleEndpoint()
- /people/{id} (GET) -> GetPersonEndpoint()
- /people (POST) -> CreatePersonEndpoint()
- /people/{id} (PUT) -> UpdatePersonEndpoint()
- /people/{id} (DELETE) -> DeletePersonEndpoint()
This mapping shows that an HTTP GET call to the /people URL will execute the GetPeopleEndpoint() handler. Another HTTP PUT call to /people/{id} will execute the UpdatePersonEndpoint() handler so on and so forth.
For the implementation of the router, we are going to use the gorilla/mux package and write the following code.
import (
"encoding/json"
"net/http" "github.com/gorilla/mux"
)
func main() {
router := mux.NewRouter()
router.HandleFunc("/people", GetPeopleEndpoint).Methods("GET")
router.HandleFunc("/people/{id}", GetPersonEndpoint).Methods("GET")
router.HandleFunc("/people", CreatePersonEndpoint).Methods("POST")
router.HandleFunc("/people/{id}", DeletePersonEndpoint).Methods("DELETE")
router.HandleFunc("/people/{id}", UpdatePersonEndpoint).Methods("PUT")
}
The logic is pretty straightforward, we initially create a new router instance. Then, we proceed to map our URL endpoints to the handlers we wrote earlier. As we can see, our handlers now have also the HTTP method they require in order to be called defined with the Methods() function.
All these functions are provided by the mux package and its documentation can be found online http://www.gorillatoolkit.org/pkg/mux
Populating with dummy data
For the sake of simplicity we are not going to use a database to store our data. Instead, everything will be stored locally in our slice named people. So, in order to populate our API with some dummy data, we are going to create a couple of entries.
people = append(people, Person{ID: "1", Firstname: "Bruce", Lastname: "Wayne", Contactinfo: Contactinfo{City: "Gotham", Zipcode: "735", Phone: "012345678"}})
people = append(people, Person{ID: "2", Firstname: "Clark", Lastname: "Kent", Contactinfo: Contactinfo{City: "Metropolis", Zipcode: "62960", Phone: "9876543210"}})
}
The server
The last thing left to complete our API is to make it accessible from the network, in other words serve it. To accomplish this, we are going to use the ListenAndServe() function from the http package which starts a HTTP server.
package main import (
"fmt"
"log"
"net/http" "github.com/gorilla/mux"
)
func main() {
fmt.Println("Starting server on port 8000...")
log.Fatal(http.ListenAndServe(":8000", router))
}
Our server is going to be listening on port 8000. The last line wraps the server function in a log function which will print an error message and return a non-zero code (1) if something goes wrong. The documentation for it can be found online https://golang.org/pkg/log/#Fatal
Testing
A working version of our API is available online from github. Let’s fire up our server by running the go run command.
go run main.go
Starting server on port 8000...
For our tests, we are going to use Postman and fire up all the HTTP requests to confirm the functionality of our handlers.
- Retrieve the records of all the people (GET)
2. Retrieve the record of a specific person (GET)
3. Create a new person record in the catalog (POST)
4. Update a person’s record information (PUT)
5. Delete a person’s record from the catalog (DELETE)
Delete a person’s record using it’s id
Conclusion
In this post I showed you how you can build a simple API in Go which can respond to HTTP requests. Following along you should be able to modify the phonebook API to serve your purpose and follow the documentation if necessary to clear some clouds.
References
golang restful api的更多相关文章
- Go实战--通过gin-gonic框架搭建restful api服务(github.com/gin-gonic/gin)
生命不止,继续 go go go !!! 先插播一条广告,给你坚持学习golang的理由: <2017 软件开发薪酬调查:Go 和 Scala 是最赚钱的语言> 言归正传! 之前写过使用g ...
- 使用 Beego 搭建 Restful API 项目
1 环境准备 首先你需要在你的环境安装以下软件: go:编程语言运行环境 git:版本控制工具 beego:go 语言流行的开发框架 bee:beego 配套的快速搭建工具 你喜欢的数据库:这里以 M ...
- RESTful 架构 && RESTful API
RESTful 架构 && RESTful API REpresentational State Transfer (REST) 具象状态传输https://en.wikipedia. ...
- (转载) RESTful API 设计指南
作者: 阮一峰 日期: 2014年5月22日 网络应用程序,分为前端和后端两个部分.当前的发展趋势,就是前端设备层出不穷(手机.平板.桌面电脑.其他专用设备......). 因此,必须有一种统一的机制 ...
- Node.js实现RESTful api,express or koa?
文章导读: 一.what's RESTful API 二.Express RESTful API 三.KOA RESTful API 四.express还是koa? 五.参考资料 一.what's R ...
- Restful Api 最佳实践
Web APIs has become an very important topic in the last year. We at M-Way Solutions are working ever ...
- 基于轻量型Web服务器Raspkate的RESTful API的实现
在上一篇文章中,我们已经了解了Raspkate这一轻量型Web服务器,今天,我们再一起了解下如何基于Raspkate实现简单的RESTful API. 模块 首先让我们了解一下"模块&quo ...
- RESTful Api 身份认证安全性设计
REST是一种软件架构风格.RESTful Api 是基于 HTTP 协议的 Api,是无状态传输.它的核心是将所有的 Api 都理解为一个网络资源.将所有的客户端和服务器的状态转移(动作)封装到 H ...
- 深入理解 RESTful Api 架构
转自https://mengkang.net/620.html 一些常见的误解 不要以为 RESTful Api 就是设计得像便于 SEO 的伪静态,例如一个 Api 的 URL 类似于 http: ...
随机推荐
- 机器学习(Machine Learning)与深度学习(Deep Learning)资料汇总
<Brief History of Machine Learning> 介绍:这是一篇介绍机器学习历史的文章,介绍很全面,从感知机.神经网络.决策树.SVM.Adaboost到随机森林.D ...
- 31.网络协议介绍tcp/udp
网络协议 TCP:网络中传输数据的协议,打电话 解决了我可能在网络中找不到别人(数据无法传输到) 保证数据传输的稳定性,可靠性 保证数据的安全性,完整性 对方要有响应 尝试重新发送 UDP:传输数据的 ...
- zTree入门使用
简单入门使用,熟悉其功能,没有与异步调用后台数据,用的是本地设置的数据. zTree的API:http://www.treejs.cn/v3/api.php 源码:https://github.com ...
- C++零散知识笔记本
目录 1.符号 1.1符号输出 1.2运算符 2.基本内置类型 wchar_t 3.内置类型所占字节数 内置类型的简写 4.变量的本质 变量与指针的故事 (1)malloc函数 (2)new关键字 5 ...
- 《你必须知道的495个C语言问题》读书笔记之第1-2章:声明和初始化
1. C标准中并没有精确定义数值类型的大小,但作了以下约束: (1) char类型可以存放小于等于127的值: (2) short int和int可以存放小于等于32767的值: (3) long i ...
- 【转帖】Linux的NUMA机制
Linux的NUMA机制 http://www.litrin.net/2014/06/18/linux的numa机制/ NUMA(Non-Uniform Memory Access)字面直译为“非一致 ...
- 【转帖】sysbench的安装和做性能测试
iMySQL | 老叶茶馆 sysbench的安装和做性能测试 http://imysql.cn/node/312 我仿照这个学的 但是 需要用更新的版本才可以. By yejr on 14 六月 ...
- Redis 数据结构 & 原理 & 持久化
一 概述 redis是一种高级的key-value数据库,它跟memcached类似,不过数据可以持久化,而且支持的数据类型也很丰富. Redis支持五种数据类型:string(字符串),hash(哈 ...
- Django-djangorestframework-异常模块-源码及自定义异常
目录 异常模块 为什么要自定义异常模块 常见的几种异常情况 异常模块源码分析 自定义 drf 异常处理 异常模块 为什么要自定义异常模块 所有经过 drf APIView 视图类产生的异常,都可以提供 ...
- XSS跨站攻击靶场-通关笔记
XSS攻击是Web攻击中最常见的攻击手法之一,XSS中文名跨站脚本攻击,该攻击是指攻击者在网页中嵌入恶意的客户端脚本,通常是使用JS编写的恶意代码,当正常用户访问被嵌入代码的页面时,恶意代码将会在用户 ...