《GO Web 编程》 Web基础

怀齐智
2023-12-01

Web基础

URL的格式:

schema://host[:port#]/path/.../[?query-string][#anchor]
scheme 指定低层使用的协议(例如:http, https, ftp)
host HTTP服务器的IP地址或者域名
port# HTTP服务器的默认端口是80,这种情况下端口号可以省略。如果使用了别的端口,必须指明,例如 http://www.cnblogs.com:8080/
path 访问资源的路径
query-string 发送给http服务器的数据
anchor 锚

Go搭建web服务器

Go对每个客户端连接使用一个协程来提供服务

package main

import (
	"fmt"
	"net/http"
	"strings"
	"log"
)

func sayhelloName(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()  //解析参数,默认是不会解析的
	fmt.Println(r.Form)  //这些信息是输出到服务器端的打印信息
	fmt.Println("path", r.URL.Path)
	fmt.Println("scheme", r.URL.Scheme)
	fmt.Println(r.Form["url_long"])
	for k, v := range r.Form {
		fmt.Println("key:", k)
		fmt.Println("val:", strings.Join(v, ""))
	}
	fmt.Fprintf(w, "Hello astaxie!") //这个写入到w的是输出到客户端的
}

func main() {
	http.HandleFunc("/", sayhelloName) //设置访问的路由
	err := http.ListenAndServe(":9090", nil) //设置监听的端口
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

GO http 讲解

首先调用Http.HandleFunc,按顺序做了几件事:
1 调用了DefaultServerMux的HandleFunc
2 调用了DefaultServerMux的Handle
3 往DefaultServeMux的map[string]muxEntry中增加对应的handler和路由规则(做了一个强制类型转换)

其次调用http.ListenAndServe(":9090", nil),按顺序做了几件事情:
1 实例化Server
2 调用Server的ListenAndServe()
3 调用net.Listen(“tcp”, addr)监听端口
4 启动一个for循环,在循环体中Accept请求
5 对每个请求实例化一个Conn,并且开启一个goroutine为这个请求进行服务go c.serve()
6 读取每个请求的内容w, err := c.readRequest()
7 判断handler是否为空,如果没有设置handler(这个例子就没有设置handler),handler就设置为DefaultServeMux
8 调用handler的ServeHttp(这个函数的功能就是调用路由去执行相应执行函数)
9 在这个例子中,下面就进入到DefaultServerMux.ServeHttp
10 根据request选择handler,并且进入到这个handler的ServeHTTP,mux.handler(r).ServeHTTP(w, r)
11 选择handler:
A 判断是否有路由能满足这个request(循环遍历ServerMux的muxEntry)
B 如果有路由满足,调用这个路由handler的ServeHttp
C 如果没有路由满足,调用NotFoundHandler的ServeHttp
12. 注意这里的两个handler是不一样的。

可参考课本和Go http Server,自定义路由和默认路由有点不一样,自定义路由更宽松一些,默认路由有个强制类型转化。

表单

r.Form里面包含了所有请求的参数,比如URL中query-string、POST的数据、PUT的数据,所有当你在URL的querystring字段和POST冲突时,会保存成一个slice,里面存储了多个值

request.Form是一个url.Values类型,里面存储的是对应的类似key=value的信息,下面展示了可以对form数据进行的一些操作:

v := url.Values{}
v.Set("name", "Ava")
v.Add("friend", "Jess")
v.Add("friend", "Sarah")
v.Add("friend", "Zoe")
// v.Encode() == "name=Ava&friend=Jess&friend=Sarah&friend=Zoe"
fmt.Println(v.Get("name"))
fmt.Println(v.Get("friend"))
fmt.Println(v["friend"])

Tips: Request本身也提供了FormValue()函数来获取用户提交的参数。如r.Form[“username”]也可写成r.FormValue(“username”)。调用r.FormValue时会自动调用r.ParseForm,所以不必提前调用。r.FormValue只会返回同名参数中的第一个,若参数不存在则返回空字符串。

对于表单输入的验证,常使用正则表达式等。

预防跨站脚本攻击(Xss)

防止多次递交表单

处理文件上传

四种常见的post形式:

  1. application/x-www-form-urlencoded这应该是最常见的 POST 提交数据的方式了。浏览器的原生 form 表单,如果不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded 方式提交数据。
  2. multipart/form-data这又是一个常见的 POST 数据提交的方式。我们使用表单上传文件时,必须让 form 的 enctyped 等于这个值
  3. application/json
  4. text/xml
  5. 参考:四种常见的 POST 提交数据方式

访问数据库

session和数据存储

cookie里面存储了sessionId

cookie是有时间限制的,根据生命期不同分成两种:会话cookie和持久cookie;
如果不设置过期时间,则表示这个cookie生命周期为从创建到浏览器关闭止,只要关闭浏览器窗口,cookie就消失了。这种生命期为浏览会话期的cookie被称为会话cookie。会话cookie一般不保存在硬盘上而是保存在内存里。
如果设置了过期时间(setMaxAge(606024)),浏览器就会把cookie保存到硬盘上,关闭后再次打开浏览器,这些cookie依然有效直到超过设定的过期时间。存储在硬盘上的cookie可以在不同的浏览器进程间共享,比如两个IE窗口。而对于保存在内存的cookie,不同的浏览器有不同的处理方式。

cookie和session都可以实现会话跟踪,cookie是客户端保存,session是服务端保存。
cookie可以记录你的用户ID、密码、浏览过的网页、停留的时间等信息。当你再次来到该网站时,网站通过读取Cookies,得知你的相关信息,就可以做出相应的动作,如在页面显示欢迎你的标语,或者让你不用输入ID、密码就直接登录等等。一个网站只能读取它自己放置的信息,不能读取其他网站的Cookie文件。因此,Cookie文件还保存了host属性,即网站的域名或ip。

因为客户端保存不安全,一般cookie只保存sessionId,其他更多信息由session保存
cookie保存可能被用户篡改。

当客户端禁用了cookie的时候,则使用URL重写(就是在返回给用户的页面里的所有的URL后面追加session标识符)

session劫持(当另一台电脑复制了你的cookie,里面存储sessionId,那么就可以假装成你去访问服务器了)

  1. 给sessionID做频繁改变,使攻击者难有机会获取有效的sessionID;同时,设置sessionID只能在cookie中传递,设置httponly,所以基于URL攻击的可能性为零,同时被XSS获取sessionID也不可能。最后,设置了MaxAge=0,这样就相当于session cookie不会留在浏览器的历史记录里面。
  2. 在每个请求里面加上token,实现类似前面章节里面讲的防止form重复递交类似的功能,我们在每个请求里面加上一个隐藏的token,然后每次验证这个token,从而保证用户的请求都是唯一性。(一个token用过之后就对他进行更新)
对于cookie,session,token的个人理解
  1. 对于传统的cookie和session来说,cookie保存sessionId,根据sessionId来服务器找到会话信息。
  2. 这里可能会出现表单重复提交和CSRF攻击问题(偷用你的cookie),通过在session中存储token值作验证可以解决这个问题(token值每次用过后更新,重复提交则无效了。CSRF攻击也没用了)token值可以在前端通过隐藏域的方式作存储和提交(<input type="hidden" name="once" value="">
  3. 使用纯粹的token来维持会话,会话信息保存在客户端中,服务器不需要维护session了。token字符串是加密的(JWT)
  4. cookie是特有的一套老系统,每次发起请求会自动带上。token不会。

文本处理

Web服务

目前主流的有如下几种Web服务:REST、SOAP。

Go已经完备地支持了Socket编程

WebSocket

WebSocket是HTML5的重要特性,它实现了基于浏览器的远程socket,它使浏览器和服务器可以进行全双工通信,许多浏览器(Firefox、Google Chrome和Safari)都已对此做了支持。在WebSocket出现之前,为了实现即时通信,采用的技术都是“轮询”,即在特定的时间间隔内,由浏览器对服务器发出HTTP Request,服务器在收到请求后,返回最新的数据给浏览器刷新,“轮询”( Ajax 轮询)使得浏览器需要对服务器不断发出请求,这样会占用大量带宽。WebSocket采用了一些特殊的报头,使得浏览器和服务器只需要做一个握手的动作,就可以在浏览器和服务器之间建立一条连接通道。且此连接会保持在活动状态,你可以使用JavaScript来向连接写入或从中接收数据,就像在使用一个常规的TCP Socket一样。它解决了Web实时化的问题,相比传统HTTP有如下好处:

  1. 一个Web客户端只建立一个TCP连接
  2. Websocket服务端可以推送(push)数据到web客户端.
  3. 有更加轻量级的头,减少数据传送量

WebSocket URL的起始输入是ws://或是wss://(在SSL上)。下图展示了WebSocket的通信过程,一个带有特定报头的HTTP握手被发送到了服务器端,接着在服务器端或是客户端就可以通过JavaScript来使用某种套接口(socket),这一套接口可被用来通过事件句柄异步地接收数据。

Restful

REST,即Representational State Transfer的缩写。即"表现层状态转化"。
如果一个架构符合REST原则,就称它为RESTful架构。
个人理解,就是一种api定义的规则吧,查了相关资料还是看不太懂。

RPC调用

错误处理

 类似资料: