Go web 学习入门

又到了周末今天准备学习一下go web的入门,在这之前我觉得有必要总结一下上周自己遇到的一个问题。项目情况是这样的就是前端上传图片使用的是base64编码形式,自己使用了一个List<String>来接受上传的内容,然后后台解码成byte数组后生成图片,按理来说是一个很常规的操作了,但是自己却遇到了不少的问题。在springmvc中我使用了@RequestParam来接收对应的参数(事实证明这个确实是个败笔),因为当时自己调试接口使用的是idea自带的restful工具,自己将图片生成的base64编码内容传到方法里面确实成功了,而且各个流程都很顺利。但是和前台联调却一直走不通,前台改成数组,我也改数组(实际上string数组和List确实是可以相互转换的,这点没错),甚至我看网上有的出现传递数组时@RequestParam(value="xxx[]"),这样的形式,但是依然失败,因为前端参数名称根本就不是xxx[]这样的形式。后来自己同事建议我放到body里面接收,因为base64编码内容较长放在header里面确实很难接收,最后自己定义了一个包含List<String>的VO,并使用@RequestBody注解成功接收到参数,算是告一段落。其实我平时写代码比较喜欢将参数放到请求头里面,哪怕是多个参数也是一样,理论上确实没什么问题,但是如果遇到我这种上传文件,参数内容很长,我建议还是放到请求体。如果参数过多最好用VO封装一下(我之所以不喜欢用VO DTO这些是因为hybris中定义很麻烦,需要重新编译、重启,比较耗时间)。好了,接下来开始今天的go web入门。关于go的一些细节这里就不过多介绍,因为有的自己也不是很清楚,另外项目结构和java也不一样,一开始很难适应。
学习一门语言应该是从最开始的基础开始,但是之所以从web开始写项目是因为自己本来java也是做web方面的,自己要稍微熟悉一点,再一个也是因为自己学了一点vert.x的web基础,相互对比印证感觉也有利自己的理解吧。我本地go环境是1.11版本,开发者工具是GoLand,这个也是jet brains家族的产品,和idea比较相像。好了,开始正式的开发入门。
我先从最基本的"hello word"开始,go也需要一个main函数作为启动的入口,所以首先先建一个main函数,另外我需要有一个处理器处理用户请求,这里是helloHandler,另外还需要指定服务监听的端口号,代码如下:

func helloHandler(writer http.ResponseWriter, req *http.Request) {
    fmt.Println("----> handler start <----")
    //writer.Header().Set("content-type","application/json")
    fmt.Fprintf(writer, "hello go, rquest url is %s",req.URL.Path)
}
func main() {
    fmt.Println("----> go start <----")
    http.HandleFunc("/hello", helloHandler)
    err := http.ListenAndServe("localhost:9090", nil)
    checkErr(err)
}
func checkErr(e error) {
    if e != nil{
        log.Fatal(e)
    }
}

main函数里面首先是一个路由器,即将用户请求的URL和对应的处理器做一个映射,再一个就是监听的地址和端口,如果本地的话,也可以只保留端口号,"nil"参数即java中的null,这个参数是一个处理器,因为我们的请求路径和handler已经做了映射关系,所以我可以传入空值。因为http.ListenAndServe()方法会返回错误值,所以我对错误进行处理,只是简单的记录下来。如果觉得麻烦也可以直接使用log.Fatal(http.ListenAndServe())。log.Fatal()其实就是一个打印加上系统退出两个方法,也就是服务出现错误,那么会打印出错误信息,然后系统退出,服务停止。
helloHandler需要的参数一个是用户请求,这个是一个指针类型,还有一个就是响应的输出流,即输出服务返回的结果。fmt是一个格式话的包,里面都是和输出相关的比如println(),printf()等等。
接下来启动服务,并在浏览器访问:localhost:9090/hello,看到项目启动成功。

image

go有一个不一样的地方在于如果你的变量没有被使用会出错,所以有时候会出现"_"这种符号,表示返回的结果会被忽略(可能描述的不太准确,大概就是这个意思,因为go语言返回结果可能不止一个)。
这个只是输出内容到页面,那么我想进行页面跳转改怎么处理呢?go语言也有相应的包来处理,
现在我在项目下新建一个template包,下面存放我的html页面,并新建一个index.html。另外加一个跳转首页的handler。对代码进行修改如下:

func main() {       
    fmt.Println("----> go start <----")     
    http.HandleFunc("/hello", helloHandler)   
    http.HandleFunc("/index",indexHandler)   
    err := http.ListenAndServe("localhost:9090", nil)    
    checkErr(err)
}
func indexHandler(writer http.ResponseWriter, request *http.Request) {    
    fmt.Println("----> index handler start <----")    
    t,err := template.ParseFiles("template/index.html")    
    checkErr(err)   
    t.Execute(writer,nil)
}

这个方法里面是go自带的"html/template"包对本地的html页面转换成模板,template.ParseFiles()方法有两个返回结果,一个是模板对象(不知道这么说对不对,它的类型其实是一个指针,但是根据指针还是能拿到相应的对象),一个就是错误信息。最后执行模板对象的Execute()方法,两个参数一个是响应流,最后一个是动态数据,我们这里只做了最简单的跳转,所以传入空值。启动项目,然后浏览器访问:localhost:9090/index

image

当然实际开发肯定不能是这样的,更多的时候还是有一个总的控制器进行控制的。我们可以自己来实现一个控制器来实现将用户请求和对应的handler进行映射,就像springmvc的前端控制器一样,对用户的请求进行分发。go里面提供了一个默认的路由器ServeMux,具体就不介绍了,因为涉及到读写锁的问题我也还没搞懂!

