-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.go
More file actions
452 lines (375 loc) · 10.9 KB
/
main.go
File metadata and controls
452 lines (375 loc) · 10.9 KB
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
package main
import (
"database/sql"
"fmt"
"log"
"net/http"
"net/url"
"strconv"
"strings"
"text/template"
"time"
"unicode/utf8"
"github.com/go-sql-driver/mysql"
"github.com/gorilla/mux"
)
var router = mux.NewRouter()
var db *sql.DB
func lpc() {
//20230519 这里是lpc提交的数据
//20230522 麻将,输了15,晚上顶雨去吃牛肉,回家窜了
//20230606 昨晚吃了炸串炸鸡夜宵,今天窜了
//20230614 没人帮我提交,这仇我先记下了
//20230618 最近装修,好累
//20230629 总算收拾完了,要猛猛挂咸鱼,落后了都
//20230717 不知道干啥,好无聊
}
func horJin() {
//这里是HorJin提交的数据
}
func yinHuiLin() {
//2023-07-27
//今日办公室里顿悟,方知我是我。
}
func sbLiuBoTao() {
//这里是谁提交的数据
}
func ZhangJiaHong() {
//这里是来自艾欧尼亚的ZX程序员提交的数据
now := time.Now().Unix()
fmt.Println("230522提交", now)
}
func DBC529() {
// 这里是来自DBC的ZX程序员
// 今天也很无聊
// gitlab 真不是个好东西 烦死
// 今天看到老李的骑兵连没了
fmt.Println("230522")
fmt.Println("240514")
}
func shuaiGeZh() {
// 5.22老板不在摸鱼一天
// 今天又帅了
// 今天更帅了
// 我张昊就是个大傻逼
}
func nobody() {
//这是一个来自神秘人的神秘代码
//s神秘代码被启用
//哈哈哈龙哥日记太有节目了
//new life
//最后一天
}
func initDB() {
var err error
config := mysql.Config{
User: "goblog",
Passwd: "123456",
Addr: "127.0.0.1:3306",
Net: "tcp",
DBName: "goblog",
AllowNativePasswords: true,
}
// 准备数据库连接池
db, err = sql.Open("mysql", config.FormatDSN())
checkError(err)
// 设置最大连接数
db.SetMaxOpenConns(100)
// 设置最大空闲连接数
db.SetMaxIdleConns(25)
// 设置每个链接的过期时间
db.SetConnMaxLifetime(5 * time.Minute)
// 尝试连接,失败会报错
err = db.Ping()
checkError(err)
}
func checkError(err error) {
if err != nil {
log.Fatal(err)
}
}
func homeHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "<h1>Hello, 欢迎来到 goblog!</h1>")
}
func aboutHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "此博客是用以记录编程笔记,如您有反馈或建议,请联系 "+
"<a href=\"mailto:1102389095@qq.com\">1102389095@qq.com</a>")
}
func notFoundHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
fmt.Fprint(w, "<h1>请求页面未找到 :(</h1><p>如有疑惑,请联系我们修复。</p>")
}
// Article 对应一条文章数据
type Article struct {
Title, Body string
ID int64
}
func articlesShowHandler(w http.ResponseWriter, r *http.Request) {
// 1. 获取 URL 参数
id := getRouteVariable("id", r)
// 2. 读取对应的文章数据
article, err := getArticleByID(id)
// 3. 如果出现错误
if err != nil {
if err == sql.ErrNoRows {
// 3.1 数据未找到
w.WriteHeader(http.StatusNotFound)
fmt.Fprint(w, "404 文章未找到")
} else {
// 3.2 数据库错误
checkError(err)
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprint(w, "500 服务器内部错误")
}
} else {
// 4. 读取成功,显示文章
tmpl, err := template.ParseFiles("resources/views/articles/show.gohtml")
checkError(err)
err = tmpl.Execute(w, article)
checkError(err)
}
}
func articlesEditHandler(w http.ResponseWriter, r *http.Request) {
// 1. 获取 URL 参数
id := getRouteVariable("id", r)
// 2. 读取对应的文章数据
article, err := getArticleByID(id)
// 3. 如果出现错误
if err != nil {
if err == sql.ErrNoRows {
// 3.1 数据未找到
w.WriteHeader(http.StatusNotFound)
fmt.Fprint(w, "404 文章未找到")
} else {
// 3.2 数据库错误
checkError(err)
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprint(w, "500 服务器内部错误")
}
} else {
// 4. 读取成功,显示表单
updateURL, _ := router.Get("articles.update").URL("id", id)
data := ArticlesFormData{
Title: article.Title,
Body: article.Body,
URL: updateURL,
Errors: nil,
}
tmpl, err := template.ParseFiles("resources/views/articles/edit.gohtml")
checkError(err)
err = tmpl.Execute(w, data)
checkError(err)
}
}
func articlesUpdateHandler(w http.ResponseWriter, r *http.Request) {
// 1. 获取 URL 参数
id := getRouteVariable("id", r)
// 2. 读取对应的文章数据
_, err := getArticleByID(id)
// 3. 如果出现错误
if err != nil {
if err == sql.ErrNoRows {
// 3.1 数据未找到
w.WriteHeader(http.StatusNotFound)
fmt.Fprint(w, "404 文章未找到")
} else {
// 3.2 数据库错误
checkError(err)
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprint(w, "500 服务器内部错误")
}
} else {
// 4. 未出现错误
// 4.1 表单验证
title := r.PostFormValue("title")
body := r.PostFormValue("body")
errors := validateArticleFormData(title, body)
if len(errors) == 0 {
// 4.2 表单验证通过,更新数据
query := "UPDATE articles SET title = ?, body = ? WHERE id = ?"
rs, err := db.Exec(query, title, body, id)
if err != nil {
checkError(err)
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprint(w, "500 服务器内部错误")
}
// √ 更新成功,跳转到文章详情页
if n, _ := rs.RowsAffected(); n > 0 {
showURL, _ := router.Get("articles.show").URL("id", id)
http.Redirect(w, r, showURL.String(), http.StatusFound)
} else {
fmt.Fprint(w, "您没有做任何更改!")
}
} else {
// 4.3 表单验证不通过,显示理由
updateURL, _ := router.Get("articles.update").URL("id", id)
data := ArticlesFormData{
Title: title,
Body: body,
URL: updateURL,
Errors: errors,
}
tmpl, err := template.ParseFiles("resources/views/articles/edit.gohtml")
checkError(err)
err = tmpl.Execute(w, data)
checkError(err)
}
}
}
func articlesIndexHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "访问文章列表")
}
func articlesCreateHandler(w http.ResponseWriter, r *http.Request) {
storeURL, _ := router.Get("articles.store").URL()
data := ArticlesFormData{
Title: "",
Body: "",
URL: storeURL,
Errors: nil,
}
tmpl, err := template.ParseFiles("resources/views/articles/create.gohtml")
if err != nil {
panic(err)
}
err = tmpl.Execute(w, data)
if err != nil {
panic(err)
}
}
// ArticlesFormData 创建博文表单数据
type ArticlesFormData struct {
Title, Body string
URL *url.URL
Errors map[string]string
}
// 表单验证
func validateArticleFormData(title string, body string) map[string]string {
errors := make(map[string]string)
// 验证标题
if title == "" {
errors["title"] = "标题不能为空"
} else if utf8.RuneCountInString(title) < 3 || utf8.RuneCountInString(title) > 40 {
errors["title"] = "标题长度需介于 3-40"
}
// 验证内容
if body == "" {
errors["body"] = "内容不能为空"
} else if utf8.RuneCountInString(body) < 10 {
errors["body"] = "内容长度需大于或等于 10 个字节"
}
return errors
}
func articlesStoreHandler(w http.ResponseWriter, r *http.Request) {
title := r.PostFormValue("title")
body := r.PostFormValue("body")
errors := validateArticleFormData(title, body)
// 检查是否有错误
// 检查是否有错误
if len(errors) == 0 {
lastInsertID, err := saveArticleToDB(title, body)
if lastInsertID > 0 {
fmt.Fprint(w, "插入成功,ID 为"+strconv.FormatInt(lastInsertID, 10))
} else {
checkError(err)
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprint(w, "500 服务器内部错误")
}
} else {
storeURL, _ := router.Get("articles.store").URL()
data := ArticlesFormData{
Title: title,
Body: body,
URL: storeURL,
Errors: errors,
}
tmpl, err := template.ParseFiles("resources/views/articles/create.gohtml")
if err != nil {
panic(err)
}
err = tmpl.Execute(w, data)
if err != nil {
panic(err)
}
}
}
func saveArticleToDB(title string, body string) (int64, error) {
// 变量初始化
var (
id int64
err error
rs sql.Result
stmt *sql.Stmt
)
// 1. 获取一个 prepare 声明语句
stmt, err = db.Prepare("INSERT INTO articles (title, body) VALUES(?,?)")
// 例行的错误检测
if err != nil {
return 0, err
}
// 2. 在此函数运行结束后关闭此语句,防止占用 SQL 连接
defer stmt.Close()
// 3. 执行请求,传参进入绑定的内容
rs, err = stmt.Exec(title, body)
if err != nil {
return 0, err
}
// 4. 插入成功的话,会返回自增 ID
if id, err = rs.LastInsertId(); id > 0 {
return id, nil
}
return 0, err
}
func forceHTMLMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 1. 设置标头
w.Header().Set("Content-Type", "text/html; charset=utf-8")
// 2. 继续处理请求
next.ServeHTTP(w, r)
})
}
func removeTrailingSlash(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 1. 除首页以外,移除所有请求路径后面的斜杆
if r.URL.Path != "/" {
r.URL.Path = strings.TrimSuffix(r.URL.Path, "/")
}
// 2. 将请求传递下去
next.ServeHTTP(w, r)
})
}
func createTables() {
createArticlesSQL := `CREATE TABLE IF NOT EXISTS articles(
id bigint(20) PRIMARY KEY AUTO_INCREMENT NOT NULL,
title varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
body longtext COLLATE utf8mb4_unicode_ci
); `
_, err := db.Exec(createArticlesSQL)
checkError(err)
}
func getRouteVariable(parameterName string, r *http.Request) string {
vars := mux.Vars(r)
return vars[parameterName]
}
func getArticleByID(id string) (Article, error) {
article := Article{}
query := "SELECT * FROM articles WHERE id = ?"
err := db.QueryRow(query, id).Scan(&article.ID, &article.Title, &article.Body)
return article, err
}
func main() {
initDB()
createTables()
router.HandleFunc("/", homeHandler).Methods("GET").Name("home")
router.HandleFunc("/about", aboutHandler).Methods("GET").Name("about")
router.HandleFunc("/articles/{id:[0-9]+}", articlesShowHandler).Methods("GET").Name("articles.show")
router.HandleFunc("/articles", articlesIndexHandler).Methods("GET").Name("articles.index")
router.HandleFunc("/articles", articlesStoreHandler).Methods("POST").Name("articles.store")
router.HandleFunc("/articles/create", articlesCreateHandler).Methods("GET").Name("articles.create")
router.HandleFunc("/articles/{id:[0-9]+}/edit", articlesEditHandler).Methods("GET").Name("articles.edit")
router.HandleFunc("/articles/{id:[0-9]+}", articlesUpdateHandler).Methods("POST").Name("articles.update")
// 自定义 404 页面
router.NotFoundHandler = http.HandlerFunc(notFoundHandler)
// 中间件:强制内容类型为 HTML
router.Use(forceHTMLMiddleware)
http.ListenAndServe(":3000", removeTrailingSlash(router))
}