Go Web编程.基础入门

Posted qq_51102350

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go Web编程.基础入门相关的知识,希望对你有一定的参考价值。

一,反射

反射:计算机在运行时,可以访问,检测和修改它本身状态或行为的一种能力。

reflect包中定义了:

refelect.Type接口:提供类型相关信息
函数定义:

func TypeOf(i interface{}) Type

reflect.Value结构体:可以提供并改变值相关信息
函数定义:

func ValueOf(i interface{}) Value

根据reflect.Value类型的变量,可以使用Interface()方法使其恢复到其接口类型的值

func (v value) Interface() interface{}

基本用法一:

package main

import (
	"fmt"
	"reflect"
)

func main() {
	x := 3.14
	fmt.Println("type:", reflect.TypeOf(x))
	fmt.Println("value", reflect.ValueOf(x))

	v := reflect.ValueOf(x)
	fmt.Println("type:", v.Type())
	fmt.Println("kind is float64:", v.Kind() == reflect.Float64)
	//Value类型提供了Int(),Float()等方法,可以让我们获取存在里面的值
	fmt.Println("value", v.Float())

	t := reflect.TypeOf(x)
	fmt.Println("v", reflect.TypeOf(v))
	fmt.Println("t", reflect.TypeOf(t))

	//从反射对象到接口变量
	i := v.Interface()
	fmt.Println("i:", reflect.TypeOf(i), reflect.ValueOf(i))
}

修改“反射类型对象”:
使用reflect.TypeOf()函数和reflect.ValueOf()函数时,若传递的不是接口变量的指针,则传递的只是一个复制体

注意:

  • 使用CanSet()方法来确定可写性
  • 使用Elem()方法返回指针指向的数据

一些用于修改值的方法的定义:

func (v Value) SetBool(x bool)
func (v Value) SetBytes(x []byte)
func (v Value) SetFloat(x float64)
func (v Value) SetInt(x int64)
func (v Value) SetString(x string)

实例:

package main

import (
	"fmt"
	"reflect"
)

func main() {
	name := "Go Web Program"
	fmt.Println(name)

	v1 := reflect.ValueOf(&name)
	v2 := v1.Elem()
	if v2.CanSet() {
		v2.SetString("hhhhhh")
		fmt.Println(name)
	}
}

二,Hello World

1,服务器端创建

如果想要创建一个Web服务器端,则需要:

  • 调用http.HandleFunc()
  • 调用http.ListenAndServe()

ListenAndServe()函数有两个参数:当前监听的端口号addr和事件处理器handler

Handler接口的定义:

type Handler interface {
	ServeHTTP(ResponseWirter, *Request)
}

只要实现了该接口,就可以实现自己的handler处理器

HandlerFunc()就是一个这样的处理器:

type HandlerFunc func(ResponseWriter, *Request)

func (f HAndlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
	f(w, r)
}
package main

import "net/http"

func SayHello(w http.ResponseWriter, req *http.Request) {
	w.Write([]byte("Hello"))
}

func main() {
	http.HandleFunc("/hello", SayHello)
	http.ListenAndServe(":8080", nil)
}

补充:
在这里插入图片描述

2,HTTPS服务器端

net/http包中提供的启动HTTPS服务的方法:

func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error

certfile:证书文件路径
keyFile:私钥文件路径

创建certifile和keyfile的命令行模板:

$ openssl req -newkey rsa:2048 -nodes -keyout 证书文件名称 -x509 -days 365 -out 私钥文件名称
package main

import (
	"log"
	"net/http"
)

func handle(w http.ResponseWriter, r *http.Request) {
	//记录请求协议
	log.Printf("Got connection: %s", r.Proto)
	//向客户端发送信息
	w.Write([]byte("233333333"))
}

func main() {
	//启动服务器
	serve := &http.Server{Addr: ":8080", Handler: http.HandlerFunc(handle)}
	//用TLS启动服务器
	log.Printf("Serving on https://0.0.0.0:8080")
	log.Fatal(serve.ListenAndServeTLS("server.crt", "server.key"))
}

3,创建简单客户端

Ⅰ,基础知识

在net/http包中提供了一个名为Client的结构体

包含一个默认变量:

var DefaultClient = &Client{}
//net/http包包含的Get()函数
func Get(url string) (resp *Response, err error){
	//注意到这里调用了Client的Get()方法
	return DefaultClient.Get(url)
}

