Gin框架
Gin 是一个用 Go (Golang) 编写的 web 框架。 它是一个类似于 martini 但拥有更好性能的 API 框架, 由于 httprouter ,速度提高了近 40 倍。 如果你是性能和高效的追求者, 你会爱上 Gin.
一、安装
1.1、安装
要安装 Gin 软件包,需要先安装 Go 并设置 Go 工作区。
1.下载并安装 gin:
1
$ go get -u github.com/gin-gonic/gin
2.将 gin 引入到代码中:
1
import "github.com/gin-gonic/gin"
3.(可选)如果使用诸如 http.StatusOK
之类的常量,则需要引入 net/http
包:
1.2、基本案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main
import "github.com/gin-gonic/gin"
func main () {
r := gin.Default()
r.GET("/ping" , func (c *gin.Context) {
c.JSON(200 , gin.H{
"message" : "pong" ,
})
})
r.LoadHTMLGlob("templates/*" )
r.GET("/index" , func (c *gin.Context) {
c.HTML(http.StatusOK, "index.html" ,nil )
})
r.Run()
}
二、Gin路由
2.1、路由方法
路由系统支持任意方式的请求,如下的方法用来提供对应方法来接收请求:
1
2
3
4
5
6
7
8
9
10
func (group *RouterGroup) DELETE(relativePath string , handlers ...HandlerFunc) IRoutes
func (group *RouterGroup) GET(relativePath string , handlers ...HandlerFunc) IRoutes
func (group *RouterGroup) HEAD(relativePath string , handlers ...HandlerFunc) IRoutes
func (group *RouterGroup) OPTIONS(relativePath string , handlers ...HandlerFunc) IRoutes
func (group *RouterGroup) PATCH(relativePath string , handlers ...HandlerFunc) IRoutes
func (group *RouterGroup) POST(relativePath string , handlers ...HandlerFunc) IRoutes
func (group *RouterGroup) PUT(relativePath string , handlers ...HandlerFunc) IRoutes
func (group *RouterGroup) ANY(relativePath string , handlers ...HandlerFunc) IRoutes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main () {
r := gin.Default()
r.LoadHTMLGlob("templates/*" )
r.GET("/get" , func (c *gin.Context) {
c.JSON(200 , gin.H{
"message" : "GET请求" ,
})
})
r.POST("/post" ,func (c *gin.Context) {
c.JSON(200 , gin.H{
"message" : "POST请求" ,
})
})
r.NoRoute(func (c *gin.Context) {
c.HTML(http.StatusNotFound, "404.html" , nil )
})
r.Run()
}
2.2、路由分组
路由分组用于将多个路由进行统一的处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package main
import (
"github.com/gin-gonic/gin"
)
func app01_foo (c *gin.Context) {
c.JSON(200 , gin.H{
"message" : "app01_foo" ,
})
}
func app01_bar (c *gin.Context) {
c.JSON(200 , gin.H{
"message" : "app01_bar" ,
})
}
func app02_foo (c *gin.Context) {
c.JSON(200 , gin.H{
"message" : "app02_foo" ,
})
}
func app02_bar (c *gin.Context) {
c.JSON(200 , gin.H{
"message" : "app02_bar" ,
})
}
func main () {
r := gin.Default()
r.LoadHTMLGlob("templates/*" )
a1 := r.Group("/app01" )
{
a1.GET("/foo" , app01_foo)
a1.GET("/bar" , app01_bar)
}
a2 := r.Group("/app02" )
{
a2.GET("/foo" , app02_foo)
a2.GET("/bar" , app02_bar)
}
r.Run()
}
路由组也支持嵌套使用:
1
2
3
4
5
6
7
8
9
a3 := r.Group("/app03" )
{
a3.Group("/shop" )
{
a3.GET("/cart" , func (context *gin.Context) {
context.String(200 ,"cart..." )
})
}
}
三、Gin视图函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
func index (c *gin.Context) {
c.HTML(http.StatusOK, "index.html" , gin.H{"name" : "Yuan" })
}
func login (c *gin.Context) {
user := c.Query("user" )
pwd := c.Query("pwd" )
fmt.Println("GET user" ,user)
fmt.Println("GET pwd" ,pwd)
fmt.Println(c.Request.Method)
c.HTML(http.StatusOK, "login.html" , nil )
}
func auth (c *gin.Context) {
user := c.PostForm("user" )
pwd := c.PostForm("pwd" )
fmt.Println("POST user" ,user)
fmt.Println("POST pwd" ,pwd)
if user == "yuan" && pwd == "123" {
c.String(200 ,"验证成功!" )
}else {
c.String(200 ,"验证失败!" )
}
}
func not_found (c *gin.Context) {
c.HTML(http.StatusNotFound, "notFound.html" , nil )
}
func article_year_month (c *gin.Context) {
fmt.Println(c.Param("year" ))
fmt.Println(c.Param("month" ))
fmt.Println(c.FullPath())
}
func article_delete (c *gin.Context) {
delete_id:=c.Param("delete_id" )
fmt.Println("删除书籍" ,delete_id)
c.Redirect(http.StatusMovedPermanently,"/index" )
}
var r = gin.Default()
func main () {
r.LoadHTMLGlob("./templates/*" )
r.GET("/index" , index)
r.GET("/login" , login)
r.POST("/auth" , auth)
blog := r.Group("/blog" )
{
blog.GET("/articles/:year/:month" , article_year_month)
blog.GET("/articles/delete/:delete_id" , article_delete)
}
r.NoRoute(not_found)
r.Run(":9090" )
}
四、Gin模板
4.1、Go模板的变量渲染
视图部分:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package main
import (
"fmt"
"net/http"
"html/template"
)
type Student struct {
Name string
Gender string
Age int
}
func index (w http.ResponseWriter, r *http.Request) {
tmpl, err := template.ParseFiles("templates/index.tmpl" )
if err != nil {
fmt.Println("create template failed, err:" , err)
return
}
s1:=Student{"yuan" ,"male" ,23 }
obj2:=map [string ]interface {}{
"s1" :s1,
"books" :[]string {"三国演义" ,"西游记" ,"红楼梦" ,"UI设计" },
"articles" :[]string {},
}
tmpl.Execute(w,obj2)
}
func main () {
http.HandleFunc("/" , index)
err := http.ListenAndServe(":9995" , nil )
if err != nil {
fmt.Println("HTTP server failed,err:" , err)
return
}
}
模板部分:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<!DOCTYPE html >
<html lang ="en" >
<head >
<meta charset ="UTF-8" >
<title > Title</title >
</head >
<body >
<h3 > 一 变量渲染</h3 >
<p > hi,{{.}}</p >
<p > 姓名: {{.Name}}</p >
<p > 年龄: {{.Age}}</p >
<p > 性别: {{.Gender}}</p >
<p > {{.s1}}</p >
<p > {{.s1.Name}}</p >
<p > {{.books}}</p >
<p > {{index .books 2}}</p >
<h3 > 二 条件判断</h3 >
{{if .articles }}
{{.articles}}
{{else}}
<p > 没有任何文章</p >
{{end}}
<h3 > 三 循环变量</h3 >
{{range $index,$value := .books}}
<p > {{$index}} : {{$value}} </p >
{{end}}
<h3 > with语句</h3 >
{{with .s1}}
<p > {{.Name}}</p >
<p > {{.Age}}</p >
{{end}}
<h3 > 注释</h3 >
{{/*这是一个注释语法*/}}
</body >
</html >
4.2、Go模板函数
语法格式:
functionName [Argument...]
Argument参数是可选的,如果有多个参数,参数直接用空格分隔。
函数名 函数调用格式 对应关系运算 说明
eq
eq arg1 arg2
arg1 == arg2
arg1等于arg2则返回true
ne
ne arg1 arg2
arg1 != arg2
arg1不等于arg2则返回true
lt
lt arg1 arg2
arg1 < arg2
arg1小于arg2则返回true
le
le arg1 arg2
arg1 <= arg2
arg1小于等于arg2则返回true
gt
gt arg1 arg2
arg1 > arg2
arg1大于arg2则返回true
ge
ge arg1 arg2
arg1 >= arg2
arg1大于等于arg2则返回true
and
and 表达式1 表达式2
表达式1 && 表达式2
表达式1和表达式2都为真的时候返回true
or
or 表达式1 表达式2
表达式1 || 表达式2
表达式1和表达式2其中一个为真的时候返回true
not
not 表达式
!表达式
表达式为false则返回true, 反之返回false
index
index arg 索引/键
index x 2 即x[2]
每个被索引的主体必须是数组、切片或者字典
len
len arg
len x 即x的元素个数
用于计算数组大小
urlquery
urlquery arg
urlquery url
用于url编码
4.3、Go模板的嵌套与继承
在实际项目中,我们不可能只有一个模板,一般来说都有很多个模板,而且这些模板也会共享一些公共的模板,这些公共的模板我们都可以定义成子模板,在需要的时候调用子模板,就可以将子模板的内容嵌入当前模板中。
提示:在项目中使用子模板,可以让项目模板具有模块化的能力,提高模块复用能力和可维护性。
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package main
import (
"fmt"
"net/http"
"html/template"
)
func index (w http.ResponseWriter, r *http.Request) {
fmt.Println("index..." )
tmpl, err := template.ParseFiles("./templates/layout.html" ,"./templates/index.html" ,"./templates/ads.html" )
if err != nil {
fmt.Println("create template failed, err:" , err)
return
}
err = tmpl.ExecuteTemplate(w,"index.html" ,nil )
if err != nil {
fmt.Println("render template failed, err:" , err)
return
}
}
func article_detail (w http.ResponseWriter, r *http.Request) {
fmt.Println("article_detail..." )
tmpl, err := template.ParseFiles("./templates/layout.html" ,"./templates/article_detail.html" ,"./templates/ads.html" )
if err != nil {
fmt.Println("create template failed, err:" , err)
return
}
err = tmpl.ExecuteTemplate(w,"article_detail.html" ,nil )
if err != nil {
fmt.Println("render template failed, err:" , err)
return
}
}
func archives (w http.ResponseWriter, r *http.Request) {
fmt.Println("achives..." )
tmpl, err := template.ParseFiles("./templates/layout.html" ,"./templates/archives.html" ,"./templates/ads.html" )
if err != nil {
fmt.Println("create template failed, err:" , err)
return
}
err = tmpl.ExecuteTemplate(w,"archives.html" ,nil )
if err != nil {
fmt.Println("render template failed, err:" , err)
return
}
}
func main () {
http.HandleFunc("/index" , index)
http.HandleFunc("/article_detail" , article_detail)
http.HandleFunc("/archives" , archives)
err := http.ListenAndServe(":6677" , nil )
if err != nil {
fmt.Println("HTTP server failed,err:" , err)
return
}
}
templates/layout.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8" >
<title>Title</title>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous" >
<style>
*{
margin: 0 ;
padding: 0 ;
}
.header{
width: 100 %;
background-color: #369 ;
color: white;
line-height: 48 px;
}
.content{
margin-top: 10 px;
}
</style>
</head>
<body>
<div class="header" >yuan老师的个人博客</div>
<div class="content container-fluid" >
<div class="row" >
<div class="col-md-2" >
<div class="panel panel-info" >
<div class="panel-heading" >文章分类</div>
<div class="panel-body" >
Panel content
</div>
</div>
<div class="panel panel-danger" >
<div class="panel-heading" >文章标签</div>
<div class="panel-body" >
Panel content
</div>
</div>
<div class="panel panel-success" >
<div class="panel-heading" >其它</div>
<div class="panel-body" >
Panel content
</div>
</div>
</div>
<div class="col-md-8" >
<div class="content" >
{{block "content" .}}
{{end}}
</div>
</div>
{{}}
<div class="col-md-2" >
{{template "ads.html" }}
</div>
</div>
</div>
</body>
</html>
templates/index.html,templates/article_detail.html,templates/archives.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{{template "layout.html" .}}
{{define "content" }}
Hello index !!!
{{end}}
{{template "layout.html" .}}
{{define "content" }}
Hello article_detail !!!
{{end}}
{{template "layout.html" .}}
{{define "content" }}
Hello archievs!!!
{{end}}
五、Gorm
5.1、gorm介绍
5.2、连接数据库
中文文档: https://www.kancloud.cn/sliver_horn/gorm/1861152
连接数据库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main
import (
"fmt"
"gorm.io/gorm"
"gorm.io/driver/mysql"
"time"
)
func main () {
dsn := "用户名:密码@tcp(127.0.0.1:3306)/数据库名?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println("err:" ,err)
}
}
5.3、声明模型
1
2
3
4
5
6
7
8
9
10
11
12
13
type Book struct {
ID uint `gorm:"primaryKey"`
Title string `gorm:"type:varchar(100);unique;not null"`
Price int64
Is_publish byte `gorm:"default:1"`
Publish_date *time.Time
CreateTime *time.Time `gorm:"autoCreateTime"`
UpdateTime *time.Time `gorm:"autoCreateTime"`
}
db.AutoMigrate(&Book{})
5.4、gorm单表操作
5.4.1、添加记录
1
2
3
4
5
6
7
8
9
10
b1 := Book{Title: "UI设计" ,Price:0 }
db.Create(&b1)
fmt.Println("添加书籍的ID:" ,b1.ID)
books := []Book{{Title: "西游记" ,Price:100 },{Title: "红楼梦" ,Price:200 },{Title: "三国演义" ,Price:300 }}
db.Create(&books)
5.4.2、查询记录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
books := []Book{}
result := db.Find(&books)
fmt.Println(result.RowsAffected )
fmt.Println(books)
for _, book := range books {
fmt.Println(book.Title,book.Price)
}
book:=Book{}
db.First(&book)
fmt.Println(book1)
book:=Book{}
db.Last(&book)
fmt.Println(book)
db.Where("title = ?" ,"红楼梦" ).First(&book)
db.Where("price <> ?" ,200 ).Find(&books)
db.Where("price between ? and ?" ,100 ,200 ).Find(&books)
db.Where("price in ?" ,[]int64 {0 ,100 ,200 }).Find(&books)
db.Where("title like ?" ,"金%" ).Find(&books)
db.Where("create_time > ?" ,"2021-01-01 00:00:00" ).Find(&books)
db.Where("publish_name = ? AND price > ?" ,"苹果出版社" ,300 ).Find(&books)
db.Where(&Book{PublishName: "苹果出版社" ,Price:200 }).Find(&books)
db.Where(&Book{PublishName: "苹果出版社" ,Price:0 }).Find(&books)
db.Where(map [string ]interface {}{"publish_name" : "苹果出版社" ,"price" :0 }).Find(&books)
db.Select("title" , "price" ).Find(&books)
db.Omit("title" , "price" ).Find(&books)
db.Not("price between ? and ?" ,100 ,200 ).Find(&books)
db.Where("price = ?" ,200 ).Or("publish_name = ?" ,"苹果出版社" ).Find(&books)
db.Order("price desc, id" ).Find(&books)
db.Limit(2 ).Find(&books)
db.Limit(2 ).Offset(1 ).Find(&books)
5.4.3、删除记录
1
2
3
4
5
6
7
book:=Book{ID: 2 }
db.Delete(&book)
db.Where("price between ? and ?" ,200 ,300 ).Delete(Book{})
db.Where("1 = 1" ).Delete(&Book{})
5.4.4、更新记录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
book := Book{}
db.First(&book)
book.Title = "新西游记"
db.Save(&book)
book := Book{ID: 20 }
db.Model(&book).Update("title" ,"UI设计" )
book := Book{}
db.Model(&book).Where("id = ?" ,20 ).Update("title" ,"UI设计2" )
book := Book{}
db.Model(&book).Where("id = ?" ,20 ).Updates(Book{Title:"UI设计3" ,Price: 333 })
book := Book{}
db.Model(&book).Where("id = ?" ,20 ).Updates(map [string ]interface {}{"title" :"UI设计4" ,"publish_name" :"西瓜出版社" })
5.5、gorm创建关联表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"log"
"os"
)
func main () {
newLogger := logger.New(
log.New(os.Stdout, "\r\n" , log.LstdFlags),
logger.Config{
LogLevel: logger.Info,
},
)
dsn := "root:@tcp(127.0.0.1:3306)/gobook?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: newLogger,
})
if err != nil {
fmt.Println("err:" , err)
}
type Company struct {
ID int
Name string
}
type CreditCard struct {
gorm.Model
Number string
UserID uint
}
type Language struct {
gorm.Model
Name string
}
type User struct {
gorm.Model
Name string
CompanyID int
Company Company
CreditCard []CreditCard
Languages []Language `gorm:"many2many:user_languages;"`
}
db.AutoMigrate(User{})
db.AutoMigrate(Company{})
db.AutoMigrate(CreditCard{})
}
5.6、gorm创建关联记录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
companys:=[]Company{Company{Name: "tencent" ,ID: 1 },Company{Name: "alibaba" ,ID: 2 }}
db.Create(&companys)
user:=User{Name: "yuan" ,CompanyID: 1 }
db.Create(&user)
user=User{
Name: "alvin" ,
Company: Company{
ID: 3 ,
Name: "bytedance" ,
},
}
db.Create(&user)
user:=User{
Name: "zhangsan" ,
CompanyID:3 ,
CreditCard:[]CreditCard{
CreditCard{
Number: "1001" ,
UserID: 3 ,
},
CreditCard{
Number: "1002" ,
UserID: 3 ,
},
},
}
db.Create(&user)
user:=User{
Name: "lisi" ,
CompanyID:2 ,
CreditCard:[]CreditCard{
CreditCard{
Number: "1003" ,
UserID: 3 ,
},
},
Languages:[]Language{
Language{
Name: "english" ,
},
Language{
Name: "chinese" ,
},
},
}
db.Create(&user)
languages:= []Language{}
db.Where("name in ?" ,[]string {"english" ,"chinese" }).Find(&languages)
user:=User{
Name: "wangwu" ,
CompanyID:2 ,
Languages:languages,
}
db.Create(&user)
5.7、gorm关联查询
5.7.1、Preload(子查询 )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
users:=[]User{}
db.Preload("Company" ).Find(&users)
fmt.Println(users)
user := User{Name: "lisi" }
db.Where(&user).Preload("Company" ).Find(&user)
fmt.Println(user)
user := User{Name: "lisi" }
db.Where(&user).Preload("Company" ).Preload("Languages" ).Find(&user)
fmt.Println(user)
user := User{}
db.Where("name =?" ,"zhangsan" ).Preload("CreditCard" ).Find(&user)
fmt.Println(user.CreditCard)
5.7.2、Joins
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
users:=[]User{}
db.Joins("Company" ).Find(&users)
fmt.Println(users)
user:=User{ID: 5 }
db.Joins("Company" ).Find(&user)
fmt.Println(user)
fmt.Println(user.Company.Name)
5.7.3、Association增删改查
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
user:= User{ID: 6 }
languages:=[]Language{}
db.Model(&user).Association("Languages" ).Find(&languages)
fmt.Println(languages)
languages:=[]Language{}
db.Model(&User{}).Association("Languages" ).Find(&languages)
fmt.Println(languages)
user:=User{ID: 5 }
l := Language{}
db.Where(&Language{ID: 1 }).Find(&l)
db.Model(&user).Association("Languages" ).Append([]Language{l,Language{Name: "riyu" }})
user:=User{ID: 5 }
db.Model(&user).Association("Languages" ).Delete(Language{ID: 1 })
db.Model(&user).Association("Languages" ).Clear()
user:=User{ID: 5 }
db.Model(&user).Association("Languages" ).Replace([]Language{Language{ID: 1 },Language{ID: 7 }})
db.Model(&user).Association("Languages" ).Replace(&Language{ID: 2 },)
user:=User{ID: 5 }
c:=db.Model(&user).Association("Languages" ).Count()
fmt.Println(c)
六、中间件
七、cookie与session
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构