Go Web学习笔记(Gin和Gorm)

本人在学长的推荐下,之后就要用Go开发后端啦!之前已经把go的基础语法过了一遍,现在学习Gin和Gorm框架,特此记录一下,也希望对你们有帮助,当然因为本人是Go新手,所以有写的不对的地方尽情指教,谢谢!

这里顺便再提供一下Go基础语法的中文官方文档

注:本篇文章大部分是基于https://www.topgoer.com/gin%E6%A1%86%E6%9E%B6这个网址写的学习笔记,其他一些则是去网上找的资料和视频,感谢大佬提供的观点和代码!!

Gin框架

用go原生的https包写hello world

package main

import (
	"fmt"
	"net/http" //引用http包
)

func sayHello(w http.ResponseWriter, r *http.Request)  {
	_, _ = fmt.Fprintf(w, "<h1>Hello Golang!</h1>")
	// b, _ := ioutil.ReadFile("./hello.txt") //ioutil这个函数能够读去文本文件
	// _, _ = fmt.Fprintf(w, string(b))
}

func main() {
	http.HandleFunc("/hello", sayHello) //访问 http://localhost:8080/hello 这个url
	err := http.ListenAndServe(":8080",nil) //监听8080端口,url中可以不用写(因为默认就是8080)
	if err != nil{
		fmt.Printf("ERROR:%v\n", err)
		return
	}
}

小记: 下载Gin框架 命令行输入:go get -u github.com/gin-gonic/gin


GET请求

package main

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

func main() {
	r := gin.Default() // 返回默认的路由引擎

	//指定用户使用GET请求访问/hello时,会执行sayhello这个函数
	r.GET("/hello", func(c *gin.Context) { //一定要是*gin.Context类型的参数
		c.JSON(200,gin.H{
			"message":"Hello golang!",
		})
	})

	//启动服务
	r.Run(":9090") //改变默认端口号,注意要加 ":" !!!
	//r.Run()
}

小记:Resful风格的请求:GET:获取、 POST:创建、 DELETE:删除、 PUT:更新


POSTMan安装

这个网上有很多教程,这里就不多提了,提几个重要的点吧:
1.首先这个软件是要用梯子的,包括下载和登陆注册
2.建议下载软件版
3.下载好后新建一个work space之后就可以开始操作了
在这里插入图片描述
小记:template框架因为要读取一个html文件,没有做到前后端分离,我个人认为比较落后,这里就先不学它了


获取参数、文件的方式

API获取

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
	"strings"
)

func main() {
	r := gin.Default()
	r.GET("/user/:name/*action", func(c *gin.Context) { //通过匿名函数的方式实现的对url的抓取
		name := c.Param("name") //获得url中的name
		action := c.Param("action")
		//截取/
		action = strings.Trim(action, "/") //获得url中的最后一个参数
		c.String(http.StatusOK, name+" is "+action)
	})
	//默认为监听8080端口
	r.Run(":8000")
}

在这里插入图片描述
URL中获取参数(Get方式)
用Query()来获得参数
用DefaultQuery()来设置默认参数

package main