//Client的Get()方法
func (c *Client) Get(url string) (resp *Response, err error){
	req, err := NewRequest("GET", url, nil)
	if err != nil{
		return nil,err
	}
	return c.Do(req)	

//net/http包包含的Post()函数
func Get(url, contentType string,body io.Reader) (resp *Response, err error){
	//注意到这里调用了Client的Post()方法
	return DefaultClient.Post(url, contentType, body)
}

func (c *Client) Post(url, contentType string, body io.Reader) (resp *Response, err error) {
	req, err := NewRequest("POST", url, body)
	if err != nil {
		return nil,err
	}
	req.Header.Set("Content-Type", contentType)
	//用于关闭NewRequest返回的Request
	return c.Do(req)

//NewRequest()是一个通用函数
func NewRequest(method, url string, body ,io.Reader) (*Request, error)
//method是参数请求类型,包括“GET” "POST"等

contentType:请求类型
io.Reader:一个接口

type Reader interface {
    Read(p []byte) (n int, err error)
}

实现了Read方法的都是一个io.Reader
常见的有:

  • strings.Reader
  • bytes.Reader
  • bytes.Buffer
  • bufio.Reader

Ⅱ,创建GET请求

关于io/ioutil包
io/ioutil

package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
)

func main() {
	resp, err := http.Get("https://www.baidu.com/")
	if err != nil {
		fmt.Println("err", err)
	}
	//将html内容赋给closer
	result := resp.Body
	bytes, err := ioutil.ReadAll(result)
	fmt.Println(string(bytes))
}

Ⅲ,创建POST请求

关于strings包:
strings

首先创建一个接收application/json类型的请求的服务端:


package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
)

func postHandler(w http.ResponseWriter,r *http.Request){
	defer r.Body.Close()
	//请求类型是application/json时从r.Body读取数据
	resp,err := ioutil.ReadAll(r.Body)
	if err != nil{
		fmt.Println("hhhhhh")
		return
	}
	fmt.Println(string(resp))
	answer := `{"status": "ok"}`
	laugh := []byte("\\nhhhhh")
	w.Write([]byte(answer))
	w.Write(laugh)
}

func main(){
	serve := &http.Server{Addr: ":2021",Handler: http.HandlerFunc(postHandler)}
	serve.ListenAndServe()
}

发出post请求:

package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
)

// net/http post demo

func main() {
	url := "http://127.0.0.1:2021"

	//使用json的方式请求
	contentType := "application/json"
	data := `{"name":"hwc","age":19}`
	resp, err := http.Post(url,contentType,strings.NewReader(data))
	if err != nil{
		fmt.Println("so sad it is")
	}
	result, err := ioutil.ReadAll(resp.Body)
	if err != nil{
		fmt.Println("so sad it is")
	}
	fmt.Println(string(result))
}

添加headers:

package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
)

// net/http post demo

func main() {
	client := &http.Client{}
	url := "http://127.0.0.1:2021"
	useragent := "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0"

	//使用json的方式请求
	contentType := "application/json"
	data := `{"name":"hwc","age":19}`

	req, err := http.NewRequest("POST",url,strings.NewReader(data))
	if err != nil{
		fmt.Println("it is all too late")
	}
	//添加headers
	req.Header.Add("Content-Type",contentType)
	req.Header.Add("User-Agent",useragent)
	
	resp,err := client.Do(req)
	if err != nil{
		fmt.Println("so sad it is")
	}
	result, err := ioutil.ReadAll(resp.Body)
	if err != nil{
		fmt.Println("so sad it is")
	}
	fmt.Println(string(result))
}

4,html/template包

该包实现了数据驱动的模板,Go语言中输出html的情景应使用这个包

动态网页:网页的布局和样式大致一样,但展示的内容不一样的网页

在一些前后段不分离的web架构中,我们需要在后端将一些数据渲染到HTML文档中,从而实现动态网页的效果。

模板的渲染机制可以理解为用文本替换操作:用数据取替换事先准备好的HTML模板中的标记

该包的作用机制:

  • 模板文件通常为.tmpl和.tpl后缀,必须使用UTF-8编码
  • 模板文件使用{{和}}包裹/标识需要传入的数据
  • 模板文件中除了{{}}包裹的内容,其它内容都不做修改原样输出

