【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://xiahunao.cn/news/3030109.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

STM32F103学习笔记 | 报错界面及解决方案 | 1.keil5中文注释的横竖(正与斜)问题

文章目录 一、报错界面二、解决方案参考文献 一、报错界面 二、解决方案 打开设置 在打开的设置选项卡中&#xff0c;图中Font显示的是这个软件当前设置的字体&#xff0c;可以看到字体是仿宋&#xff0c;这就是问题出现的原因&#xff0c;将之改成没有的字体就行了。 可以看…

一文了解spring事务特性

推荐工具 objectlog 对于重要的一些数据&#xff0c;我们需要记录一条记录的所有版本变化过程&#xff0c;做到持续追踪&#xff0c;为后续问题追踪提供思路。objectlog工具是一个记录单个对象属性变化的日志工具,工具采用spring切面和mybatis拦截器相关技术编写了api依赖包&a…

【.Net动态Web API】参数模型绑定来源

🚀前言 本文是《.Net Core进阶编程课程》教程专栏的导航站(点击链接,跳转到专栏主页,欢迎订阅,持续更新…) 专栏介绍:通过源码实例来讲解Asp.Net Core进阶知识点,让大家完全掌握每一个知识点。 专栏适用于人群:Web后端开发人员 ———————————————— …

ABB机器人IRB360介绍

随着自动化技术的不断发展&#xff0c;分拣和包装行业的应用也越来越广泛。 工业机器人扮演的角色也随之不断增加&#xff0c;其中ABB机器人的一款产品IRB 360 FlexPicker 在抓取和包装技术方面占有重要的地位。与传统的刚性自动化技术相比较&#xff0c;IRB 360具有高灵活性、…

Navicat Data Modeler Ess for Mac:强大的数据库建模设计软件

Navicat Data Modeler Ess for Mac是一款专为Mac用户设计的数据库建模与设计工具&#xff0c;凭借其强大的功能和直观的界面&#xff0c;帮助用户轻松构建和管理复杂的数据库模型。 Navicat Data Modeler Ess for Mac v3.3.17中文直装版下载 这款软件支持多种数据库系统&#x…

SQL注入(sqli-labs第一关)

sqli-labs第一关 方法一&#xff1a;手工注入 来到第一关&#xff0c;图上说我们需要一个数字的参数 于是我们先手工注入?id1 and 11 跟?id1 and 12发现页面没有报错 每张截图上面页面中有select查询语句&#xff0c;这是我在第一关的源码中加上了echo "$sql ";…

信息技术自主可控的意义,针对国产化替换,服务器虚拟化或比公有云更具优势

我们之前在文章《博通收购VMware后&#xff0c;经销商和用户如何应对&#xff1f;新出路&#xff1a;虚拟化国产替代&#xff0c;融入信创云生态》中提到&#xff1a; 从信创整体发展和政策标准来看&#xff0c;供应商必须满足两个条件&#xff1a;一是融入国产信息技术生态&am…

换新机,统信UOS V20桌面专业版(1070)教你轻松搞定数据迁移丨年度更新

硬件装备升级&#xff0c;数据如何快速迁移&#xff1f; 规模化汰换PC&#xff0c;怎样实现便捷部署&#xff1f; 统信UOS V20桌面专业版&#xff08;1070&#xff09; 带来两大换机神器&#xff01; 整机备份、数据迁移 手把手帮你轻松构建新质生产力工具&#xff01; 一键克…

短信群发公司通道有哪些要求

短信群发公司通道有哪些要求 网络稳定性 短信群发公司的通道在进行时需要具备良好的网络稳定性。这意味着通道需要能够稳定连接到互联网&#xff0c;并具备高速传输能力。在网络不稳定或者传输速度慢的情况下&#xff0c;可能会受到影响&#xff0c;甚至导致失败。 高可靠性 …

【算法入门赛】B. 自助店评分(C++、STL、推荐学习)题解与代码

比赛地址&#xff1a;https://www.starrycoding.com/contest/8 题目描述 在上一场的入门教育赛中&#xff0c;牢 e e e找到了所有自助店的位置&#xff0c;但是他想发现一些“高分好店”&#xff0c;于是他利用爬虫技术从“小众点评APP”中爬取了武汉所有自助店的评分。 评分…

