往往,我们的博客并不会包含所有的路由,也就是说,我们只有特定的几个路由,如果用户随便输入了一个路径地址,我们没有控制器来处理,那么程序就会抛出一个404,为了美观,也样式的统一,我们一般需要自定义404页面。这时候,我们就需要从路由中拦截404,然后重新定义404页面了。

404错误页面的拦截和重定义

我们先在controller文件夹建立一个common.go文件,用来放一些公共的处理函数,如404的控制器,500错误的控制器等。

404控制器

package controller

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

func NotFound(ctx iris.Context) {
	ctx.View("errors/404.html")
}

然后在template文件夹中在创建一个errors文件夹,在errors文件夹中,创建一个404.html:

404.html

{% include "partial/header.html" %}
<div class="ErrorPage-container">
    <div class="ErrorPage-text">
        <h1 class="ErrorPage-title">404h1>
        <p class="ErrorPage-subtitle">你似乎来到了没有知识存在的荒原p>
        <a class="button-primary" href="/">
            去往首页
        a>
    div>
div>
{% include "partial/footer.html" %}

上一节我们提到了模板片段,使用include引入,正好我们可以在这里用上。我们这个时候,可以将404页面的头部和尾部抽离到公共的地方去,在template文件夹中,建立一个partial文件夹,用来放代码片段,在partial文件夹中,建立一个header.html和footer.html

header.html

html>
<html>
<head>
    <meta charSet="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, viewport-fit=cover" />
    <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1"/>
    <meta name="applicable-device" content="mobile">
    <title>{% if webInfo.Title %}{{webInfo.Title}} - {% endif %}{{SiteName}}title>
    <link href="https://www.layuicdn.com/layui/css/layui.css" rel="stylesheet">
    <link href="/static/css/app.css" rel="stylesheet">
head>
<body>
<div class="layui-header">
    <div class="layui-container">
        <a href="/"><div class="layui-logo">{{SiteName}}div>a>
        <ul class="layui-nav layui-layout-left nav-list">
            <li class="layui-nav-item{% if !webInfo.NavBar %} layui-this{% endif %}"><a href="/">博客首页a>li>
            {% for item in categories %}
                <li class="layui-nav-item{% if webInfo.NavBar == item.Id %} layui-this{% endif %}"><a href="/?category_id={{item.Id}}">{{item.Title}}a>li>
            {% endfor %}
        ul>
        <ul class="layui-nav layui-layout-right">
            {% if hasLogin %}
            <li class="layui-nav-item"><a href="/article/publish">发布文章a>li>
            <li class="layui-nav-item"><a href="/admin/logout">退出a>li>
            {% else %}
                <li class="layui-nav-item"><a href="/admin/login">登录a>li>
            {% endif %}
        ul>
    div>
div>

header.html 代码片段中,我们统一引入https://www.layuicdn.com/layui/css/layui.css,layui是一个经典的前端框架,它已经给我们定义了很多常用的样式,我们借助它,可以省去很多前端样式的编写工作。

我们将自定义的控制页面样式文件,放到/static/css/app.css文件中,static目录为网站的根目录,网站的根目录为public目录。以后我们不单独说明css文件,凡是涉及到html的样式控制,我们都将css卸载app.css中。

因此,我们还需要将public目录在路由中,注册成为网站根目录,打开route/base.go,添加一行代码:

app.HandleDir("/", fmt.Sprintf("%spublic", config.ExecPath))

这样子,我们就可以通过浏览器访问到我们存放在public目录下的具体静态文件了。比如:http://127.0.0.1:8001/static/css/app.css访问到的就是我们上面定义的自定义css文件。

这里我们顺便说一下,public根目录下,我们定义static为静态文件目录,里面有三个文件夹,一个是css文件夹,用来存放css文件,一个是js文件夹,用来存放js文件,一个是images文件夹,用来存放博客网站项目用到的图片文件。

public目录下,我们还定义了uploads文件夹,用来存放通过前端上传的图片等文件。这个文件夹在git中是忽略的,因为它是从服务端上变化的文件夹。

