1.Go Web入门

目录

1.Web

2.使用Gin框架构建一个简单的web服务器

3.Gin返回jSON

3.1使用map

3.2使用map封装的gin.H

3.3结构体

4.获取参数

4.1获取querystring的参数

4.1.1Query

 4.1.2DefaultQuery

 4.1.3GetQuery

4.2获取form表单的数据

4.2.1PostForm

4.2.2DefaultPostForm

 4.2.3GetPostForm

4.3获取路径参数

5.绑定参数

5.1结构体手动参数绑定

5.2ShouldBind

6.上传文件

7.请求重定向和请求转发

8.路由和路由组

8.1路由

8.2路由器组

9中间件

9.1定义中间件

9.2注册中间件

9.2.1为全局路由注册

9.2.2为单个路由注册中间件

9.2.3为路由器组注册中间件

9.3中间件注意事项


1.Web

Web就是一个请求一个响应

package main

import (
	"fmt"
	"net/http"
)

func sayHello(w http.ResponseWriter, r *http.Request) { // 定义一个
	fmt.Fprintln(w, "hello world!")
}

func main() {
	http.HandleFunc("/hello", sayHello)  //浏览器访问/hello,将会转到sayHello去处理
	http.ListenAndServe(":8888", nil) // 监听端口并处理请求
}

2.使用Gin框架构建一个简单的web服务器

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func sayHello(c *gin.Context) { //不同于net/http库的路由函数,gin.Context封装了request和response
	c.String(http.StatusOK, "Hello World!")
}

func main() {
	r := gin.Default() //1.指定默认路由
	r.GET("/hello", sayHello) //2.浏览器访问/hello,将由sayHello函数处理
	r.Run(":8080") //3.监听端口
}

3.Gin返回jSON

3.1使用map

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()
	data := map[string]interface{}{
		"message": "seesunman",
	}
	r.GET("json", func(c *gin.Context) {
		c.JSON(http.StatusOK, data)
	})
	r.Run(":8080")
}

3.2使用map封装的gin.H

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()
	data := gin.H{
		"message": "seesunman",
		"age":     "18",
	}
	r.GET("json", func(c *gin.Context) {
		c.JSON(http.StatusOK, data)
	})
	r.Run(":8080")
}

3.3结构体

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

type User struct {
	Name string
	Age  int
}

func main() {
	r := gin.Default()
	data := User{
		Name: "seesunman",
		Age:  21,
	}
	r.GET("json", func(c *gin.Context) {
		c.JSON(http.StatusOK, data)
	})
	r.Run(":8080")
}

4.获取参数

4.1获取querystring的参数

        有Query、DefaultQuery、GetQuery

4.1.1Query

        Query返回key对应的value,如果key不存在返回""
        ""不等同于nil!!!

// Query returns the keyed url query value if it exists,
// otherwise it returns an empty string `("")`.
// It is shortcut for `c.Request.URL.Query().Get(key)`
//     GET /path?id=1234&name=Manu&value=
// 	   c.Query("id") == "1234"
// 	   c.Query("name") == "Manu"
// 	   c.Query("value") == ""
// 	   c.Query("wtf") == ""
func (c *Context) Query(key string) string {
	value, _ := c.GetQuery(key)
	return value
}

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()
	r.LoadHTMLFiles("login.html")
	r.GET("/hello", func(c *gin.Context) {
		username := c.Query("username")
		password := c.Query("password")
		c.JSON(http.StatusOK, gin.H{
			"Username": username,
			"Password": password,
		})
	})
	r.Run(":8080")
}

 4.1.2DefaultQuery

        key存在时返回对应的value,不存在返回默认的value

// DefaultQuery returns the keyed url query value if it exists,
// otherwise it returns the specified defaultValue string.
// See: Query() and GetQuery() for further information.
//     GET /?name=Manu&lastname=
//     c.DefaultQuery("name", "unknown") == "Manu"
//     c.DefaultQuery("id", "none") == "none"
//     c.DefaultQuery("lastname", "none") == ""
func (c *Context) DefaultQuery(key, defaultValue string) string {
	if value, ok := c.GetQuery(key); ok {
		return value
	}
	return defaultValue
package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()
	r.LoadHTMLFiles("login.html")
	r.GET("/hello", func(c *gin.Context) {
		// username := c.Query("username")
		username := c.DefaultQuery("xxx", "梁朝伟")
		password := c.Query("password")
		c.JSON(http.StatusOK, gin.H{
			"Username": username,
			"Password": password,
		})
	})
	r.Run(":8080")
}

 4.1.3GetQuery

        返回两个元素,第一个元素和Query返回的一样,第二个元素返回真假

        查找指定参数时返回(value,true)

        未查找到指定参数时返回("", false)