《ESP8266通信指南》15-MQTT连接、订阅MQTT主题并打印消息(基于Lua|适合新手|非常简单)

往期 《ESP8266通信指南》14-连接WIFI&#xff08;基于Lua&#xff09;-CSDN博客 《ESP8266通信指南》13-Lua 简单入门&#xff08;打印数据&#xff09;-CSDN博客 《ESP8266通信指南》12-Lua 固件烧录-CSDN博客 《ESP8266通信指南》11-Lua开发环境配置-CSDN博客 《ESP826…

面试中算法(找到两个数组的中位数)

有两个升序数组&#xff0c;如何找出这两个数组归并以后新的升序数组的中位数? 中位数把一个升序数组分成了长度相等的两部分&#xff0c;其中左半部分的最大值永远小于或等于右半部分的最小值。 如上图所示&#xff0c;对于偶数长度的数组&#xff0c;可以根据中位数分成长度…

一文了解webpack和vite中Tree-Shaking

1、什么是Tree-Shaking 1.1 摇树优化&#xff08;Tree Shaking&#xff09;是Webpack中一种用于优化JavaScript代码的技术。它的目标是通过静态分析&#xff0c;从代码中剔除未被使用的模块&#xff0c;从而减少最终打包文件的大小。 1.2 Tree-shaking 它的名字来源于通过摇晃…

物联网技术在数字化工厂中的应用,你知道多少?——青创智通

工业物联网解决方案-工业IOT-青创智通 物联网&#xff08;IoT&#xff09;技术在数字化工厂的应用正日益成为工业革命的重要推动力。随着科技的飞速发展&#xff0c;物联网技术不断革新&#xff0c;其在数字化工厂中的应用也呈现出愈发广泛和深入的态势。本文将详细探讨物联网…

传输层之 TCP 协议

TCP协议段格式 源/目的端口号&#xff1a;表示数据是从哪个进程来&#xff0c;到哪个进程去。 序号&#xff1a;发送数据的序号。 确认序号&#xff1a;应答报文的序号&#xff0c;用来回复发送方的。 4 位首部长度&#xff1a;一个 TCP 报头&#xff0c;长度是可变的&#xff…

Android 屏幕适配全攻略(上)-掌握屏幕单位,应对千变万化的设备

本文从 Android 开发中常见的长度单位 px、dp、sp 入手&#xff0c;详细介绍了它们的特点及转换关系。 接着深入探讨了屏幕尺寸、分辨率、像素密度等重要的屏幕指标&#xff0c;帮助读者全面理解它们之间的联系。最后&#xff0c;通过实例代码演示了如何在代码中进行单位转换&…

第一章 buffer cache管理 - 2 原理机制

本章节主要介绍缓冲区管理器机制&#xff0c;从原理上介绍共享缓冲区如何管理内存页。 1、缓冲区管理器的结构 PostgreSQL缓冲区管理器由缓冲区hash表、缓冲区buffer描述符和缓冲池组成。下面依次介绍这几个结构。 1.1 缓冲区标签 typedef struct buftag {RelFileNode rnod…

Python运维之协程

目录 一、定义协程 二、并发 三、异步请求 协程是一种轻量级的线程&#xff0c;它通过保存和恢复寄存器上下文和栈来实现调度切换&#xff0c;从而保留函数执行的状态。 这种机制使得协程在处理I/O密集型任务时效率较高&#xff0c;因为它们可以在I/O操作期间让出CPU&#…

5g视频彩信和普通彩信有什么区别

5G视频彩信和普通彩信有什么区别 随着科技的不断进步&#xff0c;手机通信技术也在迅速发展。5G技术的出现&#xff0c;为彩信传输提供了更高的速度和更广的带宽。在这种背景下&#xff0c;5G视频彩信和普通彩信成为了人们关注的焦点。本文将探讨这两种彩信的区别。 5G视频彩信…

Java数组的应用---选择排序(Select Sort)

一、需求&#xff1a;选择排序(Select Sort)&#xff0c;进行升序显示 在一组排列中把最大的数取出来放在一个新的列表里&#xff0c;再删去&#xff0c;在取最大的数出来&#xff0c;依次类推直到取到最后一个数字 二、定义一个无序的一维数组&#xff0c;并输出数组 程序运…