从零开始实现一个 GoWeb MVC 框架:让你更深入地理解 MVC 架构设计

1、什么是 MVC

M 即 Model 模型是指模型表示业务规则。在MVC的三个部件中,模型拥有最多的处理任务。被模型返回的数据是中立的,模型与数据格式无关,这样一个模型能为多个视图提供数据,由于应用于模型的代码只需写一次就可以被多个视图重用,所以减少了代码的重复性。

V 即 View 视图是指用户看到并与之交互的界面。比如由html元素组成的网页界面,或者软件的客户端界面。MVC的好处之一在于它能为应用程序处理很多不同的视图。在视图中其实没有真正的处理发生,它只是作为一种输出数据并允许用户操作的方式。

C 即 Controller 控制器是指控制器接受用户的输入并调用模型和视图去完成用户的需求,控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示返回的数据。

2、Iris 简介

Iris 是使用 Go 语言开发的一个 Web 开发框架,特点有 飞快 (fast)、简单 (simple)、效率 (efficient)、小巧 (micro),该框架用起来比较顺手,虽然也存在诸多的问题,但瑕不掩瑜。

Iris 对 MVC (Model View Controller) 模式有着非常好的支持,这是其它 go 语言框架所不具备。

Iris Web 框架支持以最快的方式执行,请求数据、模型、持久性数据和绑定。

想深入了解和学习的可以参考:Iris 框架中文文档

3、项目目录

首先分享一个项目目录,先了解每个文件夹干什么
在这里插入图片描述
接下来创建一个 go 项目,示例:go-iris 并按上图目录创建所需文件夹。注意:项目目录也可以按照自己习惯创建并不是唯一

最终项目结构如下图所示

在这里插入图片描述

4、编写代码

注意:conf 和 app/model 目录此项目可忽略,暂时没用上

4.1、安装软件包

在 VS Code 终端输入以下命令并执行

# 1、下载并安装 Iris,目前此项目只需要安装这一个
go get -u github.com/kataras/iris/v12@master

4.2、编写代码

工欲善其事,必先利其器,我们从工具包开始

4.2.1、通用响应结构

在 utils/common 目录新建文件 response.go,完整代码如下:

package common

import "strconv"

/**
 * <h1>通用响应结构体定义</h1>
 * Created by woniu
 */
type CommonResponse struct {
    
    
	Code    int         `json:"code"`    // 响应状态:0成功 其他失败
	Message string      `json:"message"` // 响应消息
	Data    interface{
    
    } `json:"data"`    // 响应数据
}

/*
* <h2>响应成功</h2>
* @param data 响应数据
**/
func ResponseSuccess(data interface{
    
    }) CommonResponse {
    
    
	return CommonResponse{
    
    
		Code:    0,
		Message: "操作成功",
		Data:    data,
	}
}

/*
* <h2>响应失败</h2>
* @param code 响应异常状态
* @param message 响应异常消息
**/
func ResponseError(code int, message string) CommonResponse {
    
    
	return CommonResponse{
    
    
		Code:    code,
		Message: message,
		Data:    nil,
	}
}

/*
* <h2>响应失败</h2>
* @param code 响应异常状态
**/
func ResponseErrorCode(code int) CommonResponse {
    
    
	return CommonResponse{
    
    
		Code:    code,
		Message: "操作失败",
		Data:    nil,
	}
}

/*
* <h2>响应失败</h2>
* @param message 响应异常消息
* 可以传入 0000_XXXXXXX 字符串,自动转换
**/
func ResponseErrorMessage(message string) CommonResponse {
    
    

	// 异常消息长度大于4
	if len(message) > 4 {
    
    
		// 截取异常消息前四位
		var codeStr = message[0:4]
		// 前四位字符串类型转 int 类型
		code, err := strconv.Atoi(codeStr)
		// 如果没有异常,自动分割,示例:0000_XXXXXXX
		if err == nil {
    
    
			return CommonResponse{
    
    
				Code:    code,
				Message: message[5:],
				Data:    nil,
			}
		}
	}

	return CommonResponse{
    
    
		Code:    -1,
		Message: message,
		Data:    nil,
	}
}

4.2.2、业务异常自定义

在 utils/constant 目录新建文件 errors.go,完整代码如下:

package constant

import "errors"

/**
 * <h1>业务异常自定义</h1>
 * Created by woniu
 */