// GetQuery is like Query(), it returns the keyed url query value
// if it exists `(value, true)` (even when the value is an empty string),
// otherwise it returns `("", false)`.
// It is shortcut for `c.Request.URL.Query().Get(key)`
//     GET /?name=Manu&lastname=
//     ("Manu", true) == c.GetQuery("name")
//     ("", false) == c.GetQuery("id")
//     ("", true) == c.GetQuery("lastname")
func (c *Context) GetQuery(key string) (string, bool) {
	if values, ok := c.GetQueryArray(key); ok {
		return values[0], ok
	}
	return "", false
}

4.2获取form表单的数据

4.2.1PostForm

        表单中如果有key,则返回对应的value;没有key则返回空字符串""

// PostForm returns the specified key from a POST urlencoded form or multipart form
// when it exists, otherwise it returns an empty string `("")`.
func (c *Context) PostForm(key string) string {
	value, _ := c.GetPostForm(key)
	return value
}
package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()
	r.POST("/form", func(c *gin.Context) {
		username := c.PostForm("username")
		password := c.PostForm("password")
		c.JSON(http.StatusOK, gin.H{
			"Username": username,
			"Password": password,
		})
	})
	r.Run(":8080")

}

4.2.2DefaultPostForm

        有key返回value,没key返回默认value

// DefaultPostForm returns the specified key from a POST urlencoded form or multipart form
// when it exists, otherwise it returns the specified defaultValue string.
// See: PostForm() and GetPostForm() for further information.
func (c *Context) DefaultPostForm(key, defaultValue string) string {
	if value, ok := c.GetPostForm(key); ok {
		return value
	}
	return defaultValue
}
package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()
	r.POST("/form", func(c *gin.Context) {
		username := c.DefaultPostForm("username", "seesunman")
		password := c.PostForm("password")
		c.JSON(http.StatusOK, gin.H{
			"Username": username,
			"Password": password,
		})
	})
	r.Run(":8080")

}

 4.2.3GetPostForm

        和PostForm一致,但多了一个返回值表示true/false

// GetPostForm is like PostForm(key). It returns the specified key from a POST urlencoded
// form or multipart form when it exists `(value, true)` (even when the value is an empty string),
// otherwise it returns ("", false).
// For example, during a PATCH request to update the user's email:
//     email=mail@example.com  -->  ("mail@example.com", true) := GetPostForm("email") // set email to "mail@example.com"
// 	   email=                  -->  ("", true) := GetPostForm("email") // set email to ""
//                             -->  ("", false) := GetPostForm("email") // do nothing with email
func (c *Context) GetPostForm(key string) (string, bool) {
	if values, ok := c.GetPostFormArray(key); ok {
		return values[0], ok
	}
	return "", false
}

4.3获取路径参数

        在/后的路径前加:,来设置路径变量。

        使用c.param("路径变量")获取路径变量

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()
	r.GET("/user/:name/:age", func(c *gin.Context) {
		name := c.Param("name")
		age := c.Param("age")
		c.JSON(http.StatusOK, gin.H{
			"name": name,
			"age":  age,
		})
	})
	r.Run(":8080")
}

5.绑定参数

5.1结构体手动参数绑定

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

type User struct {
	Name     string
	Password string
}

func main() {
	r := gin.Default()
	r.GET("/hello", func(c *gin.Context) {
		username := c.Query("username")
		password := c.Query("password")
		u := User{
			Name:     username,
			Password: password,
		}
		c.JSON(http.StatusOK, u)
	})
	r.Run(":8080")
}

5.2ShouldBind

        支持处理的数据有:querystring、form表单、json格式

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

type UserInfo struct {
	Username string `form:"username" json:"username"`
	Password string `form:"password" json:"password"`
}

func main() {
	c := gin.Default()
	c.POST("/hello", func(c *gin.Context) {
		var u UserInfo
		c.ShouldBind(&u)
		c.JSON(http.StatusOK, u)
	})
	c.Run(":8080")
}

 

6.上传文件

前端代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</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框架代码

package main

