【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.rhkb.cn/news/324492.html

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

相关文章

扩展学习|本体研究进展

文献来源&#xff1a; 王向前,张宝隆,李慧宗.本体研究综述[J].情报杂志,2016,35(06):163-170. 一、本体的定义 本体概念被引入人工智能、知识工程等领域后被赋予了新的含义。然而不同的专家学者对本体的理解不同,所给出的定义也有所差异。 人工智能领域的学者Neches(1991)等人对…

苹果新款 M4 芯片专注于 AI

爆炸性消息&#xff01;苹果的新一代 M4 芯片来了&#xff01;这家伙拥有 38 万亿次操作的超强神经引擎&#xff0c;速度比苹果 A11 芯片的 NPU 快 60 倍&#xff01;虽然它的速度还没有达到 Snapdragon X Elite 的 45 TOPS&#xff0c;但苹果自夸 M4 将提供与最新 PC 芯片相同…

QT-TCP通信

网上的资料太过于书面化&#xff0c;所以看起来有的让人云里雾里&#xff0c;看不懂C-tcpsockt和S-tcpsocket的关系 所以我稍微画了一下草图帮助大家理解两个套接字之间的关系。字迹有的飘逸勉强看看 下面是代码 服务端&#xff1a; MainWindow::MainWindow(QWidget *parent) …

Oracle-一次TX行锁堵塞事件

问题背景&#xff1a; 接用户问题报障&#xff0c;应用服务出现大量会话堆积现象&#xff0c;数据库锁堵塞严重&#xff0c;需要协助进行问题定位和排除。 问题分析&#xff1a; 登录到数据库服务器上&#xff0c;首先查看一下数据库当前的等待事件情况&#xff0c;通过gv$ses…

C++ 课程设计,图片元素的获取,拟合椭圆

WX: help-assignment C 课程设计&#xff0c;图片元素的获取&#xff0c;拟合椭圆 代码实现步骤&#xff1a; 读取.jpg 图片(259219443)&#xff0c;将图片中的x∈(700,1450)y∈(1,1944)的像素值置为0&#xff1b; 提取图片的r、g、b 通道&#xff0c;按照(1r-0.6g-0.3*b)对…

【QA】Java集合常用的函数

文章目录 前言Collection接口通用函数 | Collections工具类通用函数 | List接口 Set接口List接口ArrayListLinkedList Set接口TreeSetHashSetLinkedHashSet Map接口通用函数TreeMapHashMapLinkedHashMap 前言 本文介绍Java集合中常用的函数。 Collection接口 通用函数 | Co…

林更新博士之路星途璀璨再启航

林更新&#xff1a;博士之路&#xff0c;星途璀璨再启航在这个充满机遇与挑战的时代&#xff0c;有一位演员以其出色的演技和不懈的努力&#xff0c;赢得了无数观众的喜爱。他&#xff0c;就是林更新。今日&#xff0c;一条消息如重磅炸弹般在娱乐圈炸开&#xff0c;让无数粉丝…

Android Studio(AS)使用别人的项目与gradle包并运行项目

一、问题描述 在进行AS开发时&#xff0c;我们可能会使用到别人的项目&#xff0c;但发现别人把项目发给我们后会发现gradle项目同步失败o(≧口≦)o&#xff0c;此时计有三&#xff1a; 1.横行霸道、豪取抢夺&#xff1a;直接空降到项目人那里&#xff0c;强他的电脑占为己有…

Mac电脑安装打开APP显示问题已损坏 问题解决

当MAC电脑安装完软件打开时&#xff0c;显示文件已损坏&#xff0c;无法打开。搜了很多教程终于找到解决方案&#xff0c;记录下方便以后再用。 我的mac电脑是intel芯片的&#xff0c;如果你遇到这个问题&#xff0c;可以参考我的这个方案。 1.首先当打开软件后出现 “xx软件已…

PG 检查点管理与Oracle的比较

之前介绍过&#xff0c;在任何数据库中&#xff0c;一条DML操作执行都需要在内存中执行&#xff0c;但当操作越来越多&#xff0c;总有时候内存会写满&#xff0c;这时候就需要把内存中的块写入到磁盘&#xff0c;释放内存&#xff0c;保存数据。 写入到磁盘这一步&#xff0c;…