var (
	ResErrSysUserPasswordErr = errors.New("1003_密码错误")
	ResErrSysUserIsNil       = errors.New("1004_用户不存在")
)

4.2.3、用户登录功能

简单实现用户登录请求并正确响应,用户账号:admin,用户密码:123456

4.2.3.1、用户登录请求结构

在 app/dto 目录新建文件 login_dto.go,完整代码如下:

package dto

// 用户登录
type LoginDto struct {
    
    
	Account      string // 用户账号
	Password     string // 用户密码
}

4.2.3.2、系统用户响应结构

在 app/vo 目录新建文件 sys_user_vo.go,完整代码如下:

package vo

// 系统用户信息
type SysUser struct {
    
    
	Account string `json:"account"` // 用户账号
	Name    string `json:"name"`    // 用户姓名
	Age     int    `json:"age"`     // 用户年龄
}

4.2.3.3、系统用户业务处理

在 app/service 目录新建文件 sys_user_service.go,完整代码如下:

package service

import (
	"go-iris/app/dto"
	"go-iris/app/vo"
	"go-iris/utils/constant"
)

/**
 * <h1>系统用户业务处理</h1>
 * Created by woniu
 */
var SysUser = new(sysUserService)

type sysUserService struct{
    
    }

/**
 * <h2>用户名密码登录</h2>
 */
func (s *sysUserService) PasswordLogin(requestModel dto.LoginDto) (*vo.SysUser, error) {
    
    
	// 用户账号必须是 admin
	if requestModel.Account != "admin" {
    
    
		return nil, constant.ResErrSysUserIsNil
	}

	// 用户密码必须是 123456
	if requestModel.Password != "123456" {
    
    
		return nil, constant.ResErrSysUserPasswordErr
	}

	// 用户名 + 密码 校验正确,返回用户信息
	var sysUser = vo.SysUser{
    
    
		Account: "admin",
		Name:    "小明",
		Age:     18,
	}

	return &sysUser, nil
}

4.2.3.4、系统用户控制器

在 app/controller 目录新建文件 sys_user_controller.go,完整代码如下:

package controller

import (
	"go-iris/app/dto"
	"go-iris/app/service"
	"go-iris/utils/common"

	"github.com/kataras/iris/v12"
)

/**
 * <h1>系统用户控制器</h1>
 * Created by woniu
 */
var SysUser = new(SysUserController)

type SysUserController struct{
    
    }

/**
 * <h2>用户名密码登录</h2>
 */
func (sc *SysUserController) PasswordLogin(ctx iris.Context) {
    
    

	// 登录参数
	var requestModel dto.LoginDto

	// 参数绑定
	if err := ctx.ReadForm(&requestModel); err != nil {
    
    
		ctx.JSON(common.ResponseError(-1, "传参异常"))
		return
	}

	// 用户登录
	sysUser, err := service.SysUser.PasswordLogin(requestModel)

	if err != nil {
    
    
		// 响应失败
		ctx.JSON(common.ResponseErrorMessage(err.Error()))
		return
	}

	// 响应成功
	ctx.JSON(common.ResponseSuccess(sysUser))
}

4.2.4、通用响应测试

在 app/controller 目录新建文件 error_controller.go,完整代码如下:

package controller

import (
	"go-iris/utils/common"

	"github.com/kataras/iris/v12"
)

/**
 * <h1>通用响应测试控制器</h1>
 * Created by woniu
 */
var Error = new(ErrorController)

type ErrorController struct{
    
    }

/**
 * <h2>响应失败</h2>
 */
func (sc *ErrorController) ResponseError(ctx iris.Context) {
    
    
	// 响应失败
	ctx.JSON(common.ResponseError(9001, "自定义响应状态和异常消息"))
}

/**
 * <h2>响应失败</h2>
 */
func (sc *ErrorController) ResponseErrorCode(ctx iris.Context) {
    
    
	// 响应失败
	ctx.JSON(common.ResponseErrorCode(9002))
}

/**
 * <h2>响应失败</h2>
 */
func (sc *ErrorController) ResponseErrorMessage(ctx iris.Context) {
    
    
	// 响应失败
	ctx.JSON(common.ResponseErrorMessage("自定义响应异常消息"))
}

4.2.5、请求路由

在 router 目录新建文件 router.go,完整代码如下:

package router

import (

	// 自己业务 controller 路径
	"go-iris/app/controller"

	"github.com/kataras/iris/v12"
)

