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

重构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上

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ubuntu24.04LVM扩容问题

目录 一、 开机前设置&#xff1a;扩展 二、 开机后设置&#xff1a;分区管理 通过gparted管理分区有效做法。 一、 开机前设置&#xff1a;扩展 虚拟机关机。打开虚拟机设置。 挂起状态是不能扩容的 这里选择扩容到40G 二、 开机后设置&#xff1a;分区管理 使用gpar…

基于Matlab的车道线检测系统 (文末有代码获取链接)【含Matlab源码 MX_001期】

运行环境&#xff1a;Matlab2014b 部分代码&#xff1a; %% 视频流循环处理 % 创建一个循环过程来对给定视频进行车道线检测 % 该循环使用之前初始化的系统对象 warningTextColors {[1 0 0], [1 0 0], [0 0 0], [0 0 0]}; while ~isDone(hVideoSrc) RGB step(hVideoSrc);% …

Java入门基础学习笔记43——包

什么是包&#xff1f; 包是用来分门别类的管理各种不同程序的&#xff0c;类似文件夹&#xff0c;建包有利于程序的管理和维护。 建包的语法规则&#xff1a; package cn.ensource.javabean;public class Car() {} 在自己的程序中调用其他包下的程序的注意事项&#xff1a; 1…

Web3探索加密世界:空投常见类型有哪些?附操作教程

每种空投类型都有独特的特征和目的&#xff0c;我们需要了解不同类型的加密空投。本文给大家介绍的是流行的加密货币空投类型&#xff0c;以及一般空投是如何做的。感兴趣的话请看下去。 一、空投常见类型 1、持有者空投 持有者空投向钱包中持有一定数量数字货币的人免费发放…

探索Python的包与模块:构建项目的基石

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、模块与包的基础认知 1. 模块的定义与创建 2. 包的组织与管理 二、模块与包的进阶使用…

【测评】香橙派 AIpro上手初体验

AI毋庸置疑是近年来&#xff0c;热度最高的技术之一&#xff0c;作为一名工程师拥抱新技术的同时不可或缺的需要一块强悍的开发板&#xff0c;香橙派 AIpro除了拥有好看的皮囊之外&#xff0c;还拥有一个有趣且充满魅力的灵魂。作为一位长期活跃在嵌入式开发领域的工程师&#…

SQL刷题笔记day5

SQL218题目 我的错误代码&#xff1a; select de.dept_no,de.emp_no,s.salary from employees e join dept_emp de on de.emp_no e.emp_no join salaries s on s.emp_no e.emp_no where de.dept_no not in dept_manager.dept_no #not in 好像不能直接这样用 这里报错 正确代…

在树莓派3B+中下载opencv(遇到的各种问题及解决)

目录 前言 1、删除原版本下新版本 2、python虚拟环境 3、python版本共存换链接——给版本降低 4、烧录之前版本的文件&#xff08;在清华源中可以找&#xff0c;不用官网的烧录文件就行&#xff1b; 比如&#xff1a;&#xff08;balenaEtcher&#xff09;重新烧录有问题…

面试二十六、c++语言级别的多线程编程

一、 多线程编程 ​​​​​ 这里的c语言级别的多线程和linux的有一定的区别&#xff0c;c语言级别提供的多线程比较严格&#xff0c;如果主线程结束了&#xff0c;但是子线程没有结束&#xff0c;进程就会异常终止&#xff0c;而linux不会&#xff0c;会继续执行。 二、模拟卖…

三十、openlayers官网示例解析Double click, Drag and Zoom——第二次点击鼠标拖拽缩放地图效果、取消地图双击放大事件

这篇展示了如何在地图上添加第二次按下鼠标移动鼠标实现拖拽缩放地图效果。 官网demo地址&#xff1a; Double click, Drag and Zoom 官网介绍文字的翻译如下&#xff1a; 示例比较简单&#xff0c;直接贴代码&#xff1a; const map new Map({//添加第二次点击拖拽缩放地图i…

es安装错误Exception in thread “main“ java.nio.file.NoSuchFileException解决方案