import (
	"fmt"
	"net/http"

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

func main() {
	r := gin.Default()
	r.GET("/user", func(c *gin.Context) {
		//指定默认值
		//http://localhost:8080/user 才会打印出来默认的值
		name := c.DefaultQuery("name", "小恐龙")
		c.String(http.StatusOK, fmt.Sprintf("hello %s", name))
	})
	r.Run()
}

当name没有传进get参数时,返回为默认的小恐龙:

在这里插入图片描述
当然也可以传进参数:
在这里插入图片描述
表单获取参数(一般是Post方式)

表单参数可以通过PostForm()方法获取,该方法默认解析的是x-www-form-urlencoded或from-data格式的参数
(这里也同样没有前后端分离,不推荐)
html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <form action="http://localhost:8080/form" method="post" action="application/x-www-form-urlencoded">
        用户名:<input type="text" name="username" placeholder="请输入你的用户名">  <br>&nbsp;&nbsp;&nbsp;码:<input type="password" name="userpassword" placeholder="请输入你的密码">  <br>
        <input type="submit" value="提交">
    </form>
</body>
</html>

go后台文件

package main

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

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

func main() {
    r := gin.Default()
    r.POST("/form", func(c *gin.Context) {
        types := c.DefaultPostForm("type", "post")
        username := c.PostForm("username")
        password := c.PostForm("userpassword")
        c.String(http.StatusOK, fmt.Sprintf("username:%s,password:%s,type:%s", username, password, types))
    })
    r.Run()
}

上传文件(单个或多个)
html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<form action="http://localhost:8080/upload" method="post" enctype="multipart/form-data">
    上传文件:<input type="file" name="file" >
    <input type="submit" value="提交">
</form>
</body>
</html>

用FormFile()函数来获得上传的文件
SaveUploadedFile用于保存

go文件:

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()
	// 限制表单上传大小 8MB,默认为32MB
	r.MaxMultipartMemory = 8 << 20
	r.POST("/upload", func(c *gin.Context) {
		file, err := c.FormFile("file")
		if err != nil {
			c.String(500, "上传图片出错")
		}
		//c.JSON(200, gin.H{"message": file.Header.Context})
		c.SaveUploadedFile(file, file.Filename)
		c.String(http.StatusOK, file.Filename)
	})
	r.Run()
}

gin好像暂时没有限制文件大小的函数,那么我们就自己写一个,也不是很难。
就是一个要注意的点是要把原来的c.FormFile变成c.Request.FormFile。

package main

