コードリポジトリ

giteeこの例のコード
gitee以前に書いたもの

プロジェクトの作成

まずディレクトリを作成し、そのディレクトリで以下のコマンドを実行します。

1
2
3
4
5
go mod init quick-start
# 毎回実行前に実行し、プロジェクトの依存関係を正しくする
go mod tidy
# 実行
go run main.go

ディレクトリ構造

一般的なWebプロジェクトはMVC構造を採用しています。Mはモデル、Vはビュー、Cはコントローラーです。

  • Model(モデル)
    アプリケーションの核心データとビジネスロジックを管理します。
    データベースまたは他のデータソースとのやり取りを行い、データのCRUD操作を処理します。
    データに変更があった場合、Viewに更新を通知します。
  • View(ビュー)
    ユーザーインターフェースの表示を担当し、通常はModelのデータに基づいてレンダリングされます。
    ユーザーはViewを通じてアプリケーションと対話できます。
    ViewはModelの変化を監視し、自動的に表示を更新します。
  • Controller(コントローラー)
    ユーザーの入力(例えばボタンをクリック、フォームの送信など)を受け取り、ModelとViewの間のやり取りを調整します。
    ユーザーのアクションに応じてModelの方法を呼び出したり、Viewの状態を更新したりします。
    コントローラーはModelとViewの間の架け橋となります。

そのため、一般的なディレクトリ構造は以下のようになります。
models/controllers/middleware
modelsにはモデルファイル、controllersにはコントローラーファイル、middlewareにはミドルウェアファイルが格納されています。
一方、viewsは通常フロントエンドプロジェクトとして別途開発されます。

1
2
3
4
5
6
7
8
9
10
11
12
- common/ または utils/
- controllers/
- initializers/
- middleware/
- migrate/
- models/
- scripts/
- .env
- go.mod
- go.sum
- main.go
- readme.md

データベース

私たちはGORMフレームワークを使用してデータベースを操作します。

データベーステーブルの作成

GORMはデータベーステーブル構造を自動生成することをサポートしています。モデル構造体を定義し、GORMのコードを使用して生成するだけです。

まずはデータベースに接続します。ここでは便利なため、SQLiteを使用します。

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
// initializers/loadEnv.go
/*
* @Author Malred
* @Date 2025-06-15 21:24:25
* @Description データベースに接続し、外部に対してデータベースインスタンスを提供する
*/
package initializers

import (
"log"
"os"

"gorm.io/driver/mysql"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)

var DB *gorm.DB

func ConnectToDB() {
//ConnectToMysql()
ConnectToSqlite()
}

func ConnectToMysql() {
var err error
dsn := os.Getenv("MYSQL_DB_URL")
DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal("Failed to connect to database")
}
}

func ConnectToSqlite() {
var err error
dbPath := os.Getenv("SQLITE_MDB_URL") // SQLiteデータベースファイルのパスを環境変数で指定
DB, err = gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
if err != nil {
log.Fatal("Failed to connect to database")
}
}

データベースなどの設定は、.envファイルに保存します。

1
2
3
4
5
6
7
8
# Web実行ポート
PORT=4000
# JWT生成用のランダムに生成されたUUID
SECRET=8d6f7b7d-2f24-4867-ae90-feb4fc646693
# MYSQLデータベース接続URL
MYSQL_DB_URL=root:123456@tcp(127.0.0.1:3306)/quick-starter?charset=utf8mb4&parseTime=True&loc=Local
# SQLITEデータベース接続URL
SQLITE_MDB_URL=./dev.db

Go言語で.envファイルを読み込む方法は以下の通りです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// initializers/loadEnv.go
/*
* @Author Malred
* @Date 2025-06-15 21:24:15
* @Description .envファイル内の環境変数を読み込む
*/
package initializers

import (
"github.com/joho/godotenv"
"log"
)

func LoadEnvVariables() {
// .envを読み込み、ポートなどを設定する
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
}

対応するデータベーステーブルのモデルを宣言します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// models/user.go
/*
* @Author Malred
* @Date 2025-06-15 21:35:54
* @Description
*/
package models

type User struct {
ID uint `gorm:"primarykey"`
Username string
Password string
Email string
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// models/post.go
/*
* @Author Malred
* @Date 2025-06-15 21:36:06
* @Description
*/
package models

import "gorm.io/gorm"

type Post struct {
gorm.Model
Title string
Body string
}

コードでデータベーステーブルを生成します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// migrate/migrate.go
/*
* @Author Malred
* @Date 2025-06-15 21:22:42
* @Description GORMフレームワークのメソッドを使用して、データベーステーブルを作成する
*/
package main

import (
"quick-start/initializers"
"quick-start/models"
)

func init() {
initializers.LoadEnvVariables()
initializers.ConnectToDB()
}

func main() {
// データベースを作成する
initializers.DB.AutoMigrate(&models.User{})
initializers.DB.AutoMigrate(&models.Post{})
}

ユーザー情報テーブル

増減改査

ユーザー情報の基本操作を実現します:追加、削除、変更、および検索。

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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// user_test.go
/*
* @Author Malred
* @Date 2025-06-15 21:43:05
* @Description
*/
package go_quick_starter

import (
"quick-start/initializers"
"quick-start/models"
"testing"
)

func CreateUser(user *models.User) error {
result := initializers.DB.Create(user)
return result.Error
}
func GetUserByID(id uint) (*models.User, error) {
var user models.User
result := initializers.DB.First(&user, id)
return &user, result.Error
}
func GetAllUsers() ([]models.User, error) {
var users []models.User
result := initializers.DB.Find(&users)
return users, result.Error
}
func UpdateUser(user *models.User) error {
result := initializers.DB.Save(user)
return result.Error
}
func DeleteUser(id uint) error {
result := initializers.DB.Delete(&models.User{}, id)
return result.Error
}

// 保存新增的用户的ID
var ID int = 1

func TestCreateUser(t *testing.T) {
initializers.LoadEnvVariables()
initializers.ConnectToDB()

user := &models.User{
Email: "create@example.com",
Password: "password123",
Username: "createuser",
}

err := CreateUser(user)
if err != nil || user.ID == 0 {
t.Errorf("Failed to create user: %v", err)
}
ID = int(user.ID)
}

func TestGetUserByID(t *testing.T) {
initializers.LoadEnvVariables()
initializers.ConnectToDB()

retrievedUser, err := GetUserByID(uint(ID))
if err != nil || retrievedUser.ID == 0 {
t.Errorf("Failed to retrieve user: %v", err)
}
}

func TestGetAllUsers(t *testing.T) {
initializers.LoadEnvVariables()
initializers.ConnectToDB()

// 插入两个用户
user1 := &models.User{Email: "all1@example.com", Password: "pass", Username: "user1"}
user2 := &models.User{Email: "all2@example.com", Password: "pass", Username: "user2"}
initializers.DB.Create(user1)
initializers.DB.Create(user2)

users, err := GetAllUsers()
if err != nil || len(users) < 2 {
t.Errorf("Failed to get all users: %v", err)
}
}

func TestUpdateUser(t *testing.T) {
initializers.LoadEnvVariables()
initializers.ConnectToDB()

// 更新用户
user := &models.User{
ID: uint(ID),
Email: "update@example.com",
Password: "password123",
Username: "oldname",
}

err := UpdateUser(user)
if err != nil {
t.Errorf("Failed to update user: %v", err)
}
}

func TestDeleteUser(t *testing.T) {
initializers.LoadEnvVariables()
initializers.ConnectToDB()

// 删除用户
err := DeleteUser(uint(ID))
if err != nil {
t.Errorf("Failed to delete user: %v", err)
}
}

パスワード暗号化

ユーザー情報の安全性を確保するため、ユーザーのパスワードを暗号化処理する必要があります。

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
import (
"fmt"
"golang.org/x/crypto/bcrypt"
"os"
"quick-start/initializers"
"quick-start/models"
"testing"
"time"
)

func TestBcryptPasswordEncode(t *testing.T) {
user := &models.User{
Email: "create@example.com",
Password: "password123",
Username: "createuser",
}

// パスワードのハッシュ化(暗号化)
hash, err := bcrypt.GenerateFromPassword([]byte(user.Password), 10)
if err != nil {
t.Errorf("Failed to hash password")
}
fmt.Println(hash)
fmt.Println(string(hash))
}

JWTトークンの作成

JWT(JSON Web Token)は、ネットワークアプリケーション間でセキュアに情報を伝達するためのオープンスタンダードです。JWTトークンを作成することで、ユーザー認証を実現します。

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
import (
"fmt"
"github.com/golang-jwt/jwt/v4"
"golang.org/x/crypto/bcrypt"
"os"
"quick-start/initializers"
"quick-start/models"
"testing"
"time"
)

func TestJWT(t *testing.T) {
user := &models.User{
Email: "create@example.com",
Password: "password123",
Username: "createuser",
}

// パスワードのハッシュ化(暗号化)
hash, err := bcrypt.GenerateFromPassword([]byte(user.Password), 10)
if err != nil {
t.Errorf("Failed to hash password")
}
fmt.Println(hash)
fmt.Println(string(hash))

// パスワードの検証
err = bcrypt.CompareHashAndPassword([]byte(hash), []byte(user.Password))
if err != nil {
t.Errorf("Invalid password")
}

// JWTトークンの生成(通信プロトコルはHTTPSを使用し、暗号化して転送することで、JWTの漏洩を防ぐ)
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"sub": user.ID,
// 有効期限の設定
"exp": time.Now().Add(time.Hour * 24 * 30).Unix(),
})

