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

news2024/11/20 12:25:19

操作数据库

  • 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   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
}
.
.
.

在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 string
    ID          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.coloradmin.cn/o/1661635.html

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

相关文章

测径仪供风设备的操作和维护

关键字:测径仪供风系统,测径仪供风设备,测径仪冷却设备维护,测径仪冷却设备故障, 测径仪的供风设备包括高压鼓风机和空气过滤装置两部分。 鼓风机要求有独立的供电系统和电源开关&#xff0c;安装时应调整好风机叶轮的旋向&#xff0c;保证接通电源后鼓风机正常工作。另外&am…

Nginx rewrite项目练习

Nginx rewrite练习 1、访问ip/xcz&#xff0c;返回400状态码&#xff0c;要求用rewrite匹配/xcz a、访问/xcz返回400 b、访问/hello时正常访问xcz.html页面server {listen 192.168.99.137:80;server_name 192.168.99.137;charset utf-8;root /var/www/html;location / {root …

深度学习论文: LightGlue: Local Feature Matching at Light Speed

深度学习论文: LightGlue: Local Feature Matching at Light Speed LightGlue: Local Feature Matching at Light Speed PDF: https://arxiv.org/pdf/2306.13643 PyTorch代码: https://github.com/shanglianlm0525/CvPytorch PyTorch代码: https://github.com/shanglianlm0525/…

202466读书笔记|《一天一首古诗词》——借问梅花何处落,风吹一夜满关山

202466读书笔记|《一天一首古诗词》——借问梅花何处落&#xff0c;风吹一夜满关山 上册下册 《一天一首古诗词》作者李锡琴&#xff0c;蛮早前加入书架的已购买书籍&#xff0c;这次刚好有点时间&#xff0c;利用起来读完。 赏析没有细看&#xff0c;只读了诗词部分&#xff0…

YOLOv5改进 | 注意力机制 | 理解全局和局部信息的SE注意力机制

在深度学习目标检测领域&#xff0c;YOLOv5成为了备受关注的模型之一。本文给大家带来的是能够理解全局和局部信息的SE注意力机制。文章在介绍主要的原理后&#xff0c;将手把手教学如何进行模块的代码添加和修改&#xff0c;并将修改后的完整代码放在文章的最后&#xff0c;方…

多模态模型Mini-Gemini:代码模型数据均开源,MiniCPM小钢炮2.0全家桶四连发,可以在Android 手机端上运行的大模型,效果还不错

多模态模型Mini-Gemini&#xff1a;代码模型数据均开源&#xff0c;MiniCPM小钢炮2.0全家桶四连发&#xff0c;可以在Android 手机端上运行的大模型&#xff0c;效果还不错。 多模态模型Mini-Gemini&#xff1a;代码模型数据均开源 香港中文大学终身教授贾佳亚团队提出多模态模…

印象笔记使用技巧

印象笔记使用技巧 印象笔记&#xff08;Evernote&#xff09;是一款非常流行的笔记软件&#xff0c;它为用户提供了一个方便的平台来记录、整理和管理各种信息。无论是个人生活还是工作场景&#xff0c;印象笔记都可以帮助用户提高效率、整理思绪、轻松查找信息。下面是一些印象…

gpustat 不能使用问题

突然间就不能用了&#xff0c;可能是环境出了问题&#xff0c;如果GPU没问题的话&#xff0c;那么换个环境重新安装试一下&#xff08;pip install gpustat&#xff09;&#xff0c;目前是换个环境就可以了&#xff08;做个笔记&#xff09;

Mapreduce | 案例

根据提供的数据文件【test.log】 数据文件格式&#xff1a;姓名,语文成绩,数学成绩,英语成绩 完成如下2个案例&#xff1a; &#xff08;1&#xff09;求每个学科的平均成绩 &#xff08;2&#xff09;将三门课程中任意一门不及格的学生过滤出来 &#xff08;1&#xff09;求每…

DVWS靶场全总结(详细)--主要用来复习(暂未完善,累了大体框架已成)

目录 一、环境安装 二、开始闯关 2.1暴力破解 2.1.1 low: 2.1.2 medium: 2.2命令注入 2.2.1 low: ​编辑​编辑 2.2.2 medium: 2.3跨站请求伪造&#xff08;CSRF&#xff09; 2.3.1 low: 2.3.2 medium: 2.4文件包含漏洞 2.4.1 low&#xff1a; 2.4.2 medium: 2.…

