发布日期:2024-02-01 18:50浏览次数:
零基础通过开发Web服务学习Go语言
本文适合有一定编程基础,但是没有Go语言基础的同学。
也就是俗称的“骗你”学Go语言系列。
这是一个适合阅读的系列,我希望您能够在车上、厕所、餐厅都阅读它,涉及代码的部分也是精简而实用的。
Go语言能干什么?为什么要学习Go语言?
本系列文章,将会以编程开发中需求最大、应用最广的Web开发为例,一步一步的学习Go语言。当看完本系列,您能够清晰的了解Go语言Web开发的基本原理,您会惊叹于Go语言的简洁、高效和新鲜。
《刻意练习》一书中说,学习需要及时反馈结果,才能提高学习体验。
本系列文章的每一节,都会包含一段可运行的有效代码,跟着内容一步一步操作,你可以在你自己的计算机上体验每一句代码的作用。
文章围绕范例为核心,介绍知识点。文中不罗列语法和关键字,当您还不知道它们用来干什么时,反而会干扰您的注意力。
希望您在阅读本系列文章后,对Go语言产生更多的学习欲望,成为一名合格的Gopher
Gopher:原译是囊地鼠,也就是Go语言Logo的那个小可爱;这里特指Go程序员给自己的昵称。
访问Go语言官方网站下载页面:
https://golang.org/dl
可以看到官网提供了Microsoft Windows、Apple MacOS、Linux和Source下载。
直接下载对应操作系统的安装包。
在正式使用Go编写代码之前,还有一个重要的“环境变量”需要配置:“$GOPATH”
GOPATH环境变量指定工作区的位置。如果没有设置GOPATH,则假定在Unix系统上为,在Windows上为 。如果要将自定义位置用作工作空间,可以设置GOPATH环境变量。
GOPATH环境变量是用于设置Go编译可以执行文件、包源码以及依赖包所必要的工作目录路径,Go1.11后,新的木块管理虽然可以不再依赖 ,但是依然需要使用 路径来保存依赖包。
首先,创建好一个目录用作GOPATH目录
然后设置环境变量 :
Linux & MacOS:
导入环境变量
保存环境变量
Windows:
$GOPATH设置好后,它是一个空目录,当在开发工作中执行go get、go install命令后, $GOPATH所指定的目录会生成3个子目录:
bin:存放 编译的可执行二进制文件
pkg:存放 编译后的包文件,就会存放在这里
src:存放 命令下载的源码包文件
打开命令行工具,运行
如果你看到类似这样的结果,说明Go语言环境安装完成.
现在很多通用的编辑器或IDE都支持Go语言比如
Atom
Visual Studio Code
Sublime Text2
ItelliJ Idea
Go语言专用的IDE有
LiteIDE
Goland
专用的IDE无论是配置和使用都比通用编辑器/IDE的简单许多,但是我还是推荐大家使用通用编辑器/IDE,因为在开发过程中肯定会需要编写一些其他语言的程序或脚本,专用IDE在其他语言编写方面较弱,来回切换不同的编辑器/IDE窗口会很低效。
另外,专用IDE提供很多高效的工具,在编译、调试方面都很方便,但是学习阶段,建议大家手动执行命令编译、调试,有利于掌握Go语言。
命令行代码仅适用于Linux和MacOS系统,Windows根据说明在视窗下操作即可。
创建一个文件夹,进入该文件夹
新建一个文件 main.go
看到终端会输出:
第一个Go代码就完成了
这是一个很简单的Hello World,但是包含了Go语言编程的许多核心元素,接下来就详细讲解。
Go程序是由包构成的。
代码的第一行, 申明程序自己的包,用 关键字。关键字必须是第一行出现的代码。
范例代码中,申明的本包名
在代码中第二行, 导入“fmt”包, 使用 关键字。默认情况下,导入包的包名与导入路径的最后一个元素一致,例如 ,在代码中使用这个包时,直接使用,例如
导入包的写法可以多行,也可以“分组”, 例如:
或者 分组
fmt包是Go语言内建的包,作用是输出打印。
是function的缩写, 在Go语言中是定义函数的关键字。
func定义函数的格式为:
本例中定义了一个main函数。函数没有参数。然后在函数体里调用包的函数,在控制台输出字符串 “Hello, 世界”
所有Go语言的程序的入口都是main包下的main函数 ,