/**
 * <h2>注册路由</h2>
 */
func RegisterRouter(app *iris.Application) {
    
    

	// 系统用户
	login := app.Party("/sysUser")
	{
    
    
		// 用户名 + 密码登录
		login.Post("/passwordLogin", controller.SysUser.PasswordLogin)
	}

	// 通用异常
	err := app.Party("/error")
	{
    
    
		err.Get("/responseError", controller.Error.ResponseError)
		err.Get("/responseErrorCode", controller.Error.ResponseErrorCode)
		err.Get("/responseErrorMessage", controller.Error.ResponseErrorMessage)
	}
}

4.2.6、自定义请求 404 响应页面

在 app/views/html 目录新建文件 404.html,完整代码如下:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>GO - IRIS</title>
</head>
<body>
    <h1 align="center">404</h1>
</body>
</html>

4.2.7、应用程序启动

在项目根目录新建文件 main.go,完整代码如下:

package main

import (
	"go-iris/router"

	"github.com/kataras/iris/v12"
	"github.com/kataras/iris/v12/middleware/logger"
	"github.com/kataras/iris/v12/middleware/recover"
)

/**
 * <h1>应用程序启动</h1>
 * Created by woniu
 */
func main() {
    
    
	// 创建 Iris
	app := iris.New()

	// 设置调试模式
	app.Logger().SetLevel("debug")
	// 可选项添加两个内置的句柄(handlers)
	// 捕获相对于http产生的异常行为
	app.Use(recover.New())
	// 记录请求日志
	app.Use(logger.New())

	// 路由注册
	router.RegisterRouter(app)

	// 加载视图模板地址
	app.RegisterView(iris.HTML("app/views", ".html"))

	// 请求异常
	app.OnErrorCode(iris.StatusNotFound, notFound)

	// 监听并启动 8080 端口
	app.Run(iris.Addr(":8080"))
}

/**
 * <h1>请求地址不存在,跳转到 404 页面</h1>
 */
func notFound(ctx iris.Context) {
    
    
	// 出现 404 的时候,就跳转到 $views_dir/html/404.html 模板
	ctx.View("html/404.html")
}

相关代码已完成编写,最终项目结构及文件如下图

在这里插入图片描述

5、启动并测试

5.1、启动项目

在 VS Code 终端输入以下命令并执行

# 启动项目
go run main.go

有以下信息代表启动成功

Iris Version: 12.2.0-beta6

Now listening on: http://localhost:8080
Application started. Press CTRL+C to shut down.

5.2、测试接口

5.2.1、用户登录

account = admin1,用户不存在

在这里插入图片描述
password = 111111,密码错误

在这里插入图片描述
account = admin 并且 password = 123456,响应成功

在这里插入图片描述

5.2.2、通用响应异常测试

自定义响应状态和异常消息

在这里插入图片描述

自定义响应状态

在这里插入图片描述

自定义响应异常消息

在这里插入图片描述

5.2.3、请求地址不存在

例如:http://localhost:8080/woniu

在这里插入图片描述

通过上述测试验证,项目达到预期目标,小伙伴自己赶紧动手试试吧。

6、每日一记

6.1、首字母大小写控制访问权限

Go语言通过首字母的大小写来控制访问权限。无论是方法,变量,常量或是自定义的变量类型,如果首字母大写,则可以被外部包访问,反之则不可以。

而结构体中的字段名,如果首字母小写的话,则该字段无法被外部包访问和解析,比如,json解析。

6.2、Json 解析首字母大写问题

如果系统用户信息结构如下:

// 系统用户信息
type SysUser struct {
    
    
	Account string // 用户账号
	Name    string // 用户姓名
	Age     int    // 用户年龄
}

请求响应结果

{
    
    
    "code": 0,
    "message": "操作成功",
    "data": {
    
    
        "Account": "admin",
        "Name": "小明",
        "Age": 18
    }
}

如果希望,Json 化之后的属性名是小写字母的,可以使用 struct tag。如下:

// 系统用户信息
type SysUser struct {
    
    
	Account string `json:"account"` // 用户账号
	Name    string `json:"name"`    // 用户姓名
	Age     int    `json:"age"`     // 用户年龄
}

本文教程到此结束,有问题欢迎大家讨论。

实践是检验真理的唯一标准,一键送三连关注不迷路。

猜你喜欢

转载自blog.csdn.net/u011374856/article/details/127982721