// シークレットキーを渡して、暗号化する
tokenStr, err := token.SignedString([]byte(os.Getenv("SECRET")))
if err != nil {
t.Errorf("Failed to generate token")
}
fmt.Println(tokenStr)
}

テストの実施

投稿情報テーブル

増減改査

投稿情報の基本操作を実現します:追加、削除、変更、および検索。

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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// /post_test.go
package go_quick_starter

import (
"quick-start/initializers"
"quick-start/models"
"testing"
)

func CreatePost(post *models.Post) error {
result := initializers.DB.Create(post)
return result.Error
}
func GetPostByID(id uint) (*models.Post, error) {
var post models.Post
result := initializers.DB.First(&post, id)
return &post, result.Error
}
func GetAllPosts() ([]models.Post, error) {
var posts []models.Post
result := initializers.DB.Find(&posts)
return posts, result.Error
}
func UpdatePost(post *models.Post) error {
result := initializers.DB.Save(post)
return result.Error
}
func DeletePost(id uint) error {
result := initializers.DB.Delete(&models.Post{}, id)
return result.Error
}

func TestCreatePost(t *testing.T) {
initializers.LoadEnvVariables()
initializers.ConnectToDB()

post := &models.Post{
Title: "test",
Body: "test body",
}

err := CreatePost(post)
if err != nil || post.ID == 0 {
t.Errorf("Failed to create user: %v", err)
}
ID = int(post.ID)
}

func TestGetPostByID(t *testing.T) {
initializers.LoadEnvVariables()
initializers.ConnectToDB()

retrievedPost, err := GetPostByID(uint(ID))
if err != nil || retrievedPost.ID == 0 {
t.Errorf("Failed to retrieve user: %v", err)
}
}

func TestGetAllPosts(t *testing.T) {
initializers.LoadEnvVariables()
initializers.ConnectToDB()

// 二つの投稿を挿入する
post1 := &models.Post{
Title: "test",
Body: "test body",
}
post2 := &models.Post{
Title: "test",
Body: "test body",
}
initializers.DB.Create(post1)
initializers.DB.Create(post2)

users, err := GetAllPosts()
if err != nil || len(users) < 2 {
t.Errorf("Failed to get all users: %v", err)
}
}