模板引擎的使用:

  • 定义模板文件,通俗点说,写一个html文件
  • 解析模板文件

html/template提供了以下方法来解析模板文件,获得模板对象

//下面是这些方法的定义

//创建模板对象,添加模板名称
func New(name string) *Template

//创建模板对象,解析模板内容
func (t *Template) Parse(sec string) (*Template, error)

//解析模板文件,返回模板对象
func ParseFiles(filenames ...string) (*Template, error)

//批量解析文件
func ParseGlob(pattern string) (*Template, error)
//可使用该函数来进行正则匹配
  • 渲染模板文件
func (t *Template) Execute(wr io.Writer, data interface{}) error {}
func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error {}

注意:使用ParseFiles()函数可以一次加载多个模板,此时只能使用ExecuteTemplate()方法指定模板名称来执行数据融合

例:
tmpl文件:

<!DOCTYPE html>
<html>
<head>
  <meta charset = "utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>life at age 19</title>
</head>
<body>
    <h1>Foreign Time</h1>
    <p>but i'm too young too late too young to find what I've been looking for</p>
    <p>today is {{.DayTime}}</p>
    <p>my emotion is {{.Emotion}}</p>
    <p>{{.Log}}</p>
</body>
</html>

模板语法都包含在{{}}之间,其中{{.}}的点表示当前对象
传入结构体时,可依据“."来访问结构体的对应字段
.go文件:

package main

import (
	"fmt"
	"html/template"
	"net/http"
)

type dayLog struct {
	DayTime string
	Emotion string
	Log string
}

func testHandleFunc(w http.ResponseWriter,r *http.Request){
	//1,parse template
	tpl, err := template.ParseFiles("./test.tmpl")
	if err != nil{
		fmt.Println("sth wrong",err)
	}
	//2,execute template
	var dayLog1 dayLog
	dayLog1.DayTime = "2021.6.20"
	dayLog1.Emotion = "a little blue"
	dayLog1.Log = "universities painted life that has come before me"
	err = tpl.Execute(w,dayLog1)
	if err != nil{
		fmt.Println("hhhh",err)
	}
}

func main(){
	serve := &http.Server{Addr: ":2021",Handler: http.HandlerFunc(testHandleFunc)}
	serve.ListenAndServe()
}

三,接收处理Go Web请求

1,接收请求

Ⅰ,ServeMux和DefaultServeMux

ServeMux:一个结构体,可以找出与被请求URL最为匹配的URL,然后调用与之相应的处理器的ServeHTTP()方法来处理请求。

DefaultServeMux:ServeMux的一个实例。服务器默认使用DefaultServeMux来作为ServeMux结构体的实例。

HandleFunc()函数用于为指定URL注册一个处理器。会在内部调用DefaultServeMux对象的对应方法。
http.HandleFunc()函数将处理器注册到多路复用器中,多路复用器可以指定多个处理器。
定义:

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}

实例:

package main

import "net/http"

type handler1 struct {}
type handler2 struct {}

func (h1 *handler1) ServeHTTP(w http.ResponseWriter,r *http.Request){
	w.Write([]byte("hi,handler1"))
}

func (h2 *handler2) ServeHTTP(w http.ResponseWriter,r *http.Request){
	w.Write([]byte("hi,handler2"))
}

func main(){
	h1 := handler1{}
	h2 := handler2{}

	server := http.Server{
		Addr: ":8085",
		Handler: nil,//nil表明服务器使用默认多路复用器DefaultServeMux
	}
	
	//Handle()函数调用的是多路复用器DefaultServeMux.Handle()方法
	//Handle()函数用于指定多个处理器
	http.Handle("/handle1",&h1)
	http.Handle("/handle2",&h2)
	server.ListenAndServe()
}

默认的多路复用气功在实际生产环境中不建议使用。
推荐自定义多路复用器:
不同的URL对应不同处理器

package main

import (
	"log"
	"net/http"
	"time"
)

func indexHandler(w http.ResponseWriter,r *http.Request){
	w.Write([]byte(Go Web编程.基础入门

Go 每日一库之 net/http(基础和中间件)

Go Web 编程入门: 动态模板

Go语言入门

Go Web 编程入门:验证器

Go语言入门经典|文末赠书