import (
	"log"
	"net/http"

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

func main() {
	r := gin.Default()
		
	//上传单个文件
	r.POST("/upload", func(c *gin.Context) {
		_, headers, err := c.Request.FormFile("file")
		if err != nil {
			log.Printf("Error when try to get file: %v", err)
		}
		//headers.Size 获取文件大小
		if headers.Size > 1024*1024*2 { //限制2MB的大小
			c.String(200,"文件太大了")
			return
		//获取文件类型
		}else if headers.Header.Get("Content-Type") != "image/png" {
			c.String(200,"只允许上传png图片")
			return
		}else{
			//Go返回json数据的方法之一;利用map(还有一种方法是用结构体)
			my_json := map[string]interface{}{ 
				"1":"成功",
				"2":true,
			}
			c.JSON(200,my_json)
		}
		//headers.Header.Get("Content-Type")获取上传文件的类型

		c.SaveUploadedFile(headers, "./video/"+headers.Filename)
		c.String(http.StatusOK, headers.Filename)
	})
	
	//上传多个文件
	//r.POST("/upload", func(c *gin.Context) {
	//	form, err := c.MultipartForm()
	//	if err != nil {
	//		c.String(http.StatusBadRequest, fmt.Sprintf("get err %s", err.Error()))
	//	}
	//	// 获取所有图片
	//	files := form.File["files"]
	//	// 遍历所有图片
	//	for _, file := range files {
	//		// 逐个存
	//		if err := c.SaveUploadedFile(file, file.Filename); err != nil {
	//			c.String(http.StatusBadRequest, fmt.Sprintf("upload err %s", err.Error()))
	//			return
	//		}
	//	}
	//	c.String(200, fmt.Sprintf("upload ok %d files", len(files)))
	//})
	r.Run()
}

路由和路由组

路由:就是一个通过一个路径去用一种方法去请求数据
路由中不同的请求方法以及后台处理

r.any()可以对任何请求做出处理
r.NoRoute()可以自定义404页面
示例:

package main

import (
	"github.com/gin-gonic/gin"
)
func main() {
	// 默认使用了2个中间件Logger(), Recovery()
	r := gin.Default()
	r.NoRoute(func(c *gin.Context) {
		c.JSON(404, gin.H{"msg": "12138"})
	})
	r.Run()
}

路由组

package main

import (
   "github.com/gin-gonic/gin"
   "fmt"
)

// gin的helloWorld

func main() {
   // 1.创建路由
   // 默认使用了2个中间件Logger(), Recovery()
   r := gin.Default()
   // 路由组1 ,处理GET请求
   v1 := r.Group("/v1")
   // {} 是书写规范
   {
      v1.GET("/login", login)
      v1.GET("submit", submit)
      
      //路由组的可以嵌套的
      //xx := v1.shopGroup("xx")
      //xx.GET("/oo",my_func)
      
   }
   v2 := r.Group("/v2")
   {
      v2.POST("/login", login)
      v2.POST("/submit", submit)
   }
   r.Run(":8000")
}

func login(c *gin.Context) {
   name := c.DefaultQuery("name", "jack")
   c.String(200, fmt.Sprintf("hello %s\n", name))
}

func submit(c *gin.Context) {
   name := c.DefaultQuery("name", "lily")
   c.String(200, fmt.Sprintf("hello %s\n", name))
}

效果图:
在这里插入图片描述
在这里插入图片描述
小记: 不同文件夹下要导入函数的话,导出的函数首字母需要大写

JSON数据的解析和绑定:

小记:gin.h{...}和map[string]interface{}{...}的效果是一样的

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

// 定义接收数据的结构体
type Login struct {
	// binding:"required"修饰的字段,若接收为空值,则报错,是必须字段
	User    string `form:"username" json:"user" uri:"user" xml:"user" binding:"required"`
	Pssword string `form:"password" json:"password" uri:"password" xml:"password" binding:"required"`
}

func main() {
	// 1.创建路由
	// 默认使用了2个中间件Logger(), Recovery()
	r := gin.Default()
	// JSON绑定
	r.POST("loginJSON", func(c *gin.Context) {
		// 声明接收的变量
		var json Login //声明json为Login类型的结构体
		// 将request的body中的数据,自动按照json格式解析到结构体
		if err := c.ShouldBindJSON(&json); err != nil {
		//c.ShouldBindUri(...)是用于解析url中的参数的
		//c.Bind(...)是用于解析form中的参数的
			// 返回错误信息
			// gin.H封装了生成json数据的工具
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}
		// 判断用户名密码是否正确
		if json.User != "root" || json.Pssword != "admin" {
			c.JSON(http.StatusBadRequest, gin.H{"status": "304"})
			return
		}
		c.JSON(http.StatusOK, gin.H{"status": "200"})
	})
	r.Run(":8000")
}

注意: 上面结构体定义时使用了结构体tag,因为go的限制,所以结构体内名字只能返回大写开头,于是我们就可以用tag去定制结构体在不同方法请求时所传入参数名称的不同
User string `form:"username" json:"user" uri:"user" xml:"user" binding:"required"
比如这句话,用form请求时就应该是username
binding:"required"就说明是必要参数,若接收为空值,则报错,是必须字段。

重定向

package main

import (
    "net/http"

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

func main() {
    r := gin.Default()
    r.GET("/index", func(c *gin.Context) {
        c.Redirect(http.StatusMovedPermanently, "http://www.5lmh.com")
    })
    r.Run()
}

异步执行和同步执行

这里用到了go程的概念,没有了解的小伙伴可以去康康文档

package main

import (
    "log"
    "time"

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

func main() {
    // 1.创建路由
    // 默认使用了2个中间件Logger(), Recovery()
    r := gin.Default()
    // 1.异步
    r.GET("/long_async", func(c *gin.Context) {
        // 需要搞一个副本
        copyContext := c.Copy()
        // 异步处理
        go func() {
            time.Sleep(3 * time.Second)
            log.Println("异步执行:" + copyContext.Request.URL.Path)
        }()
    })
    // 2.同步
    r.GET("/long_sync", func(c *gin.Context) {
        time.Sleep(3 * time.Second)
        log.Println("同步执行:" + c.Request.URL.Path)
    })

    r.Run(":8000")
}

效果:
在这里插入图片描述

中间件

局部中间件
用于在浏览器和服务器中间处理的函数,一般是所有网站都需要使用的函数,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。
原理图类似下图:
在这里插入图片描述

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"time"
)

func hello(c *gin.Context){
	c.JSON(200,gin.H{
		"msg":"index",
	})
}

func m1(c *gin.Context){
	fmt.Println("进入验证...")
	start := time.Now()
	c.Next() // 调用后续处理函数(hello) !!!important
	//c.Abort() //阻止调用后续的处理函数
	cost := time.Since(start)
	fmt.Printf("cost:%v\n", cost)
}

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

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

	r.Run()
}

像上面的m1就是一个简单的中间件函数,
它在hello函数之前执行,可以决定hello函数执行的逻辑。
效果图:
在这里插入图片描述
全局中间件

用r.use(…)

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"time"
)

func hello(c *gin.Context){
	c.JSON(200,gin.H{
		"msg":"index",
	})
}

func m1(c *gin.Context){
	fmt.Println("进入验证...")
	start := time.Now()
	c.Next() // 调用后续处理函数(hello) !!!important
	//c.Abort() //阻止调用后续的处理函数
	cost := time.Since(start)
	fmt.Printf("cost:%v\n", cost)
}

func main() {
	r := gin.Default()
	//只要请求就要通过m1这个中间件(其实就是一个函数)
	r.Use(m1)

	r.GET("/index", hello)
	r.Run()
}

好用的中间件推荐
网址

会话控制:(cookie和session)

搜索、设置、删除cookie:

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
)

func main() {
	// 1.创建路由
	// 默认使用了2个中间件Logger(), Recovery()
	r := gin.Default()
	// 服务端要给客户端cookie
	r.GET("cookie", func(c *gin.Context) {
		// 获取客户端是否携带cookie
		cookie, err := c.Cookie("key_cookie")
		if err != nil {
			cookie = "NotSet"
			// 给客户端设置cookie
			//  maxAge int, 单位为秒
			// path,cookie所在目录
			// domain string,域名
			//   secure 是否智能通过https访问
			// httpOnly bool  是否允许别人通过js获取自己的cookie
			c.SetCookie("key_cookie", "value_cookie", 60, "/",
				"localhost", false, true)
				
		//删除cookie的话就可以直接设置时长为0或者为空
		//c.SetCookie("key_cookie", "", 60, "/","localhost", false, true)
		//c.SetCookie("key_cookie", "value_cookie", 0, "/","localhost", false, true)
		}
		fmt.Printf("cookie的值是: %s\n", cookie)
	})
	r.Run(":8000")
}

请求:http://localhost:8000/cookie 后查看cookie
在这里插入图片描述
小记:Cookie的缺点:1.不安全,明文、2.增加带宽消耗、3.可以被禁用、4.cookie有上限

session的设置、查找和删除(值设为nil)

package main

import (
	"github.com/gin-contrib/sessions"
	"github.com/gin-contrib/sessions/cookie"
	"github.com/gin-gonic/gin"
)

func main() {
	// 初始化一个cookie存储对象
	// something-very-secret应该是一个你自己的密匙,只要不被别人知道就行
	var store = cookie.NewStore([]byte("something-very-secret"))
	r := gin.Default()
	//使用中间件,store是之前创建的存储引擎,可以替换成其他引擎
	//mysession是之后会存储到浏览器上cookie中的名字,服务器通过这个名字去找到对应的session
	r.Use(sessions.Sessions("mysession", store))
	r.GET("/save", func(c *gin.Context) {
		session := sessions.Default(c)
		v := session.Get("count")
		var count int
		if v == nil {
			count = 0
		}else {
			count = v.(int)
			count++
		}
		session.Set("count",count)
		session.Save()
		c.JSON(200, gin.H{"now the count":count})
	})
	r.GET("/get", func(c *gin.Context) {
		session := sessions.Default(c)
		v := session.Get("count")
		c.JSON(200, gin.H{"the count":v})
	})
	err := r.Run()
	if err != nil {
		return
	}
}

效果图:
在这里插入图片描述
在这里插入图片描述
gin中是没有session包的,所以需要我们自己去导入其他的session包

参数验证

结构体验证:
在定义结构体的对象的后面写上要求

package main

import (
    "fmt"
    "time"

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

//Person ..
type Person struct {
    //不能为空并且大于10
    Age      int       `form:"age" binding:"required,gt=10"`
    Name     string    `form:"name" binding:"required"`
    Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"`
}