func TestUpdatePost(t *testing.T) {
initializers.LoadEnvVariables()
initializers.ConnectToDB()

// 投稿を更新する
post := &models.Post{
Title: "test",
Body: "test body",
}

err := UpdatePost(post)
if err != nil {
t.Errorf("Failed to update user: %v", err)
}
}

func TestDeletePost(t *testing.T) {
initializers.LoadEnvVariables()
initializers.ConnectToDB()

// 投稿を削除する
err := DeletePost(uint(ID))
if err != nil {
t.Errorf("Failed to delete user: %v", err)
}
}

ネットワークインターフェース

ルートの定義

RESTfulスタイルのAPI:

HTTP メソッド 操作タイプ
GET リソースの取得 ユーザーリストを取得 /api/users
POST リソースの作成 新規ユーザーを作成 /api/users
PUT リソースの更新 指定ユーザーを更新 /api/users/{id}
DELETE リソースの削除 指定ユーザーを削除 /api/users/{id}
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
// /main.go
/*
* @Author Malred
* @Date 2025-06-16 08:00:23
* @Description
*/
package main

import (
"github.com/gin-gonic/gin"
"quick-start/controllers"
"quick-start/initializers"
"quick-start/middleware"
)

// 起動時に自動的に呼び出される
func init() {
initializers.LoadEnvVariables()
initializers.ConnectToDB()
}

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

r.Use(middleware.Cors())
{
r.GET("posts", controllers.PostsIndex)
r.GET("posts/:id", controllers.PostsShow)
r.GET("posts/page", controllers.PostsPage)
r.POST("posts", middleware.RequireAuthHeader, controllers.PostsCreate)
r.PATCH("posts/:id", middleware.RequireAuthHeader, controllers.PostsUpdate)
r.DELETE("posts/:id", middleware.RequireAuthHeader, controllers.PostsDelete)
}
{
r.GET("auth/profile", middleware.RequireAuthHeader, controllers.Validate)
r.POST("users", controllers.Register)
r.POST("auth/login", controllers.Login)
}
r.Run()
}

ログインと登録

ユーザーのログインと登録機能を実装します。これには、認証とアカウント作成が含まれます。

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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// controllers/user.go
/*
* @Author Malred
* @Date 2025-06-16 08:00:44
* @Description
*/
package controllers

import (
"net/http"
"os"
"quick-start/initializers"
"quick-start/models"
"time"

"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v4"
"golang.org/x/crypto/bcrypt"
)

// 登録
func Register(c *gin.Context) {
var body struct {
Username string `json:"username" validate:"min=5"`
Password string `json:"password" validate:"min=8"`
RetypedPassword string `json:"retypedPassword" validate:"min=8,eqfield=Password"`
Email string `json:"email" validate:"email"`
}

// JSONデータを構造体にバインド
c.Bind(&body)

// 検証
err := validate.Struct(body)
if err != nil {
c.JSON(400, gin.H{
"error": err.Error(),
})
return
}

var user models.User
initializers.DB.
Where("email = ?", body.Email).
First(&user)

// 既に存在する(実はusernameも一意で、テーブル設計上)
if user != (models.User{}) {
c.JSON(500, gin.H{
"error": "そのメールアドレスはすでに存在します!",
})
return
}

// パスワードをハッシュ化
hash, err := bcrypt.GenerateFromPassword([]byte(body.Password), 10)
if err != nil {
c.JSON(500, gin.H{
"error": "パスワードのハッシュ化に失敗しました",
})
return
}

user = models.User{
Username: body.Username,
Password: string(hash),
Email: body.Email,
}

result := initializers.DB.Create(&user)
if result.Error != nil {
c.JSON(500, gin.H{
"error": "ユーザーの作成に失敗しました",
})
return
}

c.JSON(200, gin.H{
"user": user,
})
}

