【go项目01_学习记录10】

操作数据库

  • 1 插入数据
  • 2 显示文章
    • 2.1 修改 articlesShowHandler() 函数
    • 2.2 代码解析
  • 3 编辑文章
    • 3.1 添加路由
    • 3.2 编辑articlesEditHandler()
    • 3.3 新建 edit 模板
    • 3.4 代码重构
    • 3.5 完善articlesUpdateHandler()
    • 3.6 测试更新
    • 3.7 封装表单验证

1 插入数据

.
.
.
func articlesStoreHandler(w http.ResponseWriter, r *http.Request) {...// 检查是否有错误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 {...}
}func saveArticleToDB(title string, body string) (int64, error) {// 变量初始化var (id   int64err  errorrs   sql.Resultstmt *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. 插入成功的话,会返回自增 IDif id, err = rs.LastInsertId(); id > 0 {return id, nil}return 0, err
}
.
.
.

在Go语言中,defer stmt.Close()是一种常见的用法,用于在函数执行完毕后延迟关闭资源。具体来说,defer关键字用于延迟执行一个函数调用,这个函数调用通常是用来释放资源或执行清理操作。在这个例子中,stmt.Close()表示在当前函数执行完毕后,会调用stmt对象的Close()方法来关闭资源。
这种用法对于确保资源的正确释放非常有用,因为无论函数是通过正常返回还是发生异常终止,defer语句都会被执行。这样可以避免资源泄漏和确保程序的稳定性。

fmt.Fprint(w, “插入成功,ID 为”+strconv.FormatInt(lastInsertID, 10))
具体来说,strconv.FormatInt函数的第一个参数是要转换的整数值,即lastInsertID,第二个参数是指定转换的进制。在这里,第二个参数是10,表示要将整数转换为10进制的字符串。
例如,如果lastInsertID的值为123,那么strconv.FormatInt(123, 10)将返回字符串"123"。

rs, err = stmt.Exec(title, body)
返回值是一个 sql.Result 对象rs,定义如下

type Result interface {
// 使用 INSERT 向数据插入记录,数据表有自增 ID 时,该函数有返回值
LastInsertId() (int64, error)
// 表示影响的数据表行数,常用于 UPDATE/DELETE 等 SQL 语句中
RowsAffected() (int64, error)
}

因为我们的 articles 表里有设置 id 字段为自增 ID,故在我们的代码中,使用 rs.LastInsertId() 来判断是否执行成功,成功的话就返回这条新创建数据的 ID:

// 4. 插入成功的话,会返回自增 ID
if id, err = rs.LastInsertId(); id > 0 {return id, nil
}

访问localhost:3000/articles/create并填入测试数据
在这里插入图片描述

在这里插入图片描述

数据库中的表

2 显示文章

显示文章分两个步骤:
(1)读取数据
(2)渲染模板

2.1 修改 articlesShowHandler() 函数

.
.
.// Article  对应一条文章数据
type Article struct {Title, Body stringID          int64
}func articlesShowHandler(w http.ResponseWriter, r *http.Request) {// 1. 获取 URL 参数vars := mux.Vars(r)id := vars["id"]// 2. 读取对应的文章数据article := Article{}query := "SELECT * FROM articles WHERE id = ?"err := db.QueryRow(query, id).Scan(&article.ID, &article.Title, &article.Body)// 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. 读取成功fmt.Fprint(w, "读取成功,文章标题 —— "+article.Title)}
}
.
.
.

2.2 代码解析

QueryRow() 来读取单条数据

// 2. 读取对应的文章数据
article := Article{}
query := “SELECT * FROM articles WHERE id = ?”
err := db.QueryRow(query, id).Scan(&article.ID, &article.Title, &article.Body)

在这里插入图片描述
渲染模板
修改代码

.
.
.
func articlesShowHandler(w http.ResponseWriter, r *http.Request) {...// 3. 如果出现错误if err != nil {...} else {// 4. 读取成功,显示文章tmpl, err := template.ParseFiles("resources/views/articles/show.gohtml")checkError(err)err = tmpl.Execute(w, article)checkError(err)}
}
.
.
.