import (
	"net/http"
	"path"

	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()

	r.LoadHTMLGlob("lesson15/*")

	r.GET("/index", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.html", nil)
	})

	r.POST("/upload", func(c *gin.Context) {
		// 从请求中读取文件
		f, err := c.FormFile("f1") // 从请求中获取携带的参数一样
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{
				"err": err.Error(),
			})
		} else {
			// 将读取到的文件保存到本地(服务端)
			// dst := fmt.Sprintf("./%s", f.Filename)
			dst := path.Join("./lesson15/", f.Filename)
			c.SaveUploadedFile(f, dst)
			c.JSON(http.StatusOK, gin.H{
				"status": "ok",
			})
		}

	})
	r.Run(":8080")
}

7.请求重定向和请求转发

请求重定向

	r.GET("/index", func(c *gin.Context) {
		c.Redirect(http.StatusMovedPermanently, "http://seesunman.top")
	})

请求转发

	r.GET("/a", func(c *gin.Context) {
		// 跳转到 /b 对应的路由处理函数
		c.Request.URL.Path = "/b" // 把请求的URI修改
		r.HandleContext(c) // 继续后续的处理
	})

	r.GET("/b", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"message": "b",
		})
	})

8.路由和路由组

8.1路由

        路由:解析URL,调用对应的控制器(函数)

4种常用请求

	r.GET("/index", func(ctx *gin.Context) {
		ctx.JSON(http.StatusOK, gin.H{
			"method": "GET",
		})
	})

	r.POST("/index", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"method": "POST",
		})
	})
	// PUT用于更新
	r.PUT("/index", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"method": "PUT",
		})
	})
	// DELETE用于删除API
	r.DELETE("/index", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"method": "DELETE",
		})
	})

Any:可以接受任意请求

	// Any:请求方法大杂烩
	r.Any("/user", func(c *gin.Context) {
		switch c.Request.Method {
		case http.MethodGet:
			c.JSON(http.StatusOK, gin.H{"method": "GET"})
		case http.MethodPost:
			c.JSON(http.StatusOK, gin.H{"method": "POST"})
			// ...
		}
	})

NoRouter:专门处理不能处理的路由

	r.NoRoute(func(c *gin.Context) {
		c.JSON(http.StatusNotFound, gin.H{"msg": "not found"})
	})

8.2路由器组

	// 路由组的组
	// 把公用的前缀提取出来,创建一个路由组
	videoGroup := r.Group("/video")
	{
		videoGroup.GET("/index", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{"message": "index"})
		})
		videoGroup.GET("/xx", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{"message": "xx"})
		})
		videoGroup.GET("/oo", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{"message": "oo"})
		})
	}
    // 可以访问/video/index、/video/xx、/video/oo

9中间件

        Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。

9.1定义中间件

        Gin的中间件必须是一个gin.HandleFunc类型

func m1(c *gin.Context) {
	//定义一个计时的中间件
	start := time.Now()
	c.Next()
	cost := time.Since(start)
	fmt.Printf("cost: %v\n", cost)
}

c.Next() // 调用该请求的剩余处理程序

c.Abort() // 不调用该请求的剩余处理程序

c.Set() // 在上下文中设置值,方便Get获取

c.Get  //获取上下文中的值

9.2注册中间件

定义有如下函数indexHandler和中间件m1

func indexHandler(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{"msg": "index"})
}

func m1(c *gin.Context) {
	//定义一个计时的中间件
	start := time.Now()
	c.Next()
	cost := time.Since(start)
	fmt.Printf("cost: %v\n", cost)
}

9.2.1为全局路由注册

func main() {
	r := gin.Default()

	r.Use(m1) //为全局路由注册中间件

	r.GET("/index", indexHandler)

	r.Run(":8090")
}

9.2.2为单个路由注册中间件

func main() {
	r := gin.Default()

	r.GET("/index", m1, indexHandler)

	r.Run(":8090")
}

9.2.3为路由器组注册中间件

	// 写法1
    indexGroup := r.Group("/index", m1)
	{
		indexGroup.GET("/xx", indexHandler)
	}
    // 写法2
    index2Group := r.Group("/index")
	{
		index2Group.Use(m1)
		index2Group.GET("/xx2", indexHandler)
	}

9.3中间件注意事项

· gin.Default()默认使用了Logger和Recovery中间件。

// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {
	debugPrintWARNINGDefault()
	engine := New()
	engine.Use(Logger(), Recovery())
	return engine
}

· gin中间件使用goroutine

        当在中间件或者hangler中启动新的goroutine时,不能使用原始的上下文(c *gin.Context),必须使用其只读副本(c.Copy)

    go funcXX(c.Copy())

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值