// ログイン
func Login(c *gin.Context) {
var body struct {
Username string `json:"username"`
Password string `json:"password"`
}

if err := c.Bind(&body); err != nil {
c.JSON(400, gin.H{
"error": "ボディのバインドに失敗しました!",
})
return
}

var user models.User
initializers.DB.Where("username = ?", body.Username).Find(&user)

if user == (models.User{}) {
c.JSON(500, gin.H{
"error": "ユーザーが見つかりません!",
})
return
}

// パスワードの検証
err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(body.Password))
if err != nil {
c.JSON(400, gin.H{
"error": "パスワードの検証に失敗しました",
})
return
}

// JWTトークンの生成(通信プロトコルとしてHTTPSを使用し、暗号化して送信することで、JWTの漏洩を防ぐ)
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"sub": user.ID,
// 有効期限
"exp": time.Now().Add(time.Hour * 24 * 30).Unix(),
})
// シークレットキーを渡して、暗号化する
tokenStr, err := token.SignedString([]byte(os.Getenv("SECRET")))
if err != nil {
c.JSON(400, gin.H{
"error": "トークンの作成に失敗しました",
})
return
}
// クッキーを設定する
c.SetSameSite(http.SameSiteLaxMode)
c.SetCookie("Authorization", tokenStr, 3600*24*30, "", "", false, true)
c.JSON(200, gin.H{
"token": tokenStr,
"userId": user.ID,
})
}

// 検証、ユーザー情報を返す
func Validate(c *gin.Context) {
id, _ := c.Get("id")
uname, _ := c.Get("username")
email, _ := c.Get("email")
c.JSON(200, gin.H{
"id": id,
"username": uname,
"email": email,
})
}

投稿情報インターフェース

投稿情報の取得、投稿、更新、削除のためのAPIインターフェースを提供します。

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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// controllers/post.go
/*
* @Author Malred
* @Date 2025-06-16 08:00:47
* @Description
*/
package controllers

import (
"quick-start/initializers"
"quick-start/models"
"strconv"

"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
)

var validate *validator.Validate

func init() {
validate = validator.New()
}

// 追加
func PostsCreate(c *gin.Context) {
// JSONデータを取得する
var body struct {
Body string `json:"body" validate:"min=6,max=255"`
Title string `json:"title" validate:"min=3,max=20"`
}
// JSONデータを構造体にバインドする
c.Bind(&body)

// 検証する
err := validate.Struct(body)
if err != nil {
c.JSON(400, gin.H{
"error": err.Error(),
})
return
}

post := models.Post{
Title: body.Title,
Body: body.Body,
}

// 作成する
result := initializers.DB.Create(&post)
if result.Error != nil {
c.Status(400)
return
}

c.JSON(200, gin.H{
"post": post,
})
}

// 全てを取得する
func PostsIndex(c *gin.Context) {
var posts []models.Post
initializers.DB.Find(&posts)

c.JSON(200, gin.H{
"posts": posts,
})
}

// ページングして取得する
func PostsPage(c *gin.Context) {
limitStr := c.Query("limit")
curPageStr := c.Query("currentPage")

limit, err := strconv.Atoi(limitStr)
if err != nil {
c.Status(500)
return
}
curPage, err := strconv.Atoi(curPageStr)
if err != nil {
c.Status(500)
return
}

var posts []models.Post
initializers.DB.Scopes(models.Paginate(curPage, limit)).Find(&posts)

c.JSON(200, gin.H{
"posts": posts,
})
}

// IDに基づいて取得する
func PostsShow(c *gin.Context) {
id := c.Param("id")

var post models.Post
initializers.DB.First(&post, id)

c.JSON(200, gin.H{
"post": post,
})
}