下面是ServeMux ServeHTTP方法的源码:

image

我们可以使用它的ServeHTTP()方法,对用户请求进行分发。不过这里也可以参考ServeMux自己来模拟写一个类似的转发功能。在项目下新建一个webService包,并新建一个名称为CustomMux的type类型,并重写ServeHTTP()方法,代码如下:

type CustomMux struct {
}
func (mux *CustomMux) ServeHTTP(writer http.ResponseWriter, request *http.Request) {    
    switch request.URL.Path {   
    case "/":        
        IndexHandler(writer,request)        
        return    
    case "/home":        
        HomePageHandler(writer,request)        
        return    
    case "/login":        
        LoginHandler(writer,request)        
        return    
    default:       
        http.NotFound(writer,request)    
    }  
}
func LoginHandler(writer http.ResponseWriter, request *http.Request) {    
    log.Println("-----> login handler start <-----")    
    if request.Method == "Get" {       
        t,err := template.ParseFiles("template/login.html")        
        checkErr(err)        
        t.Execute(writer,request)   
    } else {        
        username := request.FormValue("username")       
        password := request.Form["password"]        
        fmt.Println("username: ", username)        
        fmt.Println("password: ", password)        
        http.Redirect(writer,request,"/home",302)    
    }
}
func HomePageHandler(writer http.ResponseWriter, request *http.Request) {    
    log.Println("-----> home page handler start <-----")   
    t,err := template.ParseFiles("template/homePage.html")   
    checkErr(err)   
    t.Execute(writer,"这是网站首页")
}
func IndexHandler(writer http.ResponseWriter, request *http.Request) {   
    log.Println("-----> index handler start <-----")    
    t,err := template.ParseFiles("template/login.html")    
    checkErr(err)    
    Name := "go template"    
    t.Execute(writer,Name)
}
func checkErr(e error) {    
    if e != nil {        
        log.Fatal(e)    
    }
}

当然我的CustomMux内部未定义任何的变量,ServeMux并不这样的,我只是做一个简单的模拟,ServeHTTP作为CustomMux对象的方法做的工作就是针对用户请求路径找到对应的处理器,并处理用户请求。"/"跳转登录页面,"/login"处理用户登录逻辑,成功后重定向到"/home","/home"跳转到主页,其中登录逻辑里面获取了用户请求参数,但是自己还没学数据库方面的内容,所以暂时略过。相关的登录页面和主页的html文件依然方法哦template文件夹下面。
如果使用我们自己定义的路由,那么需要改下main方法的内容,这里我写了两种方法,其实都是一样的,代码如下:

// 自定义全局变量
var mu webService.CustomMux
func main() {    
    fmt.Println("----> go start <----")    
    //http.HandleFunc("/hello", helloHandler)   
    //http.HandleFunc("/index",indexHandler)   
    mux := &webService.CustomMux{}    
    err := http.ListenAndServe("localhost:9090", mux)   
    //err := http.ListenAndServe("localhost:9090", &mu)   
    checkErr(err)
}

我们可以通过定义一个全局的CustomMux,然后将变量的地址作为handler传递给http.ListenAndServe(),也可以定义一个局部变量指向CustomMux的地址值,然后传递给http.ListenAndServe()。我们刚才传递第二个参数为nil,实际上使用的是默认的ServeMux来进行处理的。
接着重启服务,再次访问浏览器:localhost:9090/

image

没有问题,跳转到了登录页面,然后输入用户名和密码进行登录,同时将用户名和密码输出到控制台(数据库方面的知识后面再补充),然后看到页面跳转到主页了:

image

看浏览器的控制台也可以很直观的看到这个流程,请求的是localhost:9090/login
实际页面显示的首页,返回的状态码是302,也就是重定向,而重定向的location是"/home",没有问题。我们在看下控制台输出的用户名和密码值:

image

在loginHandler里面我做了一个判断,就是同过请求方法进行判断的,在login.html上我使用的是POST方法提交表单,而如果用户用GET请求,那么我只会继续显示登录页面信息,即不进行跳转。而如果是POST请求,取出用户提交参数,并进行校验做进一步的处理。取出用户数据的时候使用了两种方式,这两种方式都是可以的,看个人习惯。
在homePageHandler里面,t.Execute(writer,"这是网站主页")方法,向html页面写入了一段文字,然后在页面获取出来,但是go获取后台传递的数据使用的是"{{.}}"的形式,和java常用模板取值方式不太一样。还有就是根据对象属性获取"xx.yy"这种形式,等后面学习到了可以和数据库相关的内容做一个较为完整的web项目。

另外还有几种方式没有尝试,比如获取用请求URL上的参数,还有json格式的参数,文件上传,包括上传图片等等。这些留着以后有机会在尝试吧。

好了今天的学习就到这里了,go语言从自己目前的学习来看不是特别的难,只是有些思想和java不太一样,包括项目结构上,有时候感觉挺别扭的,不是很适应。当然像go比较厉害并发方面的内容,像协程啊、channel啊等等自己也是一知半解,希望随着不断的深入学习,自己也能慢慢理解吧。因为自己对go也都是刚刚入门,语法都还有很多要学的知识,所以写的内容难免会有不当之处,自己很多时候都是按照学习java的思路来学习的,也不知道对或者不对,如果有什么不当之处还请大家指正,谢谢大家!!
这次代码我上传到我的github,地址:https://github.com/ypcfly/learnGo,希望大家多多支持!!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355