《Go Web编程》之ChitChat论坛(文末赠书)

简介:

本文主要内容

  • 使用Go进行Web编程的方法

  • 设计一个典型的Go Web应用

  • 编写一个完整的Go Web应用

  • 了解Go Web应用的各个组成部分

本文我们将会构建一个简单的网上论坛Web应用,这个应用同样非常基础,但是却有用得多:它允许用户登录到论坛里面,然后在论坛上发布新帖子,又或者回复其他用户发表的帖子。在阅读完这一文之后,你将进一步地了解到使用Go进行Web应用开发的相关方法。

ChitChat简介

网上论坛无处不在,它们是互联网上最受欢迎的应用之一,与旧式的电子公告栏(BBS)、新闻组(Usenet)和电子邮件一脉相承。雅虎公司和Google公司的群组(Groups)都非常流行,雅虎报告称,他们总共拥有1000万个群组以及1.15亿个群组成员,其中每个群组都拥有一个自己的论坛;而全球最具人气的网上论坛之一——Gaia在线——则拥有2300万注册用户以及接近230亿张帖子,并且这些帖子的数量还在以每天上百万张的速度持续增长。尽管现在出现了诸如Facebook这样的社交网站,但论坛仍然是人们在网上进行交流时最为常用的手段之一。作为例子,图1展示了GoogleGroups的样子。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

图1 一个网上论坛示例:GoogleGroups里面的Go编程语言论坛

从本质上来说,网上论坛就相当于一个任何人都可以通过发帖来进行对话的公告板,公告板上面可以包含已注册用户以及未注册的匿名用户。论坛上的对话称为帖子(thread),一个帖子通常包含了作者想要讨论的一个主题,而其他用户则可以通过回复这个帖子来参与对话。比较复杂的论坛一般都会按层级进行划分,在这些论坛里面,可能会有多个讨论特定类型主题的子论坛存在。大多数论坛都会由一个或多个拥有特殊权限的用户进行管理,这些拥有特殊权限的用户被称为版主(moderator)。

在本文中,我们将会开发一个名为ChitChat的简易网上论坛。为了让这个例子保持简单,我们只会为ChitChat实现网上论坛的关键特性:在这个论坛里面,用户可以注册账号,并在登录之后发表新帖子又或者回复已有的帖子;未注册用户可以查看帖子,但是无法发表帖子或是回复帖子。现在,让我们首先来思考一下如何设计ChitChat这个应用。

应用设计

Web应用的一般工作流程是客户端向服务器发送请求,然后服务器对客户端进行响应(如图2所示),ChitChat应用的设计也遵循这一流程。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

图2 Web应用的一般工作流程,客户端向服务器发送请求,然后等待接收响应

ChitChat的应用逻辑会被编码到服务器里面。服务器会向客户端提供HTML页面,并通过页面的超链接向客户端表明请求的格式以及被请求的数据,而客户端则会在发送请求时向服务器提供相应的数据,如图3所示。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

图3 HTTP请求的URL格式

请求的格式通常是由应用自行决定的,比如,ChitChat的请求使用的是以下格式:http://<服务器名><处理器名>?<参数>

服务器名(server name)是ChitChat服务器的名字,而处理器名(handler name)则是被调用的处理器的名字。处理器的名字是按层级进行划分的:位于名字最开头是被调用模块的名字,而之后跟着的则是被调用子模块的名字,以此类推,位于处理器名字最末尾的则是子模块中负责处理请求的处理器。比如,对/thread/read这个处理器名字来说,thread是被调用的模块,而read则是这个模块中负责读取帖子内容的处理器。

该应用的参数(parameter)会以URL查询的形式传递给处理器,而处理器则会根据这些参数对请求进行处理。比如说,假设客户端要向处理器传递帖子的唯一ID,那么它可以将URL的参数部分设置成id=123,其中123就是帖子的唯一ID。

如果chitchat就是ChitChat服务器的名字,那么根据上面介绍的URL格式规则,客户端发送给ChitChat服务器的URL可能会是这样的:http://chitchat/thread/read?id=123。

当请求到达服务器时,多路复用器(multiplexer)会对请求进行检查,并将请求重定向至正确的处理器进行处理。处理器在接收到多路复用器转发的请求之后,会从请求中取出相应的信息,并根据这些信息对请求进行处理。在请求处理完毕之后,处理器会将所得的数据传递给模板引擎,而模板引擎则会根据这些数据生成将要返回给客户端的HTML,整个过程如图4所示。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

图4 服务器在典型Web应用中的工作流程

数据模型

绝大多数应用都需要以某种方式与数据打交道。对ChitChat来说,它的数据将被存储到关系式数据库PostgreSQL里面,并通过SQL与之交互。