// 更新する
func PostsUpdate(c *gin.Context) {
id := c.Param("id")

var body struct {
Body string `json:"body" validate:"min=6,max=255"`
Title string `json:"title" validate:"min=3,max=20"`
}
// JSONデータを構造体にバインドする
c.Bind(&body)

// 検証する
err := validate.Struct(body)
if err != nil {
c.JSON(400, gin.H{
"error": err.Error(),
})
return
}

var post models.Post
initializers.DB.First(&post, id)

initializers.DB.Model(&post).Updates(models.Post{
Title: body.Title,
Body: body.Body,
})

c.JSON(200, gin.H{
"post": post,
})
}

// 削除する
func PostsDelete(c *gin.Context) {
id := c.Param("id")

initializers.DB.Delete(&models.Post{}, id)

c.Status(200)
}

認証とクロスドメインミドルウェア

認証ミドルウェアは、ユーザーの身分を確認するために使用され、特定のリソースにアクセスできるのは承認されたユーザーだけであることを保証します。
クロスドメインミドルウェアは、クロスドメインリクエストの問題を解決するために使用され、異なるドメインからのリクエストがサーバーリソースにアクセスできるようにします。

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
// middleware/auth.go
/*
* @Author Malred
* @Date 2025-06-16 08:01:36
* @Description
*/
package middleware

import (
"fmt"
"net/http"
"os"
"quick-start/initializers"
"quick-start/models"
"time"

"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v4"
)

// クロスドメイン問題を解決する
func RequireAuthHeader(c *gin.Context) {
// トークンを取得する
tokenStr := c.Request.Header.Get("Authorization")
fmt.Println(tokenStr[:7])
if tokenStr[:6] != "Bearer" {
c.AbortWithStatus(http.StatusUnauthorized)
}
// トークンを検証する
token, err := jwt.Parse(tokenStr[7:], func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
return []byte(os.Getenv("SECRET")), nil
})
if err != nil {
fmt.Println(token)
c.AbortWithStatus(http.StatusUnauthorized)
}
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
// 期限切れの場合
if float64(time.Now().Unix()) > claims["exp"].(float64) {
c.AbortWithStatus(http.StatusUnauthorized)
}
// JWTに含まれるメッセージ(userId)に基づいてデータベースを照会する
var user models.User
initializers.DB.First(&user, claims["sub"])
if user.ID == 0 {
c.AbortWithStatus(http.StatusUnauthorized)
}
// c.Set("user", user)
c.Set("id", user.ID)
c.Set("username", user.Username)
c.Set("email", user.Email)
c.Next()
} else {
c.AbortWithStatus(http.StatusUnauthorized)
}
}
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
// middleware/cors.go
/*
* @Author Malred
* @Date 2025-06-16 08:01:40
* @Description
*/
package middleware

import (
"net/http"

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

// クロスドメイン問題を解決する
func Cors() gin.HandlerFunc {
return func(c *gin.Context) {
method := c.Request.Method
origin := c.Request.Header.Get("Origin")
c.Header("Access-Control-Allow-Origin", origin)
// c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Headers", "*")
c.Header("Access-Control-Allow-Methods", "*")
c.Header("Access-Control-Expose-Headers", "Content-Length,Access-Control-Allow-Origin,Access-Control-Allow-Headers,Content-Type")
c.Header("Access-Control-Max-Age", "3600")
c.Header("Access-Control-Allow-Credentials", "true")
// OPTIONSメソッドを許可する
if method == "OPTIONS" {
c.AbortWithStatus(http.StatusNoContent)
}
// リクエストを処理する
c.Next()
}
}

APIfoxを使用したテスト

APIfoxはインターフェーステストツールです。
apifox
このケースのインターフェースはすでに共有しています:
インターフェースドキュメント

現在比較的成熟している二次開発フレームワーク

今のところよくわかっていませんが、他の人の回答を見てみてください。
リンク

コミュニティ

これらのプラットフォームで私に連絡することができます:


本站总访问量