func main() {
    r := gin.Default()
    r.GET("/5lmh", func(c *gin.Context) {
        var person Person
        if err := c.ShouldBind(&person); err != nil {
            c.String(500, fmt.Sprint(err))
            return
        }
        c.String(200, fmt.Sprintf("%#v", person))
    })
    r.Run()
}

自定义函数验证

package main

import (
	"fmt"
	"github.com/go-playground/validator/v10"
)

type Users struct {
	Name   string `form:"name" json:"name" validate:"required,CustomValidationErrors"`//包含自定义函数
	Age   uint8 `form:"age" json:"age" validate:"required,gt=18"`
	Passwd   string `form:"passwd" json:"passwd" validate:"required,max=20,min=6"`
	Code   string `form:"code" json:"code" validate:"required,len=6"`
}

func main() {
	// 测试传入的数据
	users := &Users{
		Name:      "admin",
		Age:        121,
		Passwd:       "126783",
		Code:            "123456",
	}
	validate := validator.New()
	//注册自定义函数
	_=validate.RegisterValidation("CustomValidationErrors", CustomValidationErrors)
	err := validate.Struct(users)
	if err != nil {
		for _, err := range err.(validator.ValidationErrors) {
			fmt.Println(err)//Key: 'Users.Name' Error:Field validation for 'Name' failed on the 'CustomValidationErrors' tag
			return
		}
	}
	return
}

