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

news2025/1/10 23:28:33

重构MVC

  • 1 Article 模型
    • 1.1 首先创建 Article 模型文件
    • 1.2 接下来创建获取文章的方法
    • 1.3 新增 types.StringToUint64()函数
    • 1.4 修改控制器的调用
    • 1.5 重构 route 包
    • 1.6 通过 SetRoute 来传参对象变量
    • 1.7 新增方法:
    • 1.8 控制器将 Int64ToString 改为 Uint64ToString
    • 1.9 模板里修改 Int64ToString 为 Uint64ToString:
    • 1.10 修改logger:
    • 1.11 修改articles_controller.go的判断:
  • 2 重构文章列表
    • 2.1 移动articles.index路由
    • 2.2 添加Index控制器方法
    • 2.3 获取文章列表 GetAll
    • 2.4 调试程序
    • 2.5 代码版本标记

1 Article 模型

解决undefined: getArticleByID 的报错

config := mysql.New(mysql.Config{
        DSN: "root:secret@tcp(127.0.0.1:3306)/goblog?charset=utf8&parseTime=True&loc=Local",
    })

在提供的代码中,用户名是"root",密码是"secret"。这段代码中的DSN(Data Source Name)参数指定了数据库连接的信息,其中"root"是用户名,“secret"是密码。所以,根据这段代码,用户是"root”,密码是"secret"。

1.1 首先创建 Article 模型文件

app/models/article/article.go

// Package article 应用的文章模型
package article

// Article 文章模型
type Article struct {
    ID    uint64 
    Title string
    Body  string
}

1.2 接下来创建获取文章的方法

app/models/article/crud.go

package article

import (
    "goblog/pkg/model"
    "goblog/pkg/types"
)

// Get 通过 ID 获取文章
func Get(idstr string) (Article, error) {
    var article Article
    id := types.StringToUint64(idstr)
    if err := model.DB.First(&article, id).Error; err != nil {
        return article, err
    }

    return article, nil
}

First() 是 gorm.DB 提供的用以从结果集中获取第一条数据的查询方法,.Error 是 GORM 的错误处理机制。与常见的 Go 代码不同,因 GORM 提供的是链式 API,如果遇到任何错误,GORM 会设置 *gorm.DB 的 Error 字段。

1.3 新增 types.StringToUint64()函数

pkg/types/converter.go

.
.
.

// StringToUint64 将字符串转换为 uint64
func StringToUint64(str string) uint64 {
    i, err := strconv.ParseUint(str, 10, 64)
    if err != nil {
        logger.LogError(err)
    }
    return i
}

1.4 修改控制器的调用

app/http/controllers/articles_controller.go

.
.
.

// Show 文章详情页面
func (*ArticlesController) Show(w http.ResponseWriter, r *http.Request) {

    .
    .
    .

    // 2. 读取对应的文章数据
    article, err := article.Get(id)

    .
    .
    .
}

1.5 重构 route 包

pkg/route/router.go

// Package route 路由相关
package route

import (
    "goblog/pkg/logger"
    "net/http"

    "github.com/gorilla/mux"
)

var route *mux.Router

// SetRoute 设置路由实例,以供 Name2URL 等函数使用
func SetRoute(r *mux.Router) {
    route = r
}

// Name2URL 通过路由名称来获取 URL
func Name2URL(routeName string, pairs ...string) string {
    url, err := route.Get(routeName).URL(pairs...)
    if err != nil {
        logger.LogError(err)
        return ""
    }

    return url.String()
}
.
.
.

1.6 通过 SetRoute 来传参对象变量

bootstrap/route.go

.
.
.
// SetupRoute 路由初始化
func SetupRoute() *mux.Router {
    router := mux.NewRouter()
    routes.RegisterWebRoutes(router)

    route.SetRoute(router)

    return router
}

1.7 新增方法:

pkg/types/converter.go

.
.
.