我们已经介绍了九牛一毛中的一毛,接下来正式通过搭建一个简单的Web服务学习Go语言
打开之前创建好的文件,修改代码如下:
保存文件,然后在命令行工具下输入命令,运行程序
这时候,IM电竞华为集众智共议: 鸿蒙生态落地浙江助力软件产业高质量发展你会看到用 打印出来的提示,在浏览器中访问 你将访问到一个页面,显示 "这是一个开始"
我们从程序运行的顺序去了解它的工作流程
首先,定义,然后导入包。
这里,导入了一个新的包 ,宇信科技:截至2023年6月30日 软件著作权增至6IM电竞94件 专利数增至45件这个包是官方的,实现http客户端和服务端的各种功能。Go语言开发Web服务的所有功能就是基于这个包(其他第三方的Go语言Web框架也都基于这个包,没有例外)
第一句,匹配路由和处理函数
调用http包的HandleFunc方法,匹配一个路由到一个处理函数。
这句代码的意思是,当通过访问地址 http://localhost/ 时,就等同于调用了 myWeb 函数。
第二句,用fmt在控制台打印一句话,纯属提示。
第三句,开启服务并且监听端口
在这句,调用了包中的函数,该函数有两个参数,第一个是指定监听的端口号,第二个是指定处理请求的handler,通常这个参数填nil,表示使用默认的ServeMux作为handler。
什么是nil?
就是其他语言里的。
什么是handler?什么是ServeMux?ServeMux就是一个HTTP请求多路由复用器。它将每个传入请求的URL与已注册模式的列表进行匹配,并调用与URL最匹配的模式的处理程序。很熟悉吧?还记得前面的吗?他就是给http包中默认的ServeMux(DefaultServeMux)添加URL与处理函数匹配。通常都是使用http包中的默认ServeMux,所以在函数的第二个参数提供nil就可以了
函数会一直监听,除非强制退出或者出现错误。
如果这句开启监听出现错误,函数会退出监听并会返回一个error类型的对象,因此用变量接收返回对象。紧接着,判断是否为空,打印出错误内容,程序结束。
这里有两个Go语言知识点
1.定义变量
Go语言是静态语言,需要定义变量,定义变量用关键字
Go还提了一种简单的变量定义方式,自动根据赋值的对象定义变量类型,用起来很像脚本语言:
2.错误处理
在Go语言中,这是很常见的错误处理操作,另一种panic异常,官方建议不要使用或尽量少用,暂不做介绍,先从err开始。
Go语言中规定,如果函数可能出现错误,应该返回一个error对象,这个对象至少包含一个Error()方法错误信息。
因此,在Go中,是看不到try/catch语句的,函数使用error传递错误,用if语句判断错误对象并且处理错误。
3. if 语句
与大多数语言使用方式一样,唯一的区别是,表达式不需要()包起来。
另外,Go语言中的if可以嵌入一个表达式,用;号隔开,例如范例中的代码可以改为:
err这个变量的生命周期只在块中有效。
在函数中,用将 myWeb与路由匹配在一起。
函数定义了两个参数,,参数类型分别是和,是响应留写入器,是请求对象的指针。
响应流写入器 w: 用来写入http响应数据
请求对象 * r: 包含了http请求所有信息,注意,这里使用了指针,在定义参数时用标记类型,说明这个参数需要的是这个类型的对象的指针。
当有请求路径,请求对象和响应流写入器被传递给函数,并由函数负责处理这次请求。
Go语言中红的指针: 在Go语言中 除了map、slice、chan 其他函数传参都是值传递,所以,如果需要达到引用传递的效果,通过传递对象的指针实现。在Go语言中,取对象的指针用,取值用,例如:
把这些代码放在函数里,运行看看
再一次遇到老熟人,这次使用他的函数将字符串“这是一个开始”,写入到响应流写入器对象。响应流写入器里写入的内容最后会被Response输出到用户浏览器的页面上。
定义一个函数myWeb,接收参数 响应流写入器和请求对象两个参数
在main函数中,在默认的ServeMux中将路由/与myWeb绑定
运行默认的ServeMux监听本地8080端口
访问本地8080端口 路由
http将请求对象和响应写入器都传递给myWeb处理
myWeb向响应流中写入一句话,结束这次请求。
虽然代