模板文件resources/views/articles/show.gohtml

<!DOCTYPE html>
<html lang="en">
<head><title>{{ .Title }} —— 我的技术博客</title><style type="text/css">.error {color: red;}</style>
</head>
<body><p>ID: {{ .ID }}</p><p>标题: {{ .Title }}</p><p>内容:{{ .Body }}</p>
</body>
</html>

保存成功后访问 localhost:3000/articles/1
在这里插入图片描述

log.Fatal(err):这是一个标准库log包中的函数,用于输出错误信息并终止程序执行。它会将err作为参数输出到标准错误输出,并调用os.Exit(1)来终止程序执行。这样可以确保在遇到严重错误时,程序会立即停止运行。

3 编辑文章

3.1 添加路由

在这里插入图片描述

3.2 编辑articlesEditHandler()

.
.
.func articlesEditHandler(w http.ResponseWriter, r *http.Request) {// 1. 获取 URL 参数vars := mux.Vars(r)id := vars["id"]// 2. 读取对应的文章数据article := Article{}query := "SELECT * FROM articles WHERE id = ?"err := db.QueryRow(query, id).Scan(&article.ID, &article.Title, &article.Body)// 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) {fmt.Fprint(w, "更新文章")
}func articlesIndexHandler(w http.ResponseWriter, r *http.Request) {
.
.
.

3.3 新建 edit 模板

<!DOCTYPE html>
<html lang="en">
<head><title>编辑文章 —— 我的技术博客</title><style type="text/css">.error {color: red;}</style>
</head>
<body><form action="{{ .URL }}" method="post"><p><input type="text" name="title" value="{{ .Title }}"></p>{{ with .Errors.title }}<p class="error">{{ . }}</p>{{ end }}<p><textarea name="body" cols="30" rows="10">{{ .Body }}</textarea></p>{{ with .Errors.body }}<p class="error">{{ . }}</p>{{ end }}<p><button type="submit">更新</button></p></form>
</body>
</html>

测试http://localhost:3000/articles/1/edit
在这里插入图片描述
在这里插入图片描述

3.4 代码重构

articlesEditHandler()和articlesShowHandler()中有重复的代码,将其提取出来封装成函数。这样做不仅可以少写代码,也可以提高代码的可维护性。
一般不需要封装会影响返回结果的逻辑处理,所以注释 3 中的错误处理与模板渲染部分我们保持不变。
在这里插入图片描述
重构articlesEditHandler()和articlesShowHandler () 函数

在这里插入图片描述

在这里插入图片描述

3.5 完善articlesUpdateHandler()

.
.
.
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 := 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 个字节"}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)}}
}
.
.
.

在这里插入图片描述

3.6 测试更新

访问 localhost:3000/articles/1/edit ,修改内容:
在这里插入图片描述

在这里插入图片描述
再次访问 localhost:3000/articles/1/edit ,将标题修改到只有两个字,点击更新:
在这里插入图片描述
在这里插入图片描述

3.7 封装表单验证

articlesUpdateHandler() 中的表单验证与 articlesStoreHandler() 使用同一套代码,将其抽出来作为单独的函数。

//封装表单验证
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
}

validateArticleFormData() 函数应用到上文提到的两个函数中
在这里插入图片描述
在这里插入图片描述
测试 localhost:3000/articles/1/edit , 都正常。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/686160.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在哪里打印资料比较便宜

在数字时代&#xff0c;我们常常需要在各种文档、资料之间穿梭&#xff0c;然而&#xff0c;有时候我们需要的并不是数字版&#xff0c;而是纸质版。那么&#xff0c;在哪里打印资料比较便宜呢&#xff1f; 琢贝云打印以其超低的价格&#xff0c;优质的打印服务&#xff0c;赢…

第十一篇:操作系统新纪元:智能融合、量子跃迁与虚拟现实的交响曲