PHP高级教程

&#x1f40c;博主主页&#xff1a;&#x1f40c;​倔强的大蜗牛&#x1f40c;​ &#x1f4da;专栏分类&#xff1a;PHP &#x1f4da;参考教程&#xff1a;菜鸟\编程网❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、PHP 多维数组 二、PHP date&#xff08;&#…

pyqt 按钮常用格式Qss设置

pyqt 按钮常用格式Qss设置 QSS介绍按钮常用的QSS设置效果代码 QSS介绍 Qt Style Sheets (QSS) 是 Qt 框架中用于定制应用程序界面样式的一种语言。它类似于网页开发中的 CSS&#xff08;Cascading Style Sheets&#xff09;&#xff0c;但专门为 Qt 应用程序设计。使用 QSS&am…

Sora惊艳亮相:AI技术掀起创作革命,影视产业迎来新风貌!

Sora平台近期发布了名为"Sora首次印象"的更新&#xff0c;为用户带来了令人瞩目的变化。该更新不仅展示了Sora平台的发展方向&#xff0c;还介绍了其在电影制作、广告宣传等领域的潜在应用。 同时&#xff0c;Sora的首席执行官Sam Altman与好莱坞影视工作室进行了会…

Nginx Location配置详解

目录 一、Location语法结构1、语法结构说明2、各个标识的含义和优先级3、配置请求的根目录4、替换请求的uri 二、Location具体示例1、精确匹配2、正则匹配3、常规字符串匹配 三、调试Location的小技巧 一、Location语法结构 1、语法结构说明 在Nginx官方文档中定义的location…

Linux进程——Linux下常见的进程状态

前言&#xff1a;在进程学习这一块&#xff0c;我们主要学习的就是PCB这个进程控制块&#xff0c;而PBC就是用来描述进程的结构体&#xff0c;而进程状态就是PCB结构体中的一个变量。 本篇主要内容&#xff1a; 操作系统中的进程状态Linux下的进程状态 在开始之前&#xff0c;我…

C++细节,可能存在的隐患,面试题03

文章目录 11. C编译过程12. const vs #define12.1. 全局const vs 局部const 13. C内存分区14. C变量作用域14.1. 常量 vs 全局变量 vs 静态变量 15. C类型转换16. 函数指针17. 悬空指针 vs 野指针18. 为什么使用空指针&#xff0c;建议使用nullptr而不是NULL&#xff1f; 11. C…

噪声嵌入提升语言模型微调性能

在自然语言处理&#xff08;NLP&#xff09;的快速发展中&#xff0c;大模型&#xff08;LLMs&#xff09;的微调技术一直是研究的热点。最近&#xff0c;一篇名为《NEFTUNE: NOISY EMBEDDINGS IMPROVE INSTRUCTION FINETUNING》的论文提出了一种新颖的方法&#xff0c;通过在训…

Gitlab搭建

Gitlab搭建 一.gitlab安装二.gitlab安装目录三.gitlab常用命令汇总四.gitlab访问测试1.登录gitlab2.配置默认访问密码3.登陆自己创建的账户 五.gitlab图形化操作1.gitlab关闭自动注册2.gitlab免密登录3.gitlab创建项目4.使用管理员创建账户5.gitlab 添加邮箱设置5.1开启SMTP功能…

【直观对比】RTX3090和RTX4090的计算能力的参数对比

1. 引言 随着大模型的火热&#xff0c;越来越多的人希望可以购买一个硬件进行大模型的推理&#xff0c;特别是在3090和4090显卡的对比上&#xff0c;需求比较大。目前已有的显卡对比&#xff0c;大多面向于高端的A/H100或者800系列的对比和分析&#xff0c;如图所示。 项目A1…

Minimal-Supervised Medical Image Segmentation via Vector Quantization Memory

文章目录 Minimal-Supervised Medical Image Segmentation via Vector Quantization Memory摘要方法实验结果 Minimal-Supervised Medical Image Segmentation via Vector Quantization Memory 摘要 辅助重构分支&#xff1a;该分支通过提供额外的监督并产生学习视觉表示所需…