func CustomValidationErrors(fl validator.FieldLevel) bool {
	return fl.Field().String() != "admin"
}

CustomValidationErrors是自己定义的函数,可以自定义过滤参数

注:一定要看清楚结构体后面的tag加的是validate而不是binding,这个是因为validator版本的问题导致的,这个问题搞了我半天,,一定要看清楚validator的版本是v10的,v9、v8都可能出问题

这里再分享一个大佬对这个包使用方法发总结:网址

其他常用功能

记录日志

package main

import (
	"io"
	"os"

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

func main() {
	gin.DisableConsoleColor()

	// Logging to a file.
	f, _ := os.Create("gin.log")
	gin.DefaultWriter = io.MultiWriter(f)

	// 如果需要同时将日志写入文件和控制台,请使用以下代码。
	// gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
	r := gin.Default()
	r.GET("/ping", func(c *gin.Context) {
		c.String(200, "pong")
	})
	r.Run()
}

效果图:
在这里插入图片描述
验证码

///正在更新ing



Gorm框架

小记:英文Go Object(对象) Relational(关系) Mapping(映射)

Gorm的安装

命令行输入:

go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite

基本操作

这里以mysql为例

package main

import (
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

type Gorm_test struct {
	ID     int    //注意:如果定义了ID,则会被默认为主键
	Name   string
	Gender string
	Hobby  string
}

func main() {
	mysql_conn := fmt.Sprintf("%s:%s@(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", "root", "1234", "127.0.0.1", 3306, "mydatabase")

	//赋值给全局变量 Db 没有 := 就是赋值
	db, err := gorm.Open("mysql", mysql_conn)
	if err != nil {
		panic(err)
	}
	fmt.Println("初始化数据库成功......")

	//创建表,把结构体和数据表关联起来
	db.AutoMigrate(&Gorm_test{})

	//创建数据行
	u1 := Gorm_test{1, "小恐龙", "男", "钢琴"}
	db.Create(&u1) //这边建议加上&
	fmt.Println("添加数据成功")

	//查询
	var u Gorm_test
	//用First函数查询第一个
	db.First(&u) //查询第一个,把它赋值给u 注意,要修改结构体一定要传指针!
	fmt.Printf("u:%#v\n", u)

	//更新数据
	db.Model(&u).Update("hobby", "打击乐")
 
	fmt.Println("更改数据成功")

	//删除
	db.Delete(&u)
	fmt.Println("删除数据成功")


}

效果图:
在这里插入图片描述
小记:gorm在我们关联结构体时会创建一个数据库,名字就算把我们结构体的名字小写并且后面加上s

查询plus

package main

import (
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

type Gorm_test struct {
	ID     int    //注意:如果定义了ID,则会被默认为主键
	Name   string
	Gender string
	Hobby  string
}

func main() {
	mysql_conn := fmt.Sprintf("%s:%s@(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", "root", "1234", "127.0.0.1", 3306, "mydatabase")

	//赋值给全局变量 Db 没有 := 就是赋值
	db, err := gorm.Open("mysql", mysql_conn)
	if err != nil {
		panic(err)
	}
	fmt.Println("初始化数据库成功......")

	//创建表,把结构体和数据表关联起来
	db.AutoMigrate(&Gorm_test{})

	var test Gorm_test
	db.First(&test, 4) //根据主键(当前为ID),如果为空则返回:{0  }   只能查出符号条件的第一条
	fmt.Println(test)

	var test1 []Gorm_test
	db.Where("name = ?", "zlz").Find(&test1) //查询所有符合的记录并保存在test1数组中  海曙注意一定要加&!!!
	fmt.Println(test1)
}

小记:当查询不出来数据时,可以在结构体后面指定字段名,类似: ID int `gorm:"column:ID"

其他Go的知识

实时加载工具Air

这里我直接引用大佬的文章吧:网址

Go包管理工具Go Mod

具体操作可以看看这个:网址

注意的点:
需要主文件夹下面有main.go
每次创建新项目后,可以命令行输入:
go mod init 你的文件夹名
go mod tidy
之后就能自动帮你下载包啦

云服务器liunx宝塔面板配置Go环境

分享大佬的网址:网址

Go的项目结构与导包

一般我们是利用Go mod来管理包

一个项目一般只有一个go.mod

比如说我的项目叫sisipai,那么首先建一个大文件夹,下面建立两个小文件夹叫article和quku,还有一个main.go作为项目的主要文件
在这里插入图片描述
在article和quku中也分别有一个main.go的文件
里面第一行分别是

//注意要首字母大写
package Article //表明是Article的这个包
//和
package Quku //表明是Quku的这个包

注意:在article和quku中不能由.idea或者go.mod、go.sum这种东西出现(一个项目一般只有一个go.mod)

再切回sisipai这个主文件夹,写

package main
//这时候就可以import了
import (
	"sisipai/Article"
	"sisipai/Qupu"
)

如果出现报错,不要慌,在sisipai文件夹命令行下输入:

go mod init sispai
go mod tidy

就可以自己帮你找包啦!
成功运行




Go项目放在服务端运行

如果服务端是Linux,想直接放文件上去运行的话可以在本地先编译好Linux所可以运行的文件,再直接放到服务器上去跑。
方法:依次在命令行输入:

set GOARCH=amd64
set GOOS=linux
go build .

接着就会生成一个和你项目名称相同的没有后缀名的文件,这个就算linux可以执行的文件。
把它拖到你的服务器上,然后给个运行权限:

chmod +x [你的项目名称]

就可以加上"./"运行啦!

端口问题
很多新手小白(比如说我)在一开始并不知道如何让自己的go文件在服务端跑起来之后访问它,那么今天我们来解决一下这个问题。

首先你端口要是这样的:(如果你要在8080端口下运行的话)

_ = r.Run("0.0.0.0:8080“)

然后别忘了把防火墙给开启了,放行8080端口
接下去就好啦!你可以试试用curl 127.0.0.1:8080端口测试,也可以通过你服务器的公网IP访问试试。

Go语言设置跨域访问

网址
通过使用cors的中间件改变header来实现跨域,一般要给域名加上https

等待完善ing…

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值