ChitChat的数据模型非常简单,只包含4种数据结构,它们分别是:

  • User——表示论坛的用户信息;

  • Session——表示论坛用户当前的登录会话;

  • Thread——表示论坛里面的帖子,每一个帖子都记录了多个论坛用户之间的对话;

  • Post——表示用户在帖子里面添加的回复。

以上这4种数据结构都会被映射到关系数据库里面,图5展示了这4种数据结构是如何与数据库交互的。

ChitChat论坛允许用户在登录之后发布新帖子或者回复已有的帖子,未登录的用户可以阅读帖子,但是不能发布新帖子或者回复帖子。为了对应用进行简化,ChitChat论坛没有设置版主这一职位,因此用户在发布新帖子或者添加新回复的时候不需要经过审核。

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=

图5 Web应用访问数据存储系统的流程

在了解了ChitChat的设计方案之后,现在可以开始考虑具体的实现代码了。在开始学习ChitChat的实现代码之前,请注意,如果你在阅读本章展示的代码时遇到困难,又或者你是刚开始学习Go语言,那么为了更好地理解本章介绍的内容,你可以考虑先花些时间阅读一本Go语言的编程入门书,比如,由William Kennedy、Brian Ketelsen和Erik St. Martin撰写的《Go语言实战》就是一个很不错的选择。

除此之外,在阅读本章时也请尽量保持耐性:本章只是从宏观的角度展示Go Web应用的样子,并没有对Web应用的细节作过多的解释,而是将这些细节留到之后的章节再进一步说明。在有需要的情况下,本章也会在介绍某种技术的同时,说明在哪一章可以找到这一技术的更多相关信息。

请求的接收与处理

请求的接收和处理是所有Web应用的核心。正如之前所说,Web应用的工作流程如下。

(1)客户端将请求发送到服务器的一个URL上。

(2)服务器的多路复用器将接收到的请求重定向到正确的处理器,然后由该处理器对请求进行处理。

(3)处理器处理请求并执行必要的动作。

(4)处理器调用模板引擎,生成相应的HTML并将其返回给客户端。

让我们先从最基本的根URL(/)来考虑Web应用是如何处理请求的:当我们在浏览器上输入地址http://localhost的时候,浏览器访问的就是应用的根URL。在接下来的几个小节里面,我们将会看到ChitChat是如何处理发送至根URL的请求的,以及它又是如何通过动态地生成HTML来对请求进行响应的。

1 多路复用器

因为编译后的二进制Go应用总是以main函数作为执行的起点,所以我们在对Go应用进行介绍的时候也总是从包含main函数的主源码文件(main source code file)开始。ChitChat应用的主源码文件为main.go,代码清单1展示了它的一个简化版本。

代码清单1 main.go文件中的main函数,函数中的代码经过了简化

 

package main
import (
"net/http"
)

func main()
{
mux := http.NewServeMux()
files := http.FileServer(http.Dir("/public"))
mux.Handle("/static/", http.StripPrefix("/static/", files))
mux.HandleFunc("/", index)
server := &http.Server{
Addr: "0.0.0.0:8080",
Handler: mux,
}
server.ListenAndServe()
}

main.go``中``首先创建了一个多路复用器,然后通过一些代码将接收到的请求重定向到处理器。net/http标准库提供了一个默认的多路复用器,这个多路复用器可以通过调用NewServeMux函数来创建:

 

mux := http.NewServeMux()

为了将发送至根URL的请求重定向到处理器,程序使用了HandleFunc函数:

 

mux.HandleFunc("/", index)

HandleFunc函数接受一个URL和一个处理器的名字作为参数,并将针对给定URL的请求转发至指定的处理器进行处理,因此对上述调用来说,当有针对根URL的请求到达时,该请求就会被重定向到名为index的处理器函数。此外,因为所有处理器都接受一个ResponseWriter和一个指向Request结构的指针作为参数,并且所有请求参数都可以通过访问Request结构得到,所以程序并不需要向处理器显式地传入任何请求参数。

需要注意的是,前面的介绍模糊了处理器以及处理器函数之间的区别:我们刚开始谈论的是处理器,而现在谈论的却是处理器函数。这是有意而为之的——尽管处理器和处理器函数提供的最终结果是一样的,但它们实际上并不相同。本书的第3章将对处理器和处理器函数之间的区别做进一步的说明,但是现在让我们暂时先忘掉这件事,继续研究ChitChat应用的代码实现。

2 服务静态文件

除负责将请求重定向到相应的处理器之外,多路复用器还需要为静态文件提供服务。为了做到这一点,程序使用FileServer函数创建了一个能够为指定目录中的静态文件服务的处理器,并将这个处理器传递给了多路复用器的Handle函数。除此之外,程序还使用StripPrefix函数去移除请求URL中的指定前缀:

 

