介绍 本教程涵盖:创建具有加载和保存方法的数据结构使用该nethttp包构建Web应用程序使用htmltemplate包处理HTML模板使用regexp包验证用户输入使用闭包 需要知识:编程经验了解基本的网络技术(HTTP、HTML)一些UNIXDOS命令行知识 入门 目前,您需要有一台FreeBSD、Linux、macOS或Windows机器来运行Go。 在你的GOPATH和cd中为本教程创建一个新目录:mkdirgowikicdgowiki 创建一个名为的文件wiki。go,在您喜欢的编辑器中打开它,然后添加以下行:packagemainimport(fmtos) 我们从Go标准库中导入fmt和os包。稍后,当我们实现附加功能时,我们将在此import声明中添加更多包。 数据结构 让我们从定义数据结构开始。一个wiki由一系列相互关联的页面组成,每个页面都有一个标题和一个正文(页面内容)。在这里,我们定义Page为一个结构体,其中包含两个字段,分别代表标题和正文。typePagestruct{TitlestringBody〔〕byte} 该类型的〔〕byte意思是byte切片。Body元素是〔〕byte类型而不是string类型,因为这是我们将使用的io库所期望的类型。 该Page结构描述了页面数据将如何存储在内存中。但是持久存储呢?我们可以通过在上创建一个save方法来解决这个问题Page:func(pPage)save()error{filename:p。Title。txtreturnos。WriteFile(filename,p。Body,0600)} 这是一个名为save的方法,它的接收者p是一个指向的指针Page。它不接受任何参数,并返回一个error类型的值。 此方法会将Pages保存Body到文本文件中。为简单起见,我们将使用Title作为文件名。 该save方法返回一个error值,因为这是WriteFile(将字节切片写入文件的标准库函数)的返回类型。该save方法返回错误值,让应用程序在写入文件时出现任何问题时处理它。如果一切顺利,Page。save()将返回nil(指针、接口和其他一些类型的零值)。 八进制整数文字0600,作为第三个参数传递给WriteFile,表示创建文件时应仅对当前用户具有读写权限。 除了保存页面,我们还需要加载页面:funcloadPage(titlestring)Page{filename:title。txtbody,:os。ReadFile(filename)returnPage{Title:title,Body:body}} 该loadPage函数从title参数构造文件名,将文件的内容读入一个新变量body,并返回一个指向Page由正确的标题和正文值构造的文字的指针。 函数可以返回多个值。标准库函数os。ReadFile返回〔〕byte和error。在loadPage中,尚未处理错误;下划线()符号表示的空白标识符用于丢弃错误返回值(本质上,将值赋值为空)。 但是如果ReadFile遇到错误会发生什么?例如,该文件可能不存在。我们不应该忽视这样的错误。让我们修改函数以返回Page和error。funcloadPage(titlestring)(Page,error){filename:title。txtbody,err:os。ReadFile(filename)iferr!nil{returnnil,err}returnPage{Title:title,Body:body},nil} 该函数的调用者现在可以检查第二个参数;如果是,nil则它已成功加载页面。如果不是,它将是可以由调用者处理的error。 现在,我们有一个简单的数据结构和保存到文件以及从文件加载的能力。让我们编写一个main函数来测试我们所写的内容:funcmain(){p1:Page{Title:TestPage,Body:〔〕byte(ThisisasamplePage。)}p1。save()p2,:loadPage(TestPage)fmt。Println(string(p2。Body))} 编译并执行此代码后,将创建一个名为的文件TestPage。txt,其中包含p1。然后将文件读入structp2,并将其Body元素打印到屏幕上。 您可以像这样编译和运行程序:gobuildwiki。go。wikiThisisasamplePage。 介绍nethttp包装 这是一个简单Web服务器的完整工作示例:go:buildignorepackagemainimport(fmtlognethttp)funchandler(whttp。ResponseWriter,rhttp。Request){fmt。Fprintf(w,Hithere,Iloves!,r。URL。Path〔1:〕)}funcmain(){http。HandleFunc(,handler)log。Fatal(http。ListenAndServe(:8080,nil))} 该main函数以对http。HandleFunc的调用开始,它告诉http包使用handler处理对Web根的所有请求。 然后它调用http。ListenAndServe,指定它应该在任何接口(:8080)上侦听端口8080。(暂时不要担心它的第二个参数nil。)这个函数将一直阻塞,直到程序终止。 ListenAndServe总是返回一个错误,因为它只在发生意外错误时返回。为了记录该错误,我们将函数调用用log。Fatal。 handler函数的类型为http。HandlerFunc。它以http。ResponseWriter和anhttp。Request作为参数。 http。ResponseWriter值组合了HTTP服务器的响应;通过写入它,我们将数据发送到HTTP客户端。 http。Request是表示客户端HTTP请求的数据结构。r。URL。Path是请求URL的路径组件。〔1:〕意味着创建从第一个字符到结尾的子切片。 如果您运行此程序并访问URL:http:localhost:8080monkeys 该程序将显示一个页面,其中包含:Hithere,Ilovemonkeys! 使用nethttp包创建wiki页面 要使用nethttp包,必须将其导入:import(fmtoslognethttp) 让我们创建一个处理程序,viewHandler它允许用户查看wiki页面。它将处理以view为前缀的URL。funcviewHandler(whttp。ResponseWriter,rhttp。Request){title:r。URL。Path〔len(view):〕p,:loadPage(title)fmt。Fprintf(w,h1sh1s,p。Title,p。Body)} 再次注意使用忽略error来自loadPage的返回值。这是为了简单起见,通常被认为是不好的做法。我们稍后会处理这个问题。 首先,此函数从请求URL的路径组件r。URL。Path中提取页面标题。Path重新切片以删除请求路径的前导view。这是因为路径总是以view开头,它不是页面标题的一部分。 然后该函数加载页面数据,用一串简单的HTML格式化页面,并将其w写入http。ResponseWriter。 要使用这个处理程序,我们重写我们的main函数来使用viewHandler处理view路径下的任何请求。funcmain(){http。HandleFunc(view,viewHandler)log。Fatal(http。ListenAndServe(:8080,nil))} 让我们创建一些页面数据,编译我们的代码,并尝试提供一个wiki页面。 在编辑器中打开test。txt文件,并在其中保存字符串Helloworld(不带引号)。gobuildwiki。go。wiki 随着这个网络服务器的运行,访问http:localhost:8080viewtest应该会显示一个标题为test的页面,其中包含Helloworld这个词。