RESTful API
以前写网站
get /user
post /create_user
post /update_user
post /delete_user
RESTful API
get /user 获取
post /user 新建
put /user 更新
patch /user 更新部分
delete /user 删除
-
REST与技术无关,代表的是一种软件架构风格,只要API程序遵循了REST风格,那就可以称其为
RESTful API
-
REST的含义就是客户端与Web服务器之间进行交互的时候,使用HTTP协议中的4个请求方法代表不同的动作
-
get
-
post
-
put
-
patch
-
delete
-
-
Gin框架支持开发
RESTful API
的开发 -
package main import ( "net/http" "github.com/gin-gonic/gin" ) func main() { // 创建一个服务 ginServer := gin.Default() // 访问地址,处理请求 Request Response ginServer.GET("/hello", func(context *gin.Context) { context.JSON(200, gin.H{"msg": "hello,world"}) }) ginServer.POST("/user", func(ctx *gin.Context) { ctx.JSON(http.StatusOK, gin.H{"msg": "post请求"}) }) ginServer.PUT("/user") ginServer.DELETE("/user") // 服务器端口 err := ginServer.Run(":8082") if err != nil { return } }
第一个Gin示例
// cmd
go get -u github.com/gin-gonic/gin
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
// 创建一个服务
ginServer := gin.Default()
// 访问地址,处理请求 Request Response
ginServer.GET("/hello", func(context *gin.Context) {
context.JSON(200, gin.H{"msg": "hello,world"})
// http.StatusOK就是请求已经成功的200的状态码
})
// 服务器端口
err := ginServer.Run(":8082")
if err != nil {
return
}
}
-
想要更改左上角的图标
-
package main import ( "github.com/gin-gonic/gin" "github.com/thinkerou/favicon" // go get ) func main() { // 创建一个服务 ginServer := gin.Default() ginServer.Use(favicon.New("./favicon.ico")) // 访问地址,处理请求 Request Response ginServer.GET("/hello", func(context *gin.Context) { context.JSON(200, gin.H{"msg": "hello,world"}) }) // 服务器端口 ginServer.Run(":8082") }
-
加载静态页面
-
package main import ( "net/http" "github.com/gin-gonic/gin" ) func main() { // 创建一个服务 ginServer := gin.Default() // ginServer.Use(favicon.New("./favicon.ico")) // 加载静态页面 ginServer.LoadHTMLGlob("templates/*") // ginServer.LoadHTMLFiles("templates/index.html") // LoadHTMLGlob是全局加载Files是指定文件 // 响应一个页面给前端 ginServer.GET("/index", func(context *gin.Context) { // context.JSON() json数据 context.HTML(http.StatusOK, "index.html", gin.H{ "msg": "这是go后台传入的数据", }) }) // 服务器端口 err := ginServer.Run(":8082") if err != nil { return } }
-
新建一个文件夹
templates
,在其下面创建index.html
文件 -
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>一个GoWeb页面</title> </head> <body> <h1>感谢大家支持江小年的博客</h1> 获取后端的数据为: {{.msg}} </body> </html>
加载资源包
-
创建
static
文件夹-
在其中创建
css
文件夹-
style.css
-
-
js
文件夹-
common.js
-
-
-
package main import ( "net/http" "github.com/gin-gonic/gin" ) func main() { // 创建一个服务 ginServer := gin.Default() // ginServer.Use(favicon.New("./favicon.ico")) // 加载静态页面 ginServer.LoadHTMLGlob("templates/*") // ginServer.LoadHTMLFiles("templates/index.html") // LoadHTMLGlob是全局加载Files是指定文件 //加载资源文件 ginServer.Static("/static", "./static") // 响应一个页面给前端 ginServer.GET("/index", func(context *gin.Context) { // context.JSON() json数据 context.HTML(http.StatusOK, "index.html", gin.H{ "msg": "这是go后台传入的数据", }) }) // 服务器端口 err := ginServer.Run(":8082") if err != nil { return } }
-
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>一个GoWeb页面</title> <link rel="stylesheet" href="/static/css/style.css"> <script src="/static/js/common.js"></script> </head> <body> <h1>感谢大家支持江小年的博客</h1> 获取后端的数据为: {{.msg}} </body> </html>
获取参数
获取请求参数
-
// usl?userid=XXX&username=jaingxionian // /user/info/1/jiangxiaonian
-
// main.go package main import ( "net/http" "github.com/gin-gonic/gin" ) func main() { // 创建一个服务 ginServer := gin.Default() // ginServer.Use(favicon.New("./favicon.ico")) // 加载静态页面 ginServer.LoadHTMLGlob("templates/*") // ginServer.LoadHTMLFiles("templates/index.html") // LoadHTMLGlob是全局加载Files是指定文件 //加载资源文件 ginServer.Static("/static", "./static") // 响应一个页面给前端 ginServer.GET("/index", func(context *gin.Context) { // context.JSON() json数据 context.HTML(http.StatusOK, "index.html", gin.H{ "msg": "这是go后台传入的数据", }) }) // usl?userid=XXX&username=jaingxionian ginServer.GET("/user/info", func(context *gin.Context) { userid := context.Query("userid") username := context.Query("username") context.JSON(http.StatusOK, gin.H{ "userid": userid, "username": username, }) }) // /user/info/1/jiangxiaonian // 只要:后名字正确就能匹配上 ginServer.GET("/user/info/:userid/:username", func(context *gin.Context) { userid := context.Param("userid") username := context.Param("username") context.JSON(http.StatusOK, gin.H{ "userid": userid, "username": username, }) }) // 服务器端口 err := ginServer.Run(":8082") if err != nil { return } } // 运行后访问 http://localhost:8082/user/info?userid=1&username=jaingxiaonain
获取前端给后端传递的json
(序列化)参数
// gin 优化过的BindJSON
package controllers
import "github.com/gin-gonic/gin"
type OrderController struct{}
func (o OrderController) GetList(c *gin.Context) {
m := make(map[string]interface{})
err := c.BindJSON(&m)
if err == nil {
c.JSON(http.StatusOK, m)
return
}
c.JSON(4001, gin.H{"err": err})
}
// 结构体
package controllers
import "github.com/gin-gonic/gin"
type OrderController struct{}
type Search struct {
Name string `json:"name"`
Cid int `json:"cid"`
}
func (o OrderController) GetList(c *gin.Context) {
search := &Search{}
err := c.BindJSON(&search)
if err == nil {
ReturnSuccess(c, 0, search.Name, search.Cid, 1)
return
}
ReturnErrer(c, 4001, gin.H{"err": err})
}
// 结构体 package controllers import "github.com/gin-gonic/gin" type OrderController struct{} type Search struct { Name string `json:"name"` Cid int `json:"cid"` } func (o OrderController) GetList(c *gin.Context) { search := &Search{} err := c.BindJSON(&search) if err == nil { ReturnSuccess(c, 0, search.Name, search.Cid, 1) return } ReturnErrer(c, 4001, gin.H{"err": err}) }
获取表单中的参数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>一个GoWeb页面</title>
<link rel="stylesheet" href="/static/css/style.css">
<script src="/static/js/common.js"></script>
</head>
<body>
<h1>感谢大家支持江小年的博客</h1>
<form action="/user/add" method="post">
<p>username: <input type="text" name="username"></p>
<p>password: <input type="password" name="password"></p>
<button type="submit"> 提交 </button>
</form>
</body>
</html>
// .DefaultPostForm() .PostForm()
package main
import (
"encoding/json"
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
// 创建一个服务
ginServer := gin.Default()
// ginServer.Use(favicon.New("./favicon.ico"))
// 加载静态页面
ginServer.LoadHTMLGlob("templates/*")
// ginServer.LoadHTMLFiles("templates/index.html")
// LoadHTMLGlob是全局加载Files是指定文件
//加载资源文件
ginServer.Static("/static", "./static")
// 响应一个页面给前端
ginServer.GET("/index", func(context *gin.Context) {
// context.JSON() json数据
context.HTML(http.StatusOK, "index.html", gin.H{
"msg": "这是go后台传入的数据",
})
})
// 表单
ginServer.POST("/user/add", func(context *gin.Context) {
username := context.PostForm("username")
password := context.PostForm("password")
// password := context.DefaultPostForm("password", 12345)第二个参数为默认值
// 加判断逻辑代码
context.JSON(http.StatusOK, gin.H{
"msg": "ok",
"username": username,
"password": password,
})
})
// 服务器端口
err := ginServer.Run(":8082")
if err != nil {
return
}
}
路由
HTTP
重定向
-
package main import ( "encoding/json" "net/http" "github.com/gin-gonic/gin" ) func main() { // 创建一个服务 ginServer := gin.Default() // 响应一个页面给前端 ginServer.GET("/index", func(context *gin.Context) { // context.JSON() json数据 context.HTML(http.StatusOK, "index.html", gin.H{ "msg": "这是go后台传入的数据", }) }) // 路由 ginServer.GET("/test", func(context *gin.Context) { // 重定向 StatusMovedPermanently 301 context.Redirect(http.StatusMovedPermanently, "https://www.baidu.com") }) // 服务器端口 err := ginServer.Run(":8082") if err != nil { return } }
路由重定向
路由重定向,使用HandleContext
:
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
r.GET("/test", func(c *gin.Context) {
// 指定重定向的URL
c.Request.URL.Path = "/test2"
r.HandleContext(c)
})
r.GET("/test2", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"hello": "world"})
})
// Listen and serve on 0.0.0.0:8080
err := r.Run(":8080")
if err != nil {
return
}
}
404 NoRoute
()
-
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>404</title> </head> <body> <h1>江小年的404页面</h1> </body> </html>
-
package main import ( "encoding/json" "net/http" "github.com/gin-gonic/gin" ) func main() { // 创建一个服务 ginServer := gin.Default() // 路由 ginServer.GET("/test", func(context *gin.Context) { // 重定向 StatusMovedPermanently 301 context.Redirect(http.StatusMovedPermanently, "https://www.baidu.com") }) // 404 NoRoute ginServer.NoRoute(func(context *gin.Context) { context.HTML(http.StatusNotFound, "404.html", nil) }) // 服务器端口 err := ginServer.Run(":8082") if err != nil { return } }
路由组
package main
import (
"encoding/json"
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
// 创建一个服务
ginServer := gin.Default()
// ginServer.Use(favicon.New("./favicon.ico"))
// 加载静态页面
ginServer.LoadHTMLGlob("templates/*")
// ginServer.LoadHTMLFiles("templates/index.html")
// LoadHTMLGlob是全局加载Files是指定文件
//加载资源文件
ginServer.Static("/static", "./static")
// 响应一个页面给前端
ginServer.GET("/index", func(context *gin.Context) {
// context.JSON() json数据
context.HTML(http.StatusOK, "index.html", gin.H{
"msg": "这是go后台传入的数据",
})
})
// usl?userid=XXX&username=jaingxionian
ginServer.GET("/user/info", func(context *gin.Context) {
userid := context.Query("userid")
username := context.Query("username")
context.JSON(http.StatusOK, gin.H{
"userid": userid,
"username": username,
})
})
// /user/info/1/jiangxiaonian
// 只要:后名字正确就能匹配上
ginServer.GET("/user/info/:userid/:username", func(context *gin.Context) {
userid := context.Param("userid")
username := context.Param("username")
context.JSON(http.StatusOK, gin.H{
"userid": userid,
"username": username,
})
})
// 前段给后端传JSON
ginServer.POST("/json", func(context *gin.Context) {
// GetRawData() 从请求体(request.body)里获取对象
b, _ := context.GetRawData()
var m map[string]interface{}
// 包装为json数据 []byte
_ = json.Unmarshal(b, &m)
context.JSON(http.StatusOK, m)
})
// 表单
ginServer.POST("/user/add", func(context *gin.Context) {
username := context.PostForm("username")
password := context.PostForm("password")
// 加判断逻辑代码
context.JSON(http.StatusOK, gin.H{
"msg": "ok",
"username": username,
"password": password,
})
})
// 路由
ginServer.GET("/test", func(context *gin.Context) {
// 重定向 StatusMovedPermanently 301
context.Redirect(http.StatusMovedPermanently, "https://www.baidu.com")
})
// 404 NoRoute
ginServer.NoRoute(func(context *gin.Context) {
context.HTML(http.StatusNotFound, "404.html", nil)
})
// 路由组
userGroup := ginServer.Group("/user")
{
userGroup.GET("/add") // /user/add
userGroup.GET("/login") // /user/add
userGroup.GET("/logout") // /user/add
}
orderGroup := ginServer.Group("/order")
{
orderGroup.GET("add")
orderGroup.GET("delte")
}
// 服务器端口
err := ginServer.Run(":8082")
if err != nil {
return
}
}
路由嵌套
shopGroup := r.Group("/shop")
{
shopGroup.GET("/index", func(c *gin.Context) {...})
shopGroup.GET("/cart", func(c *gin.Context) {...})
shopGroup.POST("/checkout", func(c *gin.Context) {...})
// 嵌套路由组
xx := shopGroup.Group("xx")
xx.GET("/oo", func(c *gin.Context) {...})
}
中间件(Java中为拦截器)
package main
import (
"encoding/json"
"log"
"net/http"
"github.com/gin-gonic/gin"
)
// 自定义Go中间件 拦截器
func myHandler() gin.HandlerFunc {
return func(context *gin.Context) {
// Set一些值用作全局变量
// 通过自定义的中间件,设置的值,在后续处理只要调用了这个中间件的都可以拿到这里的参数
context.Set("usersession", "userid-1")
context.Next() // 放形
/* if XXX {
context.Next() // 放形
}
context.Abort() // 阻止
// 注册未指定就是全局使用
*/
}
/* 46行加入中间件 */
}
func main() {
// 创建一个服务
ginServer := gin.Default()
// ginServer.Use(favicon.New("./favicon.ico"))
// 加载静态页面
ginServer.LoadHTMLGlob("templates/*")
// ginServer.LoadHTMLFiles("templates/index.html")
// LoadHTMLGlob是全局加载Files是指定文件
//加载资源文件
ginServer.Static("/static", "./static")
// 响应一个页面给前端
ginServer.GET("/index", func(context *gin.Context) {
// context.JSON() json数据
context.HTML(http.StatusOK, "index.html", gin.H{
"msg": "这是go后台传入的数据",
})
})
// usl?userid=XXX&username=jaingxionian
ginServer.GET("/user/info", myHandler(), func(context *gin.Context) {
// 取出中间件中的值
usersession := context.MustGet("usersession").(string)
log.Println("========>", usersession) // 前端控制台输出
userid := context.Query("userid")
username := context.Query("username")
context.JSON(http.StatusOK, gin.H{
"userid": userid,
"username": username,
})
})
// /user/info/1/jiangxiaonian
// 只要:后名字正确就能匹配上
ginServer.GET("/user/info/:userid/:username", func(context *gin.Context) {
userid := context.Param("userid")
username := context.Param("username")
context.JSON(http.StatusOK, gin.H{
"userid": userid,
"username": username,
})
})
// 前段给后端传JSON
ginServer.POST("/json", func(context *gin.Context) {
// GetRawData() 从请求体(request.body)里获取对象
b, _ := context.GetRawData()
var m map[string]interface{}
// 包装为json数据 []byte
_ = json.Unmarshal(b, &m)
context.JSON(http.StatusOK, m)
})
// 表单
ginServer.POST("/user/add", func(context *gin.Context) {
username := context.PostForm("username")
password := context.PostForm("password")
// 加判断逻辑代码
context.JSON(http.StatusOK, gin.H{
"msg": "ok",
"username": username,
"password": password,
})
})
// 路由
ginServer.GET("/test", func(context *gin.Context) {
// 重定向 StatusMovedPermanently 301
context.Redirect(http.StatusMovedPermanently, "https://www.baidu.com")
})
// 404 NoRoute
ginServer.NoRoute(func(context *gin.Context) {
context.HTML(http.StatusNotFound, "404.html", nil)
})
// 路由组
userGroup := ginServer.Group("/user")
{
userGroup.GET("/add") // /user/add
userGroup.GET("/login") // /user/add
userGroup.GET("/logout") // /user/add
}
orderGroup := ginServer.Group("/order")
{
orderGroup.GET("add")
orderGroup.GET("delte")
}
// 服务器端口
err := ginServer.Run(":8082")
if err != nil {
return
}
}
// StatCost 是一个统计耗时请求耗时的中间件
func StatCost() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
c.Set("name", "wxy") // 可以通过c.Set在请求上下文中设置值,后续的处理函数能够取到该值
// 调用该请求的剩余处理程序
c.Next()
// 不调用该请求的剩余处理程序
// c.Abort()
// 计算耗时
cost := time.Since(start)
log.Println(cost)
}
}
文件上传
单个文件上传
文件上传前端页面代码:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<title>上传文件示例</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="f1">
<input type="submit" value="上传">
</form>
</body>
</html>
后端gin框架部分代码:
func main() {
router := gin.Default()
// 处理multipart forms提交文件时默认的内存限制是32 MiB
// 可以通过下面的方式修改
// router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.POST("/upload", func(c *gin.Context) {
// 单个文件
file, err := c.FormFile("f1")
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": err.Error(),
})
return
}
log.Println(file.Filename)
dst := fmt.Sprintf("C:/tmp/%s", file.Filename)
// 上传文件到指定的目录
c.SaveUploadedFile(file, dst)
c.JSON(http.StatusOK, gin.H{
"message": fmt.Sprintf("'%s' uploaded!", file.Filename),
})
})
router.Run()
}
多个文件上传
func main() {
router := gin.Default()
// 处理multipart forms提交文件时默认的内存限制是32 MiB
// 可以通过下面的方式修改
// router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.POST("/upload", func(c *gin.Context) {
// Multipart form
form, _ := c.MultipartForm()
files := form.File["file"]
for index, file := range files {
log.Println(file.Filename)
dst := fmt.Sprintf("./upload/%s_%d", file.Filename, index)
// 上传文件到指定的目录
c.SaveUploadedFile(file, dst)
}
c.JSON(http.StatusOK, gin.H{
"message": fmt.Sprintf("%d files uploaded!", len(files)),
})
})
router.Run()
}
异常捕获
func main() {
router := gin.Default()
// 处理multipart forms提交文件时默认的内存限制是32 MiB
// 可以通过下面的方式修改
// router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.POST("/upload", func(c *gin.Context) {
// Multipart form
form, _ := c.MultipartForm()
files := form.File["file"]
for index, file := range files {
log.Println(file.Filename)
dst := fmt.Sprintf("./upload/%s_%d", file.Filename, index)
// 上传文件到指定的目录
c.SaveUploadedFile(file, dst)
}
c.JSON(http.StatusOK, gin.H{
"message": fmt.Sprintf("%d files uploaded!", len(files)),
})
})
router.Run()
}
日志打印
自己搞哟,很重要的,输出错误,前段是看不到的