加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

使用Golang 搭建http web服务器

发布时间:2020-12-16 18:51:35 所属栏目:大数据 来源:网络整理
导读:Golang在搭建web服务器方面的能力是毋庸置疑的。官方已经有提供net/http包为搭建http服务器做准备。使用这个包能很简单地对web的路由,静态文件,模版,cookie等数据进行设置。至于这个包是否好用,这个就见仁见智了。你可以从net包开始封装一个web框架,当

Golang在搭建web服务器方面的能力是毋庸置疑的。官方已经有提供net/http包为搭建http服务器做准备。使用这个包能很简单地对web的路由,静态文件,模版,cookie等数据进行设置。至于这个包是否好用,这个就见仁见智了。你可以从net包开始封装一个web框架,当然也可以基于http包封装一个web框架。但是不论你是打算怎么样做,了解基本的net/http包一定是你借鉴的基础。

需求

我们要做两个简单的后台web系统。这个系统简单到只有两个页面:登陆和首页。

1 登陆页面

登陆页面需要提交用户名和密码,将用户名和密码和mysql数据库中的用户名密码比对达到验证的效果。mysql数据库的go语言驱动推荐使用mymysql(https://github.com/ziutek/mymysql)。

当用户名和密码正确的时候,需要在cookie中种下用户名和加密后的密钥来进行cookie认证。我们不对cookie设置ExpireTime,这样这个cookie的有效期就是浏览器打开到浏览器关闭的session期间。

另外,这个页面还需要加载一个js。提交用户名和密码的是由js进行ajax post命令进行查询的。

这个页面需要加载css,进行页面排版

2 首页

首页是非常简单,但它是一个动态页面。

首先右上角的”欢迎登陆, 管理员:yejianfeng“。这里的用户名yejianfeng是根据不同的用户会进行变化的。这里需要用到模版,我们又会用到了一个模版包html/template。这个包的作用就是加载模版。

其次这个页面也需要的是css,js(退出系统的删除cookie操作由js控制)

路由

分析下访问路径,会有几个文件:

/admin/index -- 首页

/login/index --登陆页显示

/ajax/login -- 登陆的ajax动作

/css/main.css -- 获取main的css文件

/js/login.js -- 获取登陆的js文件

在net/http包中,动态文件的路由和静态文件的路由是分开的,动态文件使用http.HandleFunc进行设置静态文件就需要使用到http.FileServer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main
import (
"net/http"
)
func main() {
http.Handle( "/css/" ,http.FileServer(http.Dir( "template" )))
"/js/" )))
http.HandleFunc( "/admin/" "/login/" "/ajax/" "/" http.ListenAndServe( ":8888"
}

这里的http.FileServer(http.Dir("template"))的路径是怎么算的要注意下了。相对路径都是从当前执行路径路径下开始算的,这里设置的路径树是这样:

关于http.HandleFunc如果不理解请参考我的上一篇文章

http://www.cnblogs.com/yjf512/archive/2012/08/22/2650873.html

处理器

这里需要定制4个handler对应相应的一级路径。我们将这些个handler都放入到route.go文件中

页面404处理器

main中的逻辑是当/admin/ /login/ /ajax/都不符合路径的时候就进入404页面处理器NotFoundHandler

12
func NotFoundHandler(w http.ResponseWriter,r *http.Request) {
if r.URL.Path == {
http.Redirect(w,r, "/login/index" }
t,err := template.ParseFiles( "template/html/404.html" )
(err != nil) {
log.Println(err)
}
t.Execute(w,nil)
这段逻辑是很清晰的:如果路径是"/" 即访问路径是http://192.168.100.166:8888/的时候,访问会跳转到登陆页面,否则当访问路径不满足制定的路由,读取404模版,显示404。

强烈推荐使用template.ParseFile模版文件解析

template包中也提供了Parse这类直接在代码中写上模版的函数。使用起来,你会发现这类函数远没有ParseFile好用,原因很明显,如果你把模版写在代码中,一旦模版需要进行小细节的修改,也需要重新编译。并且我们使用模版的目的就是将显示逻辑和业务逻辑分离,Parse会导致整个代码是不可维护!

当然有人会考虑到效率问题,一个是读取文件,一个是直接读取内存。但是我觉得这点效率差别用户根本不会察觉到,牺牲极小的性能保证工程性是很值得的。

ParseFile中的路径问题也是很容易犯的问题。这里的填写相对路径,则文件是从当前执行的路径开始算。

比如这个路径,执行bin/webdemo,template/html/404.html就对应/go/gopath/template/html/404.html

这一步后续动作是在template/html文件夹中创建404.html页面

登陆页面处理器

17
18
func loginHandler(w http.ResponseWriter,monospace!important; min-height:auto!important">pathInfo := strings.Trim(r.URL.Path,monospace!important; min-height:auto!important">)
parts := strings.Split(pathInfo,monospace!important; min-height:auto!important">)
var action = ""
len(parts) > 1 {
action = strings.Title(parts[1]) + "Action"
}
login := &loginController{}
controller := reflect.ValueOf(login)
method := controller.MethodByName(action)
!method.IsValid() {
method = controller.MethodByName(strings.Title( "index" ) + "Action" )
}
requestValue := reflect.ValueOf(r)
responseValue := reflect.ValueOf(w)
method.Call([]reflect.Value{responseValue,requestValue})
根据MVC思想,对具体的逻辑内容使用不同的Controller,这里定义了一个loginController,使用reflect将pathInfo映射成为controller中的方法。

这样做的好处显而易见:

1 清晰的文件逻辑。不同的一级目录分配到不同的控制器,不同的控制器中有不同的方法处理。这些控制器放在一个独立的文件中,目录结构清晰干净。

2 路由和业务逻辑分开。 main中的http.HandleFunc处理一级路由,route处理二级路由,controller处理业务逻辑,每个单元分开处理。

好了,下面需要定义loginContrller,我们另外起一个文件loginController.go

"html/template"
type loginController struct {
}
func ( this *loginController)IndexAction(w http.ResponseWriter,r *http.Request) {
"template/html/login/index.html" )
(err != nil) {
log.Println(err)
}
下面需要创建template/html/login/index.html

这个文件中包含mian.css,jquery.js,base.js。 创建这些css和js。具体的css和js内容请看github源码

js中的逻辑是这样写的:当login表单提交的时候,会调用/ajax/login进行验证操作,下面就开始写ajax的处理器

ajax处理器

route中的ajaxHandler和loginHandler是大同小异,这里就忽略不说了,具体说一下ajaxController

18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
"github.com/ziutek/mymysql/autorc"
_ "github.com/ziutek/mymysql/thrsafe"
"encoding/json"
)
type Result {
Ret int
Reason string
Data interface {}
type ajaxController {
}
*ajacController)LoginAction(w http.ResponseWriter,r *http.Request) {
w.Header().Set( "content-type" "application/json" )
err := r.ParseForm()
err != nil {
OutputJson(w,monospace!important; min-height:auto!important; color:blue!important">"参数错误" return
}
admin_name := r.FormValue( "admin_name" )
admin_password := r.FormValue( "admin_password" )
admin_name == "" || admin_password == {
return
}
db := mysql.New( "tcp" "192.168.199.128" "root" "test" "webdemo" )
err := db.Connect(); err != nil {
"数据库操作失败" return
}
defer db.Close()
rows,res,err := db.Query( "select * from webdemo_admin where admin_name = '%s'" err != nil {
return
}
name := res.Map( )
admin_password_db := rows[0].Str(name)
admin_password_db != admin_password {
"密码输入错误" return
}
// 存入cookie,使用cookie存储
expiration := time.Unix(1,0)
cookie := http.Cookie{Name: )),Path: }
http.SetCookie(w,&cookie)
"操作成功" return
}
func OutputJson(w http.ResponseWriter,ret int string {}) {
out := &Result{ret,reason,i}
b,err := json.Marshal( )
err != nil {
return
}
w.Write(b)
这段代码有几个地方可以看看:

如何设置header:

w.Header().Set("content-type","application/json")

如何解析参数:

err := r.ParseForm()

admin_name := r.FormValue("admin_name")

如何连接数据库

db := mysql.New("tcp","","192.168.199.128","root","test","webdemo")

当然这里得需要有个数据库和数据表,建表和建表的sql文件在github上能看到

create table webdemo_admin

(

admin_id int not null auto_increment,

admin_name varchar(32) not null,sans-serif; font-size:14px"> admin_password varchar(32) not null,sans-serif; font-size:14px"> primary key(admin_id)

);

如何设置cookie

cookie := http.Cookie{Name: "admin_name",Value: rows[0].Str(res.Map("admin_name")),Path: "/"}

http.SetCookie(w,&cookie)

主页处理器

adminHandler的逻辑比其他Handler多的一个是需要获取cookie

cookie,err := r.Cookie("admin_name")

并且在传递给controller的时候需要将admin_name传递进去

21
func adminHandler(w http.ResponseWriter,0)!important">// 获取cookie
cookie,err := r.Cookie( err != nil || cookie.Value == {
}
)
admin := &adminController{}
controller := reflect.ValueOf(admin)
method := controller.MethodByName(action)
!method.IsValid() {
)
}
requestValue := reflect.ValueOf(r)
responseValue := reflect.ValueOf(w)
userValue := reflect.ValueOf(cookie.Value)
其他的部分都是一样的了。

它对应的Controller的Action是

14
type User {
UserName string
}
type adminController {
*adminController)IndexAction(w http.ResponseWriter,r *http.Request,user ) {
"template/html/admin/index.html" (err != nil) {
log.Println(err)
}
这里就将user传递出去给admin/index模版

模版内部使用{{.UserName}}进行参数显示

后记

至此,这个基本的webdemo就完成了。启动服务之后,就会在8888端口开启了web服务。

当然,这个web服务还有许多东西可以优化,个人信息验证,公共模板的提炼使用,数据库的密码使用密文等。但是这个例子中已经用到了搭建web服务器最基本的几个技能了。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!