暴力数据结构之栈与队列(队列详解)

1.队列的定义 队列是一种特殊的线性表&#xff0c;它遵循先进先出&#xff08;FIFO&#xff09;的原则。在队列中&#xff0c;只允许在表的一端进行插入操作&#xff08;队尾&#xff09;&#xff0c;而在另一端进行删除操作&#xff08;队头&#xff09;。这种数据结构确保了最…

【栈】Leetcode 比较含退格的字符串

题目讲解 844. 比较含退格的字符串 算法讲解 使用栈模拟&#xff0c;但遇到#字符就让栈顶元素出栈&#xff0c;但是在写的过程中有两点需要注意&#xff1a;当#出现在第一个位置&#xff0c;需要特殊处理一下&#xff1b;当栈为空的时候&#xff0c;还出现#字符需要特殊处理…

普通人可以抓住黄金价格大涨的投资机会吗?

黄金价格的波动对于投资者来说&#xff0c;总是充满了诱惑和挑战。近期&#xff0c;全球经济形势变化多端&#xff0c;地缘政治冲突频发&#xff0c;这些因素无不对黄金市场造成影响&#xff0c;使得黄金价格出现大幅波动。那么&#xff0c;作为普通人&#xff0c;能否抓住黄金…

会赚钱的人都在做这件事:你了解吗?

在我们日常生活的点滴中&#xff0c;以及在各种场合的交互中&#xff0c;利他思维始终扮演着不可或缺的角色。当我们追求合作与共赢时&#xff0c;单方面的自我立场显然是不够的&#xff0c;真正的关键在于换位思考&#xff0c;寻找并满足对方的需求。 互利互赢的核心理念正是利…

centos安装mysql-client

直接安装&#xff1a; yum install mysql-community-client报了错误No package mysql-community-client available. 原因&#xff1a;CentOS/RHEL系统默认的软件源中并不包含MySQL软件包&#xff0c;需要通过添加第三方存储库来获取MySQL相关软件 添加源 安装MySQL官方的Yum…

【智能优化算法】白鲨智能优化算法(White Shark Optimizer,WSO)

白鲨智能优化算法(White Shark Optimizer,WSO)是期刊“KNOWLEDGE-BASED SYSTEMS”&#xff08;中科院一区期刊 IF8.6&#xff09;的2022年智能优化算法 01.引言 白鲨智能优化算法(White Shark Optimizer,WSO)的核心理念和基础灵感来自大白鲨的行为&#xff0c;包括它们在导航和…

关于Ardupilot的固定翼(plane)的控制

起因 由于项目原来是使用的四旋翼,并且是PX4版本的四旋翼; 如今需要对无人机固定翼进行控制,并要求使用Ardupilot的固件进行研究。 特定在此记录对固定翼的学习,以和大家分享观点和交流学习。 PX4和Ardupilot关系 PX4和Ardupilot都是固件,固件就是软件的意思。两者都是…

Amesim基础篇-热仿真常用模型库-Air Conditioning-Pipes

前言 基于上文对空调库各个元件的介绍&#xff0c;本文进一步将其中的管路展开。 管路介绍 1 摩擦阻力管&#xff08;R&#xff09;&#xff1a; 具有阻力特性的管路&#xff0c;通过管长以及管截面计算阻力。 2 可调节阻力管&#xff08;R&#xff09;&#xff1a; 只具有…

进一步解读英伟达 Blackwell 架构、NVlink及GB200 超级芯片

2024年3月19日&#xff0c;英伟达CEO黄仁勋在GTC大会上公布了新一代AI芯片架构BLACKWELL&#xff0c;并推出基于该架构的超级芯片GB200&#xff0c;将助推数据处理、工程模拟、电子设计自动化、计算机辅助药物设计、量子计算和生成式 AI 等领域。 为了纪念杰出的数学家David H…

设计合理的IT运维服务目录:打造高效运维的蓝图

在数字化转型的浪潮中&#xff0c;一个设计合理、内容详尽的IT运维服务目录是连接服务提供者与消费者之间的桥梁&#xff0c;它不仅体现了服务设计的专业性&#xff0c;还直接影响着运维效率和服务质量。如何设计出既合理又高效的IT运维服务目录&#xff1f;让我们结合ITIL 4框…