从seo角度做网站流量,查看网站cms,企业网站开发与设计论文,私人推流服务器Go web
Web工作方式
浏览器本身是一个客户端#xff0c;当你输入URL的时候#xff0c;首先浏览器会去请求DNS服务器#xff0c;通过DNS获取相应的域名对应的IP#xff0c;然后通过IP地址找到IP对应的服务器后#xff0c;要求建立TCP连接#xff0c;等浏览器发送完HTTP …Go web
Web工作方式
浏览器本身是一个客户端当你输入URL的时候首先浏览器会去请求DNS服务器通过DNS获取相应的域名对应的IP然后通过IP地址找到IP对应的服务器后要求建立TCP连接等浏览器发送完HTTP Request包后服务器接收到请求包之后才开始处理请求包服务器调用自身服务 URL和DNS解析
URL是“统一资源定位符”的英文缩写用于描述一个网络上的资源
scheme://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 锚DNS 是**“域名系统”的英文缩写是一种组织成域层次结构的计算机和网络服务命名系统他用于TCP/IP网络从事将主机名或域名转换为实际IP地址**的工作 DNS解析过程
1.在浏览器中输入www.qq.com域名操作系统会检查自己本地的hosts文件是否由这个网址映射关系如果有直接返回域名解析
2.如果hosts里没有这个域名的映射则查找本地DNS解析器缓存是否有这个网址映射关系
3.如果本地DNS缓存器缓存都没有相应的网址映射关系首先会找TCP/IP参数中设置的首选DNS服务器在此我们叫他本地DNS服务器,如果要查询的域名包含在本地配置区域资源中则返回解析结果给客户机完成域名解析
4.如果要查询的域名不由本地DNS服务器区域解析 举个例子来说你想知道某个一起上法律课的女孩的电话并且你偷偷拍了她的照片回到寝室告诉一个很仗义的哥们儿这个哥们儿二话没说拍着胸脯告诉你甭急我替你查(此处完成了一次递归查询即问询者的角色更替)。然后他拿着照片问了学院大四学长学长告诉他这姑娘是xx系的然后这哥们儿马不停蹄又问了xx系的办公室主任助理同学助理同学说是xx系yy班的然后很仗义的哥们儿去xx系yy班的班长那里取到了该女孩儿电话。(此处完成若干次迭代查询即问询者角色不变但反复更替问询对象)最后他把号码交到了你手里。完成整个查询过程
HTTP协议详解
HTTP协议是Web工作的核心
HTTP是一种让Web服务器与浏览器通过Internet发送与接收数据的协议它建立在TCP协议之上一般采用TCP的80端口。
HTTP协议是无状态的同一个客户端的这次请求和上次请求时没有对应关系对HTTP服务器来说它并不知道这两个请求
HTTP请求包浏览器信息
GET /domains/example/ HTTP/1.1 //请求行: 请求方法 请求URI HTTP协议/协议版本
Hostwww.iana.org //服务端的主机名
User-AgentMozilla/5.0 (Windows NT 6.1) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4 //浏览器信息
Accepttext/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8 //客户端能接收的mine
Accept-Encodinggzip,deflate,sdch //是否支持流压缩
Accept-CharsetUTF-8,*;q0.5 //客户端字符编码集
//空行,用于分割请求头和消息体
//消息体,请求资源参数,例如POST传递的参数HTTP协议定义了很多与服务器交互的请求方法最基本的有4种分别是GET,POST,PUT,DELETE。一个URL地址用于描述一个网络上的资源而HTTP中的GET, POST, PUT, DELETE就对应着对这个资源的查增改删4个操作。我们最常见的就是GET和POST了。GET一般用于获取/查询资源信息而POST一般用于更新资源信息。 这个是GET信息 这个是POST信息
GET和POST信息
1.GET请求信息体为空POST请求带有消息体
2.GET提交的数据会放在URL之后以分割URL和传输数据参数之间以相连
3.GET提交的数据大小有限制而POST方法提交的数据没有限制
4.GET方式提交数据会带来安全问题
如果输入用户名和密码提交数据用户名和密码将出现在URL上如果页面可以被缓存将从历史记录获得该用户的账号和密码
HTTP响应包服务器信息
response包
HTTP/1.1 200 OK //状态行
Server: nginx/1.0.8 //服务器使用的WEB软件名及版本
Date:Date: Tue, 30 Oct 2012 04:14:25 GMT //发送时间
Content-Type: text/html //服务器发送信息的类型
Transfer-Encoding: chunked //表示发送HTTP包是分段发的
Connection: keep-alive //保持连接状态
Content-Length: 90 //主体内容长度
//空行 用来分割消息头和主体
!DOCTYPE html PUBLIC -//W3C//DTD XHTML 1.0 Transitional//EN... //消息体第一行叫做状态行由HTTP协议版本号状态码状态消息 三部分组成
我们可以看到200是它的状态码
1XX 提示信息 - 表示请求已被成功接收继续处理2XX 成功 - 表示请求已被成功接收理解接受3XX 重定向 - 要完成请求必须进行更进一步的处理4XX 客户端错误 - 请求有语法错误或请求无法实现5XX 服务器端错误 - 服务器未能实现合法的请求 200是常用的302表示跳转
HTTP协议是无状态的和Connection:keep-alive 的区别
无状态是指协议对于事务处理没有记忆能力服务器不知道客户端是什么状态。从另一方面讲打开一个服务器上的网页和你之前打开这个服务器上的网页之间没有任何联系
从HTTP/1.1起默认都开启了Keep-Alive保持连接特性简单地说当一个网页打开完成后客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭如果客户端再次访问这个服务器上的网页会继续使用这一条已经建立的TCP连接。
Keep-Alive不会永久保持连接它有一个保持时间
这是一次请求的request和response 我们可以注意到
为什么一个URL请求但是左边栏里面为什么会有那么多的资源请求
这个就是浏览器的一个功能第一次请求url服务器端返回的是html页面然后浏览器开始渲染HTML当解析到HTML DOM里面的图片链接css脚本和js脚本的链接浏览器就会自动发起一个请求静态资源的HTTP请求获取相对应的静态资源
3.2 Go搭建一个Web服务器
Go语言里面提供了一个完善的net/http包通过这个包可以对Web的路由静态文件模板cookie等数据进行设置和操作
package mainimport (fmtlognet/httpstrings
)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)}
}3.3 Go如何使得Web工作
Web工作方式的几个概念
Request用户请求的新消息用来解析用户的请求信息包括postgetcookieurl等信息
Response服务器需要反馈给客户端的信息
Conn:用户的每次请求连接
Handler:处理请求和生成返回信息的处理逻辑
分析http包运行机制 创建Listen Socket, 监听指定的端口, 等待客户端请求到来。Listen Socket接受客户端的请求, 得到Client Socket, 接下来通过Client Socket与客户端通信。处理客户端的请求, 首先从Client Socket读取HTTP请求的协议头, 如果是POST方法, 还可能要读取客户端提交的数据, 然后交给相应的handler处理请求, handler处理完毕准备好客户端需要的数据, 通过Client Socket写给客户端。
如何监听端口
Go通过一个 函数ListenAndServe来处理这个事情,底层
初始化一个serve对象然后调用net.Listen(“tcp”,addr),也就是底层用TCP协议搭建了一个服务然后监控我们设置的端口
如何接收客户端请求?
//接下来我会调用srv.Serve(net.Listener)这个函数这个函数就是处理接收客户端的请求信息
func (srv *Server) Serve(l net.Listener) error {//用来处理接收客户端的请求信息defer l.Close()var tempDelay time.Duration // how long to sleep on accept failurefor {rw, e : l.Accept()if e ! nil {if ne, ok : e.(net.Error); ok ne.Temporary() {if tempDelay 0 {tempDelay 5 * time.Millisecond} else {tempDelay * 2}if max : 1 * time.Second; tempDelay max {tempDelay max}log.Printf(http: Accept error: %v; retrying in %v, e, tempDelay)time.Sleep(tempDelay)continue}return e}tempDelay 0c, err : srv.newConn(rw)//创建一个Connif err ! nil {continue}go c.serve()//单独开了一个goroutine}
}如何分配handler?
如何具体分配到相应的函数来处理请求 conn首先会解析requestc.readRequest,然后获取对应的handler:
handler :c.server.Handler Go的http详解
Go的http有两个核心功能ConnServeMux
Conn的goroutine
Go为了实现高并发和高性能使用了goroutine来处理Conn的读写事件这样每个请求都能保持独立
c, err : srv.newConn(rw)
if err ! nil {continue
}
go c.serve()客户端的每次请求都会创建一个Conn这个Conn里面保存了该次请求的信息然后再传递到对应的handler该handler中可以读取到相应的header信息这样保证了每个请求的独立性
ServeMux的自定义路由器的实现
并发Concurrency是指在计算机科学中指的是同时执行多个独立的任务或操作的能力。这些任务可能在同一时间段内交替执行也可能在同一时间段内同时执行但它们都是在同一个时间段内并发地进行。并发通常涉及到多个线程、进程或任务同时执行并且这些执行之间可能相互独立也可能存在一定的交互关系。并发通常被用来提高系统的性能、资源利用率和响应能力。
需要锁机制是为了防止只有一个进程能访问共享资源
type ServeMux struct {mu sync.RWMutex //锁由于请求涉及到并发处理因此这里需要一个锁机制m map[string]muxEntry // 路由规则一个string对应一个muxEntry实体这里的string就是注册的路由表达式hosts bool // 是否在任意的规则中带有host信息
}
type muxEntry struct {explicit bool // 是否精确匹配h Handler // 这个路由表达式对应哪个handlerpattern string //匹配字符串
}
//每个路由表达式都对应着一个handlertype Handler interface {ServeHTTP(ResponseWriter, *Request) // 路由实现器
}我们调用了HandlerFunc(f),强制类型转换f称为HandlerFunc类型这样f就拥有了ServeHTTP方法
HandlerFunc(f)-因为HandlerFunc实现了Handler接口类型
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {f(w, r)
}默认路由器实现了ServeHTTP:
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {if r.RequestURI * {w.Header().Set(Connection, close)w.WriteHeader(StatusBadRequest)return}h, _ : mux.Handler(r)h.ServeHTTP(w, r)
}通过对http包的分析之后现在让我们来梳理一下整个的代码执行过程。 首先调用Http.HandleFunc 按顺序做了几件事 1 调用了DefaultServeMux的HandleFunc 2 调用了DefaultServeMux的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这个例子就没有设置handlerhandler就设置为DefaultServeMux 8 调用handler的ServeHttp 9 在这个例子中下面就进入到DefaultServeMux.ServeHttp 10 根据request选择handler并且进入到这个handler的ServeHTTP mux.handler(r).ServeHTTP(w, r)11 选择handler A 判断是否有路由能满足这个request循环遍历ServeMux的muxEntry B 如果有路由满足调用这个路由handler的ServeHTTP C 如果没有路由满足调用NotFoundHandler的ServeHTTP
表单
表单是一个包含表单元素的区域
通过表单我们可以让客户端于服务器进行数据的交互
表单元素是允许用户在表单中输入信息的元素
form
...
input 元素
...
/form4.1处理表单的输入
package mainimport (fmtlognet/httptext/template
)func sayhelloName(w http.ResponseWriter, r *http.Request) {r.ParseForm()//解析url传递的参数对于POST则解析相应包的主体//如果没有调用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(value:, v)}fmt.Fprintln(w, Hello astaxie!)//这个写入到w的是输出客户端的}
func login(w http.ResponseWriter, r *http.Request) {fmt.Println(method:, r.Method) //获取请求的方法if r.Method GET {t, _ : template.ParseFiles(login.gtpl)log.Println(t.Execute(w,nil))}else{//请求的要么是登录数据要么是执行登录的逻辑判断//如查询数据库验证登录信息等fmt.Println(username:,r.Form[username])fmt.Println(password:,r.Form[password])}
}func main() {http.HandleFunc(/,sayhelloName) // 设置访问的路由http.HandleFunc(/login,login)err : http.ListenAndServe(9090,nil)if err!nil{log.Fatal(ListenAndServe:,err);}
}4.2验证表单的输入
如何在服务器端验证我们从一个表单元素中得到了一个值。
//如果username是map的值必须通过这种形式
if len(r.Form[username][0])0{//为空的处理
}我们通过r.Form.Get()来获取单个值
只能是数字
如果想要确保一个表单输入框中获取的只能是数字
getint,err:strconv.Atoi(r.Form.Get(age))
if err!nil{//数字转化出错了那么可能就不是数字
}
//接下来就可以判断这个数字的大小范围了
if getint 100 {//太大了
}只能是中文
于中文我们目前有两种方式来验证可以使用 unicode 包提供的 func Is(rangeTab *RangeTable, r rune) bool 来验证也可以使用正则方式来验证这里使用最简单的正则方式如下代码所示
if m, _ : regexp.MatchString(^\\p{Han}$, r.Form.Get(realname)); !m {return false
}英文
if m, _ : regexp.MatchString(^[a-zA-Z]$, r.Form.Get(engname)); !m {return false
}电子邮箱地址
if m, _ : regexp.MatchString(^([\w\.\_]{2,10})(\w{1,}).([a-z]{2,4})$, r.Form.Get(email)); !m {fmt.Println(no)
}else{fmt.Println(yes)
}手机号码
if m, _ : regexp.MatchString(^(1[3|4|5|8][0-9]\d{4,8})$, r.Form.Get(mobile)); !m {return false
}下拉菜单
想要知道元素生成的下拉菜单中是否有被选中的项目
假设我们的select是这样一些元素
select namefruit
option valueappleapple/option
option valuepearpear/option
option valuebananabanana/option
/selectslice:[]string{apple,pear,banana}
v : r.Form.Get(fruit)
for _, item : range slice {if item v {return true}
}
return false单选按钮
当我们需要判断一个单选按钮是否选中的时候
input typeradio namegender value1男
input typeradio namegender value2女和下拉菜单的做法一样
slice:[]string{1,2}
for _, v : range slice {if v r.Form.Get(gender) {return true}
}
return false复选框
就是多选的
input typecheckbox nameinterest valuefootball足球
input typecheckbox nameinterest valuebasketball篮球
input typecheckbox nameinterest valuetennis网球slice:[]string{football,basketball,tennis}
a:Slice_diff(r.Form[interest],slice)
if a nil{return true
}
return falsefunc Slice_diff(slice1, slice2 []interface{}) (diffslice []interface{}) {for _, v : range slice1 {if !In_slice(v, slice2) {diffslice append(diffslice, v)}}return
}日期和时间
你想确定日期时间是否有效
t : time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
fmt.Printf(Go launched at %s\n, t.Local())身份证号码
身份证有十五位和十八位
//验证15位身份证15位的是全部数字
if m, _ : regexp.MatchString(^(\d{15})$, r.Form.Get(usercard)); !m {return false
}
//验证18位身份证18位前17位为数字最后一位是校验位可能为数字或字符X。
if m, _ : regexp.MatchString(^(\d{17})([0-9]|X)$, r.Form.Get(usercard)); !m {return false
}4.3预防跨站脚本
我们的网站包含了大量的动态内容以提高用户体验比过去要复杂的多所谓动态内容就是根据用户环境的需要Web应用程序能够输出相应的内容 。
对XSS跨站脚本攻击最佳的防护应该结合以下两种方法
1.对所有输入数据进行检验
2.进行适当的处理防止任何已经成功注入的脚本在浏览器端运行
HTML转义
HTML 转义是指将 HTML 特殊字符转换为对应的 HTML 实体以防止恶意用户插入 HTML 或 JavaScript 代码导致的安全漏洞。HTML/template 包提供了几个函数来帮助进行 HTML 转义。
在 web 应用程序中如果允许用户输入的内容直接嵌入到 HTML 页面中而没有经过转义处理就可能会导致安全漏洞。这种情况被称为跨站脚本攻击Cross-Site ScriptingXSS。
XSS 攻击的原理是攻击者向网页中注入恶意的客户端脚本当其他用户访问包含恶意脚本的页面时这些脚本就会在用户浏览器中执行从而达到攻击的目的例如盗取用户的会话信息、篡改页面内容等。
举个例子如果一个网站允许用户在评论中输入任意内容并且将这些内容直接显示在网页上而不进行转义处理那么攻击者可以在评论中插入恶意的 JavaScript 代码。当其他用户浏览这些评论时恶意脚本就会在其浏览器中执行可能导致用户的账户被盗取或者其他安全问题。
因此对用户输入的内容进行适当的转义处理是防范 XSS 攻击的重要手段之一。HTML 转义函数就是一种常见的转义处理方式它将 HTML 特殊字符转换为对应的 HTML 实体从而使得恶意脚本失效保护了网站的安全。
脚本
脚本Script是一种用于自动化和控制计算机程序行为的编程语言。脚本通常以文本形式编写并且可以被解释器直接执行而不需要预先编译成二进制代码。脚本语言通常用于快速编写小型程序或自动化任务如网页交互、系统管理、数据处理等。
在 web 开发中脚本通常指的是客户端脚本Client-side Script它是嵌入在网页中的脚本代码由浏览器解释执行。常见的客户端脚本语言包括 JavaScript、VBScript 等。这些脚本可以在用户浏览网页时在浏览器中执行用于实现网页的交互功能、动态效果等。
举个例子当你在网页上点击一个按钮触发了一些动作或者弹出了一个对话框很可能是因为网页中嵌入了 JavaScript 脚本它在用户与网页交互时被触发执行。
然而如果恶意用户能够将自己的恶意脚本注入到网页中并成功执行就可能导致安全问题比如跨站脚本攻击XSS或其他类型的安全漏洞。因此开发者需要注意对用户输入数据进行适当的过滤和转义以防止这类安全风险的发生。
MD5Message Digest Algorithm 5是一种常用的哈希函数用于将任意长度的消息或数据转换为固定长度的哈希值通常为128位的二进制字符串或者32个十六进制字符。
MD5
MD5 哈希对象通常在计算机安全、数据完整性验证等领域中使用其主要用途包括
数据完整性验证** MD5 哈希函数可以将任意长度的数据转换为固定长度的哈希值。通过对原始数据进行哈希处理可以生成一个唯一的标识符称为哈希值。如果数据的任何部分发生了改变其哈希值也会发生变化。因此可以通过比较数据的哈希值来验证数据的完整性确保数据在传输或存储过程中没有被篡改。
package mainimport (crypto/md5fmthtml/templateiolognet/httpstrconvstringstime
)func sayhelloName(w http.ResponseWriter, r *http.Request) {r.ParseForm() //解析url传递的参数对于POST则解析响应包的主体request body//注意:如果没有调用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 login(w http.ResponseWriter, r *http.Request) {fmt.Println(method:, r.Method)if r.Method GET {//如果时GET请求就返回登录页面crutime : time.Now().Unix()//获取当前时间戳(当前时刻)h : md5.New()//创建一个MD5MD5对象可以保证这个时间戳的安全性io.WriteString(h, strconv.FormatInt(crutime, 10))//将当前时间戳转换成字符串并写入哈希对象htoken : fmt.Sprintf(%x, h.Sum(nil))//fmt.Sprintf用来返回格式化的字符串//计算哈希值并将其格式化为十六进制字符串t, _ : template.ParseFiles(vote.html)//解析模板文件并赋值给tt.Execute(w, token)//将token格式化的字符串渲染到模板中//发送给客户端} else {//如果时POST就需要处理 用户提交的登陆数据r.ParseForm()//解析表单数据token : r.Form.Get(token)if token ! {//验证token的合法性} else {//不存在token则报错}fmt.Println(username length:, len(r.Form[username][0]))fmt.Println(username:, template.HTMLEscapeString(r.Form.Get((username))))//打印用户名并适应HTML转义fmt.Println(password:, template.HTMLEscapeString(r.Form.Get(password)))template.HTMLEscape(w, []byte(r.Form.Get(username))) //将用户名转移之后输出到客户端}
}
func main() {http.HandleFunc(/, sayhelloName) //设置访问的路由http.HandleFunc(/login, login) //设置访问的路由err : http.ListenAndServe(:9090, nil) //设置监听的端口if err ! nil {log.Fatal(ListenAndServe: , err)}
}
4.5处理文件上传
要使表单能够上传文件
我们要添加form的enctype属性enctype属性有三种情况
application/x-www-form-urlencoded 表示在发送前编码所有字符默认multipart/form-data 不对字符编码。在使用包含文件上传控件的表单时必须使用该值。text/plain 空格转换为 加号但不对特殊字符编码。
package mainimport (crypto/md5fmthtml/templateionet/httposstrconvtime
)func upload(w http.ResponseWriter, r *http.Request) {fmt.Println(method, r.Method) //获取请求的方法if r.Method GET {crutime : time.Now().Unix()h : md5.New()io.WriteString(h, strconv.FormatInt(crutime, 10))token : fmt.Sprintf(%x, h.Sum(nil))t, _ : template.ParseFiles(upload.html)t.Execute(w, token)} else {r.ParseMultipartForm(32 20)//限制最大大小为32MBfile, handler, err : r.FormFile(uploadfile)//文件handler是multipart.FileHeader//里面存储了如下结构信息/*type FileHeader struct{Filename stringHeader textproto.MIMEHeader}*/if err ! nil {fmt.Println(err)return}defer file.Close()fmt.Fprintf(w, %v, handler.Header)f, err : os.OpenFile(./test/handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)if err ! nil {fmt.Println(err)return}defer f.Close()io.Copy(f, file)}
}
!DOCTYPE html
html
headmeta charsetUTF-8title上传文件/title
/head
body
form enctypemultipart/form-data action /upload method postinput type file name uploadfile /input type hidden name token value{{.}}/input type submit valueupload
/form
/body
/html上传文件主要三步处理
1.表单中增加enctype multipart/from-data
2.服务器端调用r.ParseMultipartForm,把上传的文件存储在内存和临时文件中
3.使用r.FormFile获取文件句柄然后对文件进行存储等处理
5 访问数据库
5.1database/sql接口
什么是数据库驱动
数据库驱动是一种API
用于连接应用程序与数据库之间的通信
可以通过数据库驱动对数据库完成相应的查询插入和更新
但是Go没有数据库驱动未开发着定义了一些标准接口口开发者可以根据定义的接口来开发相应的数据库驱动
sql.Register
用来注册数据库驱动会实现init函数在init里面调用Register(name string ,driver driver.Driver)完成本驱动的注册
在database/sql内部通过一个map来存储用户定义的相应驱动
var drivres make(map[string]driver.Driver)我们可以通过这个函数同时注册多个数据库驱动只要不重复 import (database/sql_ github.com/mattn/go-sqlite3/*_是go设计的一个巧妙的地方它作为一个占位符。然后我们引入后面的包但是不直接使用这个包中定义的资源/*原因就是我们在引入一个包的时候会init函数初始化而init函数又恰好注册了这个数据库驱动*/*/)
driver.Driver
Driver是一个数据库驱动的接口他定义了一个methodOpen(name string),这个方法返回一个 数据库的Conn接口
type Driver interface{
Open(name string)(Conn,error)
}返回的Conn只能用于进行一次goroutine操作
...
go goroutineA (Conn) //执行查询操作
go goroutineB (Conn) //执行插入操作
...driver.Conn
Conn是一个数据库连接的接口定义它定义了一系列方法这个Conn只能应用在一个goroutine里面
type Conn interface{Prepare(query string) (Stmt,error)//返回与当前连接相关的Sql的准备状态可以进行查询、删除操作Close() error//关闭当前连接释放资源Begin() (Tx,error)//Tx是代表事务的通过它可以对事务进行回滚和递交
}driver.Stmt
Stmt是一种准备好的状态和Conn相关联而且只能应用于一个goroutine中不能应用于多个goroutine
type Stmt interface{
Close() error//Close函数关闭当前的连接状态
NumInput() int//这个返回的是参数个数当返回0 数据库驱动就会只能检查调用者的参数。当数据库驱动包不知道预留参数的时候返回-1
Exec(args []Value) (Result,error)//用来执行Prepare准备好的squl传入参数执行update/insert等操作返回Result数据
Query(args []Value) (Rows,error)//也是执行操作执行select操作返回Rows操作集
}driver.Tx
事务处理一般就两个过程递交或者回滚。数据库驱动里面也只需要实现这两个函数就可以
type Tx interface{
Commit() error
Rollback() error
}driver.Execer
这是一个Conn可选择实现的接口
type Execer interface{Exec(query string,args []Value) (Result,error)
}如果这个接口没有定义那么在调用DB.Exec,就会首先调用Prepare返回Stmt,然后执行Stmt的Exec然后关闭Stmt
driver.Result
这个是执行Update/Insert等操作的结果接口定义
type Result interface{
LastInsertId() (int64,error)//返回由数据库执行插入操作得到的自增ID号
RowAffected() (int64,error)//返回query操作影响的数据条目数
}driver.Rows
Rows是执行查询返回的结果集接口定义
type Rows interface{Columns() []string//返回查询数据库表的字段信息//不是返回表的所有字段Close() error//关闭Rows迭代器Next(dest []Value) error//用来返回下一条数据把数据传给dest//dest里面的数据必须是driver.Value的值除了string//数据中的string必须要转换成[]byte
}driver.RowsAffected
是int64的别名
实现了Result接口
type RowAffected int64
func (RowsAffected) LastInsertId() (int64,error)
func (v RowsAffected) RowsAffected() (int64,error)driver.Value 是一个空接口可以容纳任何数据 type Value interface{}Value是驱动需要操作的value
Value只能是下面的任意一种
int64
float64
bool
[]byte
string [*]除了Rows.Next返回的不能是string.
time.Time
nildriver.ValueConverter
将一个普通的值转变为value
type ValueConverter interface{ConverterValue(v interface{}) (value,error)
}ValueConverter有很多好处
转化driver.value到数据库相应的字段把数据库查询结果转化成driver.Value值在scan函数里面如何把driver.Value值转化成用户定义的值
driver.Valuer
Valuer接口定义了返回一个driver.Value的方式
type Valuer interface{
Value() (Value,error)
}很多类型都实现了这个Value方法用来自身与driver.Value的转化
一个驱动只要实现了这些接口就能完成增删查改等基本操作
database/sql
提供了一个更高阶的方法用以简化数据库操作同时内部还建议性地实现了一个conn pool
type DB struct{
driver driver.Driver
dsn string
mu sync.Mutex
freeConn []driver.Conn
closed bool
}Open函数返回的是DB对象里面有一个freeConn,它就是那个简易的连接池。
Web 应用程序的一部分
我们可以看到Web应用程序时执行以下操作的一段程序
1.通过HTTP发送请求
2.处理HTTP请求信息并执行必要的工作
3.生成HTML并在HTTP的相应消息中返回它
Web应用程序有两个不同的部分处理程序和模板引擎
处理程序接收并处理从客户端发送的HTTP请求
处理程序是控制器也是模型
我们需要了解一下MVC模式
模型-视图-控制器模式是一种用于编写Web应用的流行模式
模型是底层数据的表示
视图是模型对用户的可视化
控制器通过控制用户的输入来修改模型
请求格式通常是应用程序本身的特权
格式
http:///?
func main() {//首先创建一个多路复用器用来重定向mux : http.NewServeMux()//创建一个文件服务器用于提供静态文件的服务接收http.FileSystem接口类型的参数//返回http.Handler类型的对象//http.Dir(/public)表示文件服务器的根目录为/public目录files : http.FileServer(http.Dir(/public))mux.Handle(/static/, http.StripPrefix(/static/, files))mux.HandleFunc(/, index)server : http.Server{Addr: 0.0.0.0:8080,Handler: mux,}server.ListenAndServe()
}
files : http.FileServer(http.Dir(/public))
-http.FileServer( a http.FileSystem)
所以说http.Dir(“/public”) 会返回一个实现了http.FileSystem的对象这个对象是一个指定路径的文件系统
表示文件服务器的根目录是当前文件系统中的”/public“目录
当客户向服务端请求静态文件FileServer get the information and get into FileSystem.And Then got into the “/public” to find if the file exit
func index(w http.ResponseWriter, r *http.Request) {files : []string{templates/layout.html,templates/navbar.html,templates/index.html,}templates : template.Must(template.ParseFiles(files...))threads, err : data.Threads(); if err nil {templates.ExecuteTemplate(w, layout, threads)}
}3.73.8 3.10 3.14
HTML的基本概念和知识点
1.HTML
是一个用于创建网页结构的标记语言由一系列标签组成用于描述页面的结构和内容
2.HTML标签
标签格式
eg :xzc就是标签的名称
3.HTML元素
通常成对出现
比如开始标签和结束标签 表示段落的开始
表示段落的结束
开始标签和结束标签之间的内容就是该元素的内容
4.HTML属性
属性位于开始标签内部并且具有名称和值
eg 5.常用HTML标签 -
用来定义标签
用来定义段落
用来创建链接
6.HTML文档结构
HTML文档通常由!DOCTYPE html声明、html元素、head元素和body元素组成。
!DOCTYPE html声明用于指定文档类型
html元素是文档的根元素
head元素包含了文档的元信息如标题、样式表和脚本等而body元素包含了页面的可见内容。
7.HTML注释
注释以 结尾
中国 3.创建css文件在css文件中编写并在HTML中引进来1.在head中引用css
引用方式head中添加link hrefcss文件地址 relstylesheet!DOCTYPE html
html langen
headmeta charsetUTF-8meta http-equivX-UA-Compatible contentIEedgemeta nameviewport contentwidthdevice-width, initial-scale1.0titleDocument/titlelink hrefcss/01.css relstylesheet/
/head
bodyh1CSS使用方法3在head中使用linked/h1
/body
/html
2.在style中引用(style位于head和body中间)
引用方式style中添加import url(对应CSS文件地址)!DOCTYPE html
html langen
headmeta charsetUTF-8meta http-equivX-UA-Compatible contentIEedgemeta nameviewport contentwidthdevice-width, initial-scale1.0titleDocument/title
/head
bodyh1CSS使用方法3在style中使用 import url(地址) /h1
/body
/html
选择器
1.元素(标签)选择器
!DOCTYPE html
html langen
headmeta charsetUTF-8meta http-equivX-UA-Compatible contentIEedgemeta nameviewport contentwidthdevice-width, initial-scale1.0titleDocument/title
/head
style/*所有p标签字体都会变成红色(也可以有其它格式设置本处以颜色红举例)标签选择器会选择所有对应的标签即p标签*/p{color: red;}
/style
bodyp段落1/pp段落2/pp段落3/pp段落4/p
/body
/html
2.类选择器
!DOCTYPE html
html langen
headmeta charsetUTF-8meta http-equivX-UA-Compatible contentIEedgemeta nameviewport contentwidthdevice-width, initial-scale1.0titleDocument/title
/head
style/* 标签里面加上class名字.class名字 指定的会变成红色即段落2以红色显示(也可以有其它格式设置本处以颜色红举例)*/.p2{color: red;}
/style
bodyp段落1/pp classp2段落2/pp段落3/pp段落4/p
/body
/html
3.id选择器
!DOCTYPE html
html langen
headmeta charsetUTF-8meta http-equivX-UA-Compatible contentIEedgemeta nameviewport contentwidthdevice-width, initial-scale1.0titleDocument/title
/head
style/* 标签里面加上id名字# id名字 指定的会变成红色即段落2以红色显示(也可以有其它格式设置本处以颜色红举例)*/#p2{color: red;}
/style
bodyp段落1/pp idp2段落2/pp段落3/pp段落4/p
/body
/html
5.选择器组
!DOCTYPE html
html langen
headmeta charsetUTF-8meta http-equivX-UA-Compatible contentIEedgemeta nameviewport contentwidthdevice-width, initial-scale1.0titleDocument/title
/head
style/* 如果两个选择器内部设置相同那么可以合并两个选择器中间用逗号隔开*/.p2,#p3{color: red;}
/style
bodyp段落1/pp classp2段落2/pp idp3段落3/pp段落4/p
/body
/html
6.派生选择器
!DOCTYPE html
html langen
headmeta charsetUTF-8meta http-equivX-UA-Compatible contentIEedgemeta nameviewport contentwidthdevice-width, initial-scale1.0titleDocument/title
/head
style/* div 标签 所有的标签都会变红此处以变红为例当然也可有有其它设置*/div p{color: red;}/style
bodypp1/ppp2/ppp3/ppp4/ppp5/pspanpp6/pspanpp7/p/span/spanpp8/p
/body
/html
!DOCTYPE html
html langen
headmeta charsetUTF-8meta http-equivX-UA-Compatible contentIEedgemeta nameviewport contentwidthdevice-width, initial-scale1.0titleDocument/title
/head
style/* div 标签 只有最外层的标签都会变红此处以变红为例当然也可有有其它设置*/div p{color: red;}/style
bodypp1/ppp2/ppp3/ppp4/ppp5/pspanpp6/pspanpp7/p/span/spanpp8/p
/body
/html
伪类
!DOCTYPE html
html langen
headmeta charsetUTF-8meta http-equivX-UA-Compatible contentIEedgemeta nameviewport contentwidthdevice-width, initial-scale1.0titleDocument/title
/head
style/* 未点击百度一下之前的颜色 */a:link{color:rgb(8, 7, 7)}/* 鼠标放在百度一下上面的样子 */a:hover{font-size: 50px;}/* 访问过百度一下的颜色 */a:visited{color: rgb(161, 15, 15);}/* 鼠标点住百度一下没松开时候的颜色 */a:active{color: lawngreen;}
/style
bodya hrefhttps://www.baidu.com百度一下/a
/body
/html
属性
1.文本属性
font-family:设置文本字体
font-size:设置字体尺寸
font-weight:设置字体的粗细
color:字体严肃
text-align:字体对齐方式
line-height:行高
text-decoration:文本装饰
text-indent:文本缩进2.背景属性
background-image:url(图片地址) 设置背景图片
background-color 设置背景颜色
background-position 背景图片的位置
background-repeat 背景的平铺方式——repeat 图片在垂直水平方向上重复
——repeat-x 图片在水平方向上重复
——repeat-y 图片在垂直方向上重复
——no-repeat 图片不重复
background-attachment:fixed 背景图片固定3.列表属性
list-style-type: none没有标记 disc默认标记
circle 标记是实心圆
dquare 标记是实心方块
decimal 标记是数字
javascript基础知识
我们在HTML元素中的
document.write(Hello World) 可以写出“Hello World”
可以插入在部分和部分都可以
引用外部脚本的方法