footer.html

<div class="layui-trans layui-footer">
    <div class="layui-container">
        <div class="layout">irisweb 网站系统div>
        <p>©2020 Fesion. All Rights Reservedp>
    div>
div>
body>
<script src="https://www.layuicdn.com/layui/layui.js">script>
<script src="/static/js/app.js">script>
html>

我们将js文件放到footer.html 页脚代码片段中来。把js文件放在页脚加载会阻塞页面加载,如果js加载过慢,会导致页面长时间白屏,或者长时间无法晃动鼠标等假死状态。因此我们将js放在页面底部,让页面的dom加载完毕了再执行js操作。

这里我们引入了https://www.layuicdn.com/layui/layui.jslayui的js组件,它是layui前端框架必须的文件。layui 兼容人类正在使用的全部浏览器(IE6/7除外),可作为 PC 端后台系统与前台界面的速成开发方案。

我们将自定义的js文件,存放在/static/js/app.js中,因为需要使用layui框架以及框架的组件,我们还需要用js来引入用到的组件。app.js的代码初步为:

layui.use(['element', 'layedit', 'form', 'layer'], function(){
    var $ = layui.$;
    var element = layui.element;
    var layedit = layui.layedit;
    var form = layui.form;
    var layer = layui.layer;
    var editorIndex = null;
});

我们后面将用到elementlayeditformlayer这几个组件因此我们在这里先把他们注册了,后面就可以直接使用了。

注册404错误

我们再次打开route.go,在Register()函数中,添加上app.OnErrorCode(iris.StatusNotFound, controller.NotFound)

package route

import (
	"github.com/kataras/iris/v12"
	"irisweb/controller"
)

func Register(app *iris.Application) {
	app.OnErrorCode(iris.StatusNotFound, controller.NotFound)
	app.Get("/", controller.IndexPage)
}

这样子,我们就注册了404了,现在我们只有一个首页的路由,我们在浏览器访问其他任意路由,它都会输出我们定义的404错误页面了。

500错误输出

500错误,是程序内部错误,比如模板解析异常等,它就会抛出500错误,这时候,我们也要把它拦截下来,输出一个自定义的500错误信息给用户。

500控制器

我们在common.go 中个添加如下函数:

func InternalServerError(ctx iris.Context) {
	errMessage := ctx.Values().GetString("error")
	if errMessage == "" {
		errMessage = "(Unexpected) internal server error"
	}
	ctx.ViewData("errMessage", errMessage)
	ctx.View("errors/500.html")
}

ctx.Values() 是一个很有用的东西,主要用来使 处理方法与中间件 通信 记住真的很重要。ctx.Values().GetString("error") 获取自定义错误提示信息。

500.html

我们在errors文件夹下添加500.html文件:

{% include "partial/header.html" %}
<div class="ErrorPage-container">
    <div class="ErrorPage-text">
        <h1 class="ErrorPage-title">500h1>
        <p class="ErrorPage-subtitle">系统给程序员提了一个bug,程序员正在修复中:{{errMessage}}p>
        <a class="button-primary" href="/">
            去往首页
        a>
    div>
div>
{% include "partial/footer.html" %}

注册500错误

我在打开route.go,在Register()函数中,添加上app.OnErrorCode(iris.StatusInternalServerError, controller.InternalServerError)

package route

import (
	"github.com/kataras/iris/v12"
	"irisweb/controller"
)

func Register(app *iris.Application) {
	app.OnErrorCode(iris.StatusNotFound, controller.NotFound)
	app.OnErrorCode(iris.StatusInternalServerError, controller.InternalServerError)
	app.Get("/", controller.IndexPage)
}

这样子,我们就注册了500了,如果我们的模板有错误,它就会显示我们定义的500页面了。

完整的项目示例代码托管在GitHub上,需要查看完整的项目代码可以到github.com/fesiong/goblog 上查看,也可以直接fork一份来在上面做修改。