操作系统新纪元&#xff1a;智能融合、量子跃迁与虚拟现实的交响曲 1 引言 在数字化的浪潮中&#xff0c;操作系统如同一位智慧的舵手&#xff0c;引领着信息技术的航船穿越波涛汹涌的海洋。随着人工智能、物联网、量子计算等前沿技术的蓬勃发展&#xff0c;操作系统正站在一个…

分布式的计算框架之Spark(python第三方库视角学习PySpark)

基本介绍 Apache Spark是专为大规模数据处理而设计的快速通用的计算引擎 。现在形成一个高速发展应用广泛的生态系统。 特点介绍 Spark 主要有三个特点&#xff1a; 首先&#xff0c;高级 API 剥离了对集群本身的关注&#xff0c;Spark 应用开发者可以专注于应用所要做的计…

[NISACTF 2022]popchains

第一步&#xff1a;看到 include($value); 作为链尾&#xff0c;则要触发 append($value) -->>__invoke()&#xff0c;看到$function()。 __invoke()&#xff1a;对象以函数形式被调用时触发 第二步&#xff1a;$function() &#xff0c;则要触发 __get($key)&#xff0…

【电子商务设计师】2024年5月考试答题技巧与注意事项

电子商务设计师答题技巧&#xff1a; 1、综合知识 &#xff08;1&#xff09;首先是分析试题的技巧 --先看清楚问题&#xff0c;再看选项&#xff1b; --判断题目到底考察的是什么知识点&#xff0c;排除干扰项。 &#xff08;2&#xff09;掌握答题的技巧 --题目往往会选…

Java入门基础学习笔记13——数据类型

数据类型的分类&#xff1a; 基本数据类型 引用数据类型 基本数据类型&#xff1a;4大类8种类型&#xff1a; 定义整形用int&#xff0c;再大的数用long。 package cn.ensource.variable;public class VariableDemo2 {public static void main(String[] args) {//目标&#x…

文章分享:《肿瘤DNA甲基化标志物检测及临床应用专家共识(2024版)》

本文摘自于《肿瘤DNA甲基化标志物检测及临床应用专家共识&#xff08;2024版&#xff09;》 目录 1. DNA甲基化标志物概述 2 DNA甲基化标志物的临床检测 2.1 临床样本前处理注意事项 2.2 DNA甲基化标志物检测技术方法 2.2.1 DNA提取与纯化 2.2.2 DNA转化 2.2.3 DNA 甲基…

中国地面基本气象逐小时数据获取方式

环境气象数据服务平台提供了全国大约2100个点位&#xff0c;2023年1月1日至今的小时级数据。包括气温、气压、湿度、风、降水等要素。 数据基于ECMWF ERA5-Land Hourly陆面再分析资料和中国地面基本气象观测逐三小时数据&#xff0c;使用机器学习模型加工所得&#xff0c;对比…

Hive Windows Functions 窗口函数

Hive Windows Functions 窗口函数 在 Hive 中&#xff0c;窗口函数&#xff08;Window Functions&#xff09;用于在查询结果中执行聚合、排序和分析操作&#xff0c;而无需将数据分组。窗口函数允许你在查询结果中的一组行上执行计算&#xff0c;而不会改变原始数据的行数&am…

基于SSM的文化遗产的保护与旅游开发系统(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的文化遗产的保护与旅游开发系统&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;…

【typescript测试 - Jest 配置与使用】

安装 npm install --save-dev types/jestnpm install --save-dev ts-jest配置 tsconfig.json {"compilerOptions": {"types": ["jest"]} }jest.config.js module.exports {preset: ts-jest,testEnvironment: node, };使用 // add.js funct…

企业数据有什么价值?

在当下的数字经济时代&#xff0c;数据已上升为国家重要的基础性战略资源&#xff0c;加快建设数字中国、网络强国这一蓝图的实现&#xff0c;离不开数据要素的支撑。数据作为新型生产要素&#xff0c;具有非消耗性、非竞争性等特征&#xff0c;为突破传统生产要素的增长约束提…