打开文件,修改函数,如下:
运行程序
然后用任何工具(推荐Postman)提交一个POST请求,并且带上URL参数,或者在命令行中用cURL提交
页面和终端命令行工具会答应出以下内容:
请求的所有内容,都保存在对象中,也就是获得的参数 。
首先,调用,作用是填充数据到 和
接下来,分别循环获取遍历打印出 函数返回的值 和 值里的每一个参数。
和 分别是URL参数对象和表单参数对象,它们都是键值对值,键的类型是字符串,值的类型是数组。
在http协议中,无论URL和表单,相同名称的参数会组成数组。
循环遍历:for...range
Go语言的循环只有关键字,以下是Go中4种循环
前3种,循环你可以看作条件循环的变体(无限循环就是无条件的循环)。
本例种用到的是 循环,遍历可遍历对象,并且每轮循环都会将键和值分别赋值给变量 和
我们页面还是只是输出一句“这是一个开始”。我们需要一个可以见人的页面,这样可以不行
你也许也想到了,是不是可以在输出时,硬编码HTML字符串?当然可以,但是Go http包提供了更好的方式,HTML模版。
接下来,我们就用HTML模版做一个真正的页面出来
读取HTML模版文件,用数据替换掉对应的标签,生成完整的HTML字符串,响应给浏览器,这是所有Web开发框架的常规操作。Go也是这么干的。
Go html包提供了这样的功能:
""
函数不变,增加导入包,然后修改函数,如下:
在命令行中运行 ,访问
看, 中的和 被替换成了 和。并且,不再使用函数输出数据到Response了
但是...这还是在代码里硬编码HTML字符串啊...
别着急,template包可以解析文件,继续修改代码:
根目录下创建一个子目录存放模版文件 templates, 然后进入目录创建一个文件 ,并写入一些HTML代码 (我不是个好前端)
修改函数
在运行一下看看,页面按照HTML文件的内容输出了,并且{{.name}}和{{.someStr}}也替换了,对吧?
可以看到,包的核心功能就是将HTML字符串解析暂存起来,然后调用的时候,用数据替换掉HTML字符串中的里面的内容
在第一个方式中 初始化一个template对象变量,然后用调用函数解析字符串模版。
然后,创建一个map对象,渲染的时候会用到。
最后,调用函数,不仅用数据渲染模版,还替代了函数的工作,将输出到Response数据流写入器中。
第二个方式中,直接调用 包的函数,直接解析相对路径下的index.html文件并创建对象变量。
本节出现了两个新东西 类型 和 赋值给“”
map类型
map类型: 字典类型(键值对),之前的获取请求参数章节中出现的 url/values类型其实就是从map类型中扩展出来的
的初始化可以使用:
make是内置函数,只能用来初始化 map、slice 和 chan,并且make函数和另一个内置函数new不同点在于,它返回的并不是指针,而只是一个类型。
map赋值于其他语言的字典对象相同,取值有两种方式,请看下面的代码:
代码中的变量ok,可以用来判断这一项是否设置过,取值时如果项不存在,是不会异常的,取出来的值为该类型的零值,比如 int类型的值,不存在的项就为0;string类型的值不存在就为空字符串,所以通过值是否为0值是不能判断该项是否设置过的。ok,会获得true 或者 false,判断该项是否设置过,true为存在,false为不存在于map中。
Go中的map还有几个特点需要了解:
的项的顺序是不固定的,每次遍历排列的顺序都是不同的,所以不能用顺序判断内容
可以用 遍历
在函数参数中是引用传递(Go语言中,只有map、slice、chan是引用传递,其他都是值传递)
赋值给 “_”
Go有一个特点,变量定义后如果没使用,会报错,无法编译。一般情况下没什么问题,但是极少情况下,我们调用函数,但是并不需要使用返回值,但是不使用,又无法编译,怎么办?
"" 就是用来解决这个问题的,用来丢弃函数的返回值。比如本例中, 除了返回模版对象外,还会返回一个对象,但是这样简单的例子,出错的可能性极小,所以我不想处理了,将返回值用“”丢弃掉。
注意注意注意:在实际项目中,请不要丢弃error,任何意外都是可能出现的,丢弃error会导致当出现罕见的意外情况时,非常难于Debug。所有的error都应该要处理,至少写入到日志或打印到控
制台。(切记,不要丢弃 error ,很多Gopher们在这个问题上有大把的血泪史)OK,到目前为止,用Go语言搭建一个简单的网页的核心部分就完成了。
对。例子里的模版全是HTML代码,一个漂亮的网页还必须用到图片、js脚本和css样式文件,可是...和PHP不同,请求路径是通过HandleFunc匹配到处理函数的,难道要把js、css和图片都通过函数输出后,再用HandleFunc和URL路径匹配?
以在index.html文件里引用一个index.js文件为例。
在项目的根目录下创建static目录,进入static目录,创建js目录,然后在js目录里创建一个index.js文件。
打开之前的index.html文件,在
后面加上
运行 ,访问 http://localhost:8080,页面会弹出提示框。
页面在浏览器中运行时,当运行到浏览器会请求 这个路径
程序检查到第一层路由匹配,于是用文件服务处理这次请求,匹配到程序运行的路径下相对路径。
匹配的设置是 文件中这两句
也可以写成一句,更容易理解
很简单...但是,可能还是不满足需求,因为, 如果
对应到 https://studygolang.com/articles/static/js
对应到 https://studygolang.com/articles/static/css
对应到 https://studygolang.com/articles/static/img
对应到 https://studygolang.com/articles/static/upload
这样所有请求的路径都必须匹配一个static目录下的子目录。
如果,我就想访问static目录下的文件,或者,js、css、img、upload目录就在项目根目录下怎么办?
http包下,还提供了一个函数 剥开前缀,如下:
这样,浏览器中访问/js/时,直接对应到https://studygolang.com/articles/static目录下,不需要再加一个/js/子目录。
所以,如果需要再根目录添加多个静态目录,并且和URL的路径匹配,可以这样:
对应到 https://studygolang.com/articles/js
对应到 https://studygolang.com/articles/css
对应到 https://studygolang.com/articles/img
对应到 https://studygolang.com/articles/upload
到这里,一个从流程上完整的Web服务程序就介绍完了。
整理一下,一个Go语言的Web程序基本的流程:
定义请求处理函数
用http包的HandleFunc匹配处理函数和路由
ListenAndServe开启监听
当有http请求时:
http请求到监听的的端口
根据路由将请求对象和响应写入器传递给匹配的处理函数
处理函数经过一番操作后,将数据写入到响应写入器
响应给请求的浏览器
之前调试都使用的是 命令运行程序。
您会发现,每次运行都会重新编译源码,如何将程序运行在没有Go环境的计算机上?
使用 命令,它会编译源码,生成可执行的二进制文件。
最简单的 命令什么参数都不用加,它会自动查找目录下的main包下的main()函数,然后依次查找依赖包编译成一个可执行文件。
其他依赖文件的相对路径需要和编译成功后的可执行文件一致,例如范例中的templates文件夹和static文件夹。
默认情况下,会编译为和开发操作系统对应的可执行文件,如果要编译其他操作系统的可执行文件,需要用到交叉编译。
例如将Linux和MacOSX系统编译到windows
在Windows上需要使用SET命令, 例如在Windows上编译到Linux系统
快速简单搭建Go开发环境
导入包、申明包
func 定义函数
变量的申明方法
Go语言的异常处理
for循环
map类型
用http包,编写一个网站程序
本系列内容很少,很简洁,希望您能对Go多一点点了解,对Go多增加一点点兴趣。
还有很多内容成为一个合格的Gopher必须要了解的知识
struct 结构体
给struct定义方法
interface 接口定义和实现
chan类型
slice类型
goroutine
panic处理
以后的文章中会涉及更多关于Go语言编程的内容
欢迎关注晓代码公众号,和大家一起学习吧
有疑问加站长微信联系(非本文作者)
原创文章出自IM电竞,欢迎转载!