files := http.FileServer(http.Dir("/public"))
mux.Handle("/static/", http.StripPrefix("/static/", files))

当服务器接收到一个以/static/开头的URL请求时,以上两行代码会移除URL中的/static/字符串,然后在public目录中查找被请求的文件。比如说,当服务器接收到一个针对文件http://localhost/static/css/bootstrap.min.css的请求时,它将会在public目录中查找以下文件:

 

<application root>/css/bootstrap.min.css

当服务器成功地找到这个文件之后,会把它返回给客户端。

3 创建处理器函数

正如之前的小节所说,ChitChat应用会通过HandleFunc函数把请求重定向到处理器函数。正如代码清单2所示,处理器函数实际上就是一个接受ResponseWriterRequest指针作为参数的Go函数。

代码清单2 main.go文件中的index处理器函数

 

func index(w http.ResponseWriter, r *http.Request) {
files := []string{"templates/layout.html",
"templates/navbar.html",
"templates/index.html",}
templates := template.Must(template.ParseFiles(files...))
threads, err := data.Threads(); if err == nil {
templates.ExecuteTemplate(w, "layout", threads)
}
}

index函数负责生成HTML并将其写入ResponseWriter中。因为这个处理器函数会用到html/template标准库中的Template结构,所以包含这个函数的文件需  要在文件的开头导入html/template库。之后的小节将对生成HTML的方法做进一步的介绍。

除了前面提到过的负责处理根URL请求的index处理器函数,main.go文件实际上还包含很多其他的处理器函数,如代码清单3所示。

代码清单3 ChitChat应用的main.go源文件

 

package main
import (
"net/http"

相关文章
|
16天前
|
自然语言处理 Java 数据库连接
掌握JSP页面编程:动态生成Web内容
【4月更文挑战第3天】Java Server Pages (JSP) 是一种用于创建动态Web内容的Java技术,它结合HTML并允许在页面中嵌入Java代码。JSP支持代码片段、表达式语言(EL)和JSTL标签库,简化动态内容生成。当服务器接收到请求时,执行JSP中的Java代码并将结果嵌入HTML返回给客户端。示例展示了如何显示当前日期和时间。JSP可与Servlet、JavaBeans、数据库等结合,用于构建功能丰富的交互式Web应用。
掌握JSP页面编程:动态生成Web内容
|
1月前
|
负载均衡 Java 中间件
使用Go语言构建高性能Web服务
Go语言作为一种快速、高效的编程语言,其在构建高性能Web服务方面具有独特优势。本文将探讨如何利用Go语言开发和优化Web服务,以实现更高的性能和可伸缩性。
|
3月前
|
安全 Go 数据处理
Go语言CSP编程实战:通道通信技术
Go语言CSP编程实战:通道通信技术
42 0
|
3月前
|
Go
高效Go语言编程:os包实用技术大揭示
高效Go语言编程:os包实用技术大揭示
40 0
|
1月前
|
SQL 前端开发 Go
编程笔记 GOLANG基础 001 为什么要学习Go语言
编程笔记 GOLANG基础 001 为什么要学习Go语言
N..
|
25天前
|
JavaScript 前端开发 PHP
web编程的正则表达式
web编程的正则表达式
N..
9 1
|
1月前
|
SQL 机器学习/深度学习 缓存
Go语言Web应用实战与案例分析
【2月更文挑战第21天】本文将通过实战案例的方式,深入探讨Go语言在Web应用开发中的应用。我们将分析一个实际项目的开发过程,展示Go语言在构建高性能、可扩展Web应用方面的优势,并分享在开发过程中遇到的问题和解决方案,为读者提供宝贵的实战经验。
|
1月前
|
安全 中间件 Go
Go语言Web服务性能优化与安全实践
【2月更文挑战第21天】本文将深入探讨Go语言在Web服务性能优化与安全实践方面的应用。通过介绍性能优化策略、并发编程模型以及安全加固措施,帮助读者理解并提升Go语言Web服务的性能表现与安全防护能力。
|
1月前
|
开发框架 JSON Go
Go语言Web开发基础与框架探索
【2月更文挑战第21天】本文将带领读者深入了解Go语言在Web开发领域的基础知识和常用框架。通过介绍Go语言的Web开发特点、核心库的使用,以及流行框架如Gin、Echo等的基本用法和优势,帮助读者快速上手Go语言Web开发,提升开发效率。
|
1月前
|
Go 开发工具 git
编程笔记 GOLANG基础 003 Go语言开发环境搭建
编程笔记 GOLANG基础 003 Go语言开发环境搭建