// Uint64ToString 将 uint64 转换为 string
func Uint64ToString(num uint64) string {
    return strconv.FormatUint(num, 10)
}

1.8 控制器将 Int64ToString 改为 Uint64ToString

app/http/controllers/articles_controller.go

.
.
.
// Show 文章详情页面
func (*ArticlesController) Show(w http.ResponseWriter, r *http.Request) {
    .
    .
    .
    } else {
        // 4. 读取成功,显示文章
        tmpl, err := template.New("show.gohtml").
            Funcs(template.FuncMap{
                "RouteName2URL":  route.Name2URL,
                "Uint64ToString": types.Uint64ToString,
            }).

1.9 模板里修改 Int64ToString 为 Uint64ToString:

resources/views/articles/show.gohtml

    {{/* 构建删除按钮  */}}
    {{ $idString := Uint64ToString .ID  }}
    <form action="{{ RouteName2URL "articles.delete" "id" $idString }}" method="post">
        <button type="submit" onclick="return confirm('删除动作不可逆,请确定是否继续')">删除</button>
    </form>

浏览 localhost:3000/articles/3 ,显示:
在这里插入图片描述

访问localhost:3000/articles/1000,显示拒绝访问

1.10 修改logger:

pkg/logger/logger.go

// Package logger 日志相关
package logger

import "log"

// LogError 当存在错误时记录日志
func LogError(err error) {
    if err != nil {
        log.Println(err)
    }
}

1.11 修改articles_controller.go的判断:

GORM 有单独的错误类型 —— gorm.ErrRecordNotFound
app/http/controllers/articles_controller.go

.
.
.

// Show 文章详情页面
func (*ArticlesController) Show(w http.ResponseWriter, r *http.Request) {

    .
    .
    .

    // 3. 如果出现错误
    if err != nil {
        if err == gorm.ErrRecordNotFound { // <---- 这一行
            // 3.1 数据未找到
            .
            .
            .
        } else {
            // 3.2 数据库错误
            .
            .
            .
        }
    } else {
        // 4. 读取成功,显示文章
        .
        .
        .
    }
}

再次访问localhost:3000/articles/521
在这里插入图片描述

2 重构文章列表

2.1 移动articles.index路由

把 main.go 里的articles.index路由剪切到 web.go 中,并简单修改
routes/web.go

.
.
.
func  RegisterWebRoutes(r *mux.Router) {
    .
    .
    .
    r.HandleFunc("/articles", ac.Index).Methods("GET").Name("articles.index")
}

2.2 添加Index控制器方法

将 main.go 中 articlesIndexHandler 函数剪切并修改名称到控制器中:

app/http/controllers/articles_controller.go

// Index 文章列表页
func (*ArticlesController) Index(w http.ResponseWriter, r *http.Request) {

    // 1. 获取结果集
    articles, err := article.GetAll()

    if err != nil {
        // 数据库错误
        logger.LogError(err)
        w.WriteHeader(http.StatusInternalServerError)
        fmt.Fprint(w, "500 服务器内部错误")
    } else {
        // 2. 加载模板
        tmpl, err := template.ParseFiles("resources/views/articles/index.gohtml")
        logger.LogError(err)

        // 3. 渲染模板,将所有文章的数据传输进去
        err = tmpl.Execute(w, articles)
        logger.LogError(err)
    }
}

2.3 获取文章列表 GetAll

获取文章列表封装到模型的 GetAll 方法里:

app/models/article/crud.go

// GetAll 获取全部文章
func GetAll() ([]Article, error) {
    var articles []Article
    if err := model.DB.Find(&articles).Error; err != nil {
        return articles, err
    }
    return articles, nil
}

上面两个步骤的代码的功能与下面代码一样:

// 1. 执行查询语句,返回一个结果集
rows, err := db.Query("SELECT * from articles")
logger.LogError(err)
defer rows.Close()

var articles []Article
//2. 循环读取结果
for rows.Next() {
    var article Article
    // 2.1 扫码每一行的结果并赋值到一个 article 对象中
    err := rows.Scan(&article.ID, &article.Title, &article.Body)
    logger.LogError(err)
    // 2.2 将 article 追加到 articles 的这个数组中
    articles = append(articles, article)
}
// 2.3 检测循环时是否发生错误
err = rows.Err()
logger.LogError(err)

GORM 的优势之一:不需要时刻记住关闭连接
访问 localhost:3000/articles :
在这里插入图片描述
没有显示数据

2.4 调试程序

GORM 提供了一个调试功能,允许在命令行里查看请求的 SQL 信息,config里面设置:

pkg/model/model.go

// Package model 应用模型数据层
package model

import (
    "goblog/pkg/logger"

    "gorm.io/gorm"
    gormlogger "gorm.io/gorm/logger"

    // GORM 的 MSYQL 数据库驱动导入
    "gorm.io/driver/mysql"
)

// DB gorm.DB 对象
var DB *gorm.DB

// ConnectDB 初始化模型
func ConnectDB() *gorm.DB {

    var err error

    config := mysql.New(mysql.Config{
        DSN: "root:secret@tcp(127.0.0.1:3306)/goblog?charset=utf8&parseTime=True&loc=Local",
    })

    // 准备数据库连接池
    DB, err = gorm.Open(config, &gorm.Config{
        Logger: gormlogger.Default.LogMode(gormlogger.Info),
    })

    logger.LogError(err)

    return DB
}

顶部的 import 语句,导入 gorm/logger 时,因 goblog/pkg/logger 名称冲突,故为其指定名称:
gormlogger “gorm.io/gorm/logger”
在这里插入图片描述
刷新 localhost:3000/articles 页面
在这里插入图片描述
[rows:3] 意味着从数据库了成功取出了三条数据。

试着在控制器里打印一下 articles 变量:
app/http/controllers/articles_controller.go

.
.
.
// Index 文章列表页
func (*ArticlesController) Index(w http.ResponseWriter, r *http.Request) {

    // 1. 获取结果集
    articles, err := article.GetAll()

    fmt.Println("文章数据", articles)

    .
    .
    .
}

刷新 localhost:3000/articles 页面,观察命令行:
在这里插入图片描述
经调试,数据没问题,模板index.gohtml的问题
resources/views/articles/index.gohtml查看模板

<!DOCTYPE html>
<html lang="en">
<head>
    <title>所有文章 —— 我的技术博客</title>
    <style type="text/css">.error {color: red;}</style>
</head>
<body>
    <h1>所有文章</h1>
    <ul>
        {{ range $key, $article := . }}
            <li><a href="{{ $article.Link }}"><strong>{{ $article.ID }}</strong>: {{ $article.Title }}</a></li>
        {{ end }}
    </ul>
</body>
</html>

$article.Link 这是一个对象方法,还未创建,将 main 里的对应方法移动到模型中,并简单修改:
app/models/article/article.go

.
.
.
// Link 方法用来生成文章链接
func (article Article) Link() string {
    return route.Name2URL("articles.show", "id", strconv.FormatUint(article.ID, 10))
}

刷新 localhost:3000/articles 页面
在这里插入图片描述
问题解决,清理刚才调试的内容:

  1. 删除 fmt.Println(“文章数据”, articles)
  2. 设置日志级别为Warn即可,pkg/model/model.go 中设置为Logger: gormlogger.Default.LogMode(gormlogger.Warn)
  3. 删除main.go中的 articlesIndexHandler 和 Link 这两个函数

2.5 代码版本标记

git add .
git commit -m "重构文章列表页面"
git push  //注释,push到远程github上

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

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

相关文章

Linux程序开发(八):操作系统进程通信编程

Tips&#xff1a;"分享是快乐的源泉&#x1f4a7;&#xff0c;在我的博客里&#xff0c;不仅有知识的海洋&#x1f30a;&#xff0c;还有满满的正能量加持&#x1f4aa;&#xff0c;快来和我一起分享这份快乐吧&#x1f60a;&#xff01; 喜欢我的博客的话&#xff0c;记得…

Java面向对象-常用类 (包装类)

常用类 – 包装类 基本数据类型的包装类 理解&#xff1a;包装类是8种基本数据类型对应的类 出现原因&#xff1a;Java是一种纯面向对象语言&#xff0c;但是java中有8种基本数据类型&#xff0c;破坏了java为纯面向对象的特征。为了承诺在java中一切皆对象&#xff0c;java…

【Hive SQL 每日一题】分析电商平台的用户行为和订单数据

需求描述 假设你是一位数据分析师&#xff0c;负责分析某电商平台的用户行为和订单数据&#xff0c;平台上有多个用户&#xff0c;用户可以在不同的日期下单&#xff0c;每个订单包含多个商品。请你完成相关业务分析&#xff0c;帮助平台优化运营策略和用户体验。 数据准备 …

音视频-常用的分析工具介绍-连续补充

目录 1&#xff1a;Audacity 2&#xff1a;MediaInfo 3&#xff1a;MP4Box 4&#xff1a;hexinator 5&#xff1a;Adobe Audition 6&#xff1a;VideoEye 7&#xff1a;YUVplayer &#xff08;YUV/RGB播放器&#xff09; 在做音视频分析时&#xff0c;经常用到各种分析工…

Sentinel的隔离和降级

文章目录 1、概念简介2、FeignClient整合Sentinel2.1、修改配置&#xff0c;开启sentinel功能2.2、编写失败降级逻辑2.3、总结 3、线程隔离&#xff08;舱壁模式&#xff09;3.1、线程隔离的实现方式3.2、sentinel的线程隔离1&#xff09;配置隔离规则2&#xff09;Jmeter测试 …

flink程序本地运行报: A JNI error has occurred和java.lang.NoClassDefFoundError

1.问题描述 在idea中运行flink job程序出现如下错误&#xff1a; Error: A JNI error has occurred, please check your installation and try again Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/flink/api/common/io/FileInputFormat …

Excel模板计算得出表格看板

背景 表格看板及导出&#xff0c;单元格时间年是根据筛选器时间变化的 较往年和往年是计算单元格 思路 1.通过excel模板来把数据填入excel再数据清洗得到数据返回前端 2.数据填充&#xff0c;通过行列作为key 列如&#xff1a;key整体20241月&#xff0c;根据key匹配数据填…

资料防拷贝该如何实现?数据防拷贝的方法有哪些

数据安全和隐私保护成为企业和个人关注的重点。电脑中存储的资料往往包含了重要的商业机密、个人隐私或其他敏感信息。 因此&#xff0c;如何有效防止他人非法拷贝电脑资料&#xff0c;成为了一个亟待解决的问题。 本文将探讨数据防拷贝的方法&#xff0c;以帮助企业和个人保护…

linux查看硬盘信息

1、查看挂接的分区状态 [rootMaster ~]# fdisk -l |grep Disk 2、查看硬盘和分区分布 [rootMaster ~]# lsblk 3、查看硬盘和分区的详细信息 [rootMaster ~]# fdisk -l 4、查看挂接的分区状态 [rootMaster ~]# swapon -s 5、查看硬盘使用情况 [rootMaster ~]# df -hT 6、硬…

Mysql总结1

Mysql常见日志 &#xff08;1&#xff09;错误日志&#xff1a;记录数据库服务器启动、停止、运行时存在的问题&#xff1b; &#xff08;2&#xff09;慢查询日志&#xff1a;记录查询时间超过long_query_time的sql语句&#xff0c;其中long_query_time可配置&#xff0c;且…

docker所在磁盘空间不足 迁移数据

1.查看原始目录docker info | grep "Docker Root Dir" 一般在/var/lib/docker 2.停止docker service docekr stop 3.移动数据 注意 移动前不要创建docker目录&#xff01; mv /var/lib/docker /home/docker 4.进入目录查看是否与原始目录相同&#xff0c;确认一…

精准键位提示,键盘盲打轻松入门

在说明精准键位提示之前&#xff0c;我们先来看一张图&#xff1a; 这是一张标准的基准键位图&#xff0c;也就是打字时我们双手的8个手指放在基准键位上&#xff0c;在打不同的字母时&#xff0c;我们的手指以基准键位为中心&#xff0c;或上、或下、或左、或右&#xff0c;在…

全域运营是本地生活的下半场?新的创业风口来了?

随着全域概念的兴起&#xff0c;全域运营赛道也逐渐进入人们的视野之中&#xff0c;甚至有业内人士预测&#xff0c;全域运营将会是本地生活下半场的大趋势。 之所以这么说&#xff0c;是因为全域运营作为包含了公域和私域内所有运营业务的新模式&#xff0c;不仅能同时做所有本…

楼道堆积物视觉识别监控系统

楼道堆积物视觉识别监控系统采用了AI神经网络和深度学习算法&#xff0c;楼道堆积物视觉识别监控系统通过摄像头实时监测楼道的情况&#xff0c;通过图像处理、物体识别和目标跟踪算法&#xff0c;系统能够精确地识别楼道通道是否被堆积物阻塞。楼道堆积物视觉识别监控系统检测…

RA-RISK ANALYSIS

文章目录 一、期刊简介二、征稿信息三、期刊表现四、投稿须知五、咨询 一、期刊简介 Risk Analysis代表风险分析学会出版&#xff0c;在ISI期刊引文报告中的社会科学、数学方法类别中排名前10位&#xff0c;为风险分析领域的新发展提供了焦点。这本国际同行评审期刊致力于发表…

面试准备【面试准备】

面试准备【面试准备】 前言面试准备自我介绍&#xff1a;项目介绍&#xff1a; 论坛项目功能总结数据库表设计注册功能登录功能显示登录信息功能发布帖子评论私信点赞功能关注功能通知搜索网站数据统计热帖排行缓存 论坛项目技术总结Http的无状态cookie和session的区别为什么要…

Python TCP编程简单实例

客户端&#xff1a;创建TCP链接时&#xff0c;主动发起连接的叫做客户端 服务端&#xff1a;接收客户端的连接 连接其他服务器 可以通过tcp连接其他服务器。 示例&#xff1a; import socket# 1.创建一个socket # 参数1&#xff1a;指定协议 AF_INET&#xff08;ipv4&#…

TSMaster发送CAN报文

打开TSMaster工程 从菜单栏打开CAN报文发送窗口&#xff1a;【分析】->【报文发送】->【添加CAN/CAN FD发送】 可以选择【从CAN数据库添加报文】或者是【添加新的原始报文】 方法一 添加新的原始报文 可以配置报文发送的触发方式&#xff0c;有【手动】和【周期】两种。…

Linux多线程系列三: 生产者消费者模型,信号量使用,基于阻塞队列和环形队列的这两种生产者消费者代码的实现

Linux多线程系列三: 生产者消费者模型,信号量,基于阻塞队列和环形队列的这两种生产者消费者代码的实现 一.生产者消费者模型的理论1.现实生活中的生产者消费者模型2.多线程当中的生产者消费者模型3.理论 二.基于阻塞队列的生产者消费者模型的基础代码1.阻塞队列的介绍2.大致框架…

力扣刷题--2733. 既不是最小值也不是最大值【简单】

题目描述 给你一个整数数组 nums &#xff0c;数组由 不同正整数 组成&#xff0c;请你找出并返回数组中 任一 既不是 最小值 也不是 最大值 的数字&#xff0c;如果不存在这样的数字&#xff0c;返回 -1 返回所选整数。 示例 1&#xff1a; 输入&#xff1a;nums [3,2,1,…