docker 启动es出现一下错误的解决方案 Exception in thread “main” java.nio.file.NoSuchFileException: /usr/share/elasticsearch/config/jvm.options Exception in thread "main" java.nio.file.NoSuchFileException: /usr/share/elasticsearch/config/jvm.op…

React@16.x(11)ref

目录 1&#xff0c;介绍1.1&#xff0c;得到的结果 2&#xff0c;参数类型2.1&#xff0c;字符串&#xff08;不再推荐&#xff09;2.2&#xff0c;对象2.3&#xff0c;函数函数调用时机 3&#xff0c;注意点 1&#xff0c;介绍 reference 引用。和 vue 中的 refs 类似&#x…

装机必备——360压缩安装教程

装机必备——360压缩安装教程 软件下载 软件名称&#xff1a;360压缩 软件语言&#xff1a;简体中文 软件大小&#xff1a;3.38M 系统要求&#xff1a;Windows7或更高&#xff0c; 32/64位操作系统 硬件要求&#xff1a;CPU2GHz &#xff0c;RAM4G或更高 下载通道①迅雷云盘丨…

python自动化-自动化网络配置工具v2(可巡检,可批量配置)

在日常工作中遇到需要配置相同配置的场景&#xff0c;网络工程师一个个去登陆配置会让工作效率显得没那么高效。 但是随着科技发展&#xff0c;人们不断的学习&#xff0c;我们似乎可以使用一些软件或者脚本来帮助我们实现巡检任务或者配置任务。 今天我想给大家分享一款我自己…

PCIe协议之-DLLP详解

✨前言&#xff1a; &#x1f31f;数据链路层的功能 数据链路层将从物理层中获得报文&#xff0c; 并将其传递给事务层&#xff1b; 同时接收事务层的报文&#xff0c; 并将其转发到物理层; 核心的功能有以下三点 1.保证TLP在 PCIe 链路中的正确传递; 2.数据链路层使用了容错…

YOLOv10:实时端到端目标检测

Ao Wang Hui Chen∗  Lihao Liu Kai Chen Zijia Lin  Jungong Han Guiguang Ding Tsinghua University Corresponding Author. 文献来源&#xff1a;中英文对照阅读 摘要 在过去的几年里&#xff0c;YOLO 因其在计算成本和检测性能之间的有效平衡而成为实时目标检测领…

GitLab的安装及基础操作

1. 项目目标 &#xff08;1&#xff09;熟练使用rpm包安装gitlab &#xff08;2&#xff09;熟练配置gitlab &#xff08;3&#xff09;熟练创建gitlab群组、成员、项目 &#xff08;4&#xff09;熟练使用gitlab推送和拉取代码 2. 项目准备 2.1. 规划节点 主机名 主机I…

景源畅信电商:做抖音运营怎么开始第一步?

在数字化时代的浪潮中&#xff0c;抖音作为一款短视频平台迅速崛起&#xff0c;成为许多人表达自我、分享生活的重要舞台。随着用户量的激增&#xff0c;如何做好抖音运营&#xff0c;尤其是迈出成功的第一步&#xff0c;成为了众多内容创作者和品牌主们关注的焦点。接下来&…

鹏哥C语言复习——调试

目录 什么是调试&#xff1f; Debug和Release&#xff1a; 调试方法&#xff1a; 环境准备&#xff1a; 调试快捷键介绍&#xff1a; 调试快捷键注意事项&#xff1a; 监视与内存查看&#xff1a; 数组元素的监视&#xff1a; 编译常见错误归类&#xff1a; 编译型错…

基于SpringBoot的旅游管理系统

基于SpringBoot的旅游管理系统 旅游管理系统开发技术功能模块代码结构数据库设计运行截图源码获取 旅游管理系统 开发技术 技术&#xff1a;SpringBoot、MyBatis-Plus、MySQL、Beetl、Layui。 框架&#xff1a;基于开源框架Snowy-Layui开发。 工具&#xff1a;IDEA、Navicat等…