GO 语言 Web 开发实战一

news2024/12/26 10:54:52

xdm,咱今天分享一个 golang web 实战的 demo

go 的 http 包,以前都有或多或多的提到一些,也有一些笔记在我们的历史文章中,今天来一个简单的实战

HTTP 编程 Get

先来一个 小例子,简单的写一个 Get 请求

  • 拿句柄

  • 设置监听地址和端口

  • 进行数据处理

    package main
    
    import (
    	"fmt"
    	"net/http"
    )
    
    func myHandle(w http.ResponseWriter, req *http.Request){
    	defer req.Body.Close()
    	par := req.URL.Query()
    	fmt.Println("par :",par)
    	//回写数据
    	fmt.Fprintln(w,"name",par.Get("name"),"hobby",par.Get("hobby"))
    
    }
    
    // server 端
    func main() {
    
    	http.HandleFunc("/", myHandle)
    
    	err := http.ListenAndServe("0.0.0.0:9999", nil)
    	if err != nil{
    		fmt.Printf("ListenAndServe err : %v",err)
    		return
    	}
    
    }
    
    

上述的代码比较简单,就是一个简单的 http get 请求 , 主要处理数据的是 myHandle 函数

Client 客户端 实现方法 get

  • client.go

    • get方法、post方法、patch方法、head方法、put方法等等,用法基本一致
    • 设置url
    • get (或者其他方法)方法请求 url
    • 处理数据
    package main
    
    import (
    	"fmt"
    	"io/ioutil"
    	"net/http"
    	"net/url"
    )
    
    //httpserver 端
    func main() {
    
    	//1.处理请求参数
    	params := url.Values{}
    	params.Set("name", "xiaomotong")
    	params.Set("hobby", "乒乓球")
    
    	//2.设置请求URL
    	rawUrl := "http://127.0.0.1:9999"
    	reqURL, err := url.ParseRequestURI(rawUrl)
    	if err != nil {
    		fmt.Printf("url.ParseRequestURI() 函数执行错误,错误为:%v\n", err)
    		return
    	}
    
    	//3.整合请求URL和参数
    	reqURL.RawQuery = params.Encode()
    
    	//4.发送HTTP请求
    	// reqURL.String() String将URL重构为一个合法URL字符串。
    	fmt.Println("Get url:", reqURL.String())
    	resp, err := http.Get(reqURL.String())
    	if err != nil {
    		fmt.Printf("http.Get()函数执行错误,错误为:%v\n", err)
    		return
    	}
    	defer resp.Body.Close()
    
    	//5.一次性读取响应的所有内容
    	body, err := ioutil.ReadAll(resp.Body)
    
    	if err != nil {
    		fmt.Printf("ioutil.ReadAll()函数执行出错,错误为:%v\n", err)
    		return
    	}
    
    	fmt.Println("Response: ", string(body))
    }
    
    

上述编码中有使用到 reqURL.RawQuery = params.Encode()

Encode 方法将请求参数编码为 url 编码格式 (“a=123&b=345”),编码时会以键进行排序

常见状态码

  • http.StatusContinue = 100
  • http.StatusOK = 200
  • http.StatusFound = 302
  • http.StatusBadRequest = 400
  • http.StatusUnauthorized = 401
  • http.StatusForbidden = 403
  • http.StatusNotFound = 404
  • http.StatusInternalServerError = 500

HTTP 编程 Post 方法

  • 编写 server 代码 server.go

  • 设置句柄

  • 设置监听地址和端口

  • 处理相应数据

    package main
    
    import (
    	"fmt"
    	"io/ioutil"
    	"net/http"
    )
    
    func handPost(w http.ResponseWriter, req *http.Request) {
    	defer req.Body.Close()
    
    	if req.Method == http.MethodPost {
    		b, err := ioutil.ReadAll(req.Body)
    		if err != nil {
    			fmt.Printf("ReadAll err %v", err)
    			return
    		}
    
    		fmt.Println(string(b))
    
    		resp := `{"status":"200 OK"}`
    
    		w.Write([]byte(resp))
    
    		fmt.Println("reponse post func")
    	} else {
    		fmt.Println("can't handle ", req.Method)
    		w.Write([]byte(http.StatusText(http.StatusBadRequest)))
    	}
    }
    
    //post server
    
    func main() {
    
    	http.HandleFunc("/", handPost)
    
    	err := http.ListenAndServe("0.0.0.0:9999", nil)
    	if err != nil {
    		fmt.Printf("ListenAndServe err %v", err)
    		return
    	}
    }
    
    

Client 客户端 实现

  • client.go

    • get方法、post方法、patch方法、head方法、put方法等等,用法基本一致
    • 设置 url
    • post 方法请求
    • 处理数据
    package main
    
    import (
    	"fmt"
    	"io/ioutil"
    	"net/http"
    	"strings"
    )
    
    //post client
    
    func main() {
    
    	reqUrl := "http://127.0.0.1:9999"
    	contentType := "application/json"
    	data := `{"name":"xiaomotong","age":18}`
    
    	resp, err := http.Post(reqUrl, contentType, strings.NewReader(data))
    	if err != nil {
    		fmt.Printf("Post err %v", err)
    		return
    	}
    	defer resp.Body.Close()
    
    	b, err := ioutil.ReadAll(resp.Body)
    	if err != nil {
    		fmt.Printf("ReadAll err %v", err)
    		return
    	}
    
    	fmt.Println(string(b))
    
    }
    
    

上述 post 方法的编码 明显 比 get 方法的编码传参多了很多,我们一起来看看官方源码是如何做的

func Post(url, contentType string, body io.Reader) (resp *Response, err error) {
	return DefaultClient.Post(url, contentType, body)
}
  • url

请求地址

  • contentType

内容的类型,例如 application/json

  • body

具体的请求体内容,此处是 io.Reader 类型的,因此我们传入数据的时候,也需要转成这个类型

表单 form 的处理

既然是 web 相关的实战,表单肯定是一个离不开的话题 , golang 里面当然有对表单的实际处理功能

  • 前面逻辑一样,服务端开启服务,监听端口
  • 每个路由对应这个处理函数
  • 处理函数中 request.ParseForm() 解析表单的具体数据
package main

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

const form = `<html><body><form action="#" method="post" name="bar">
                    <input type="text" name="in"/>
                    <input type="text" name="out"/>
                     <input type="submit" value="Submit"/>
             </form></html></body>`

func HomeServer(w http.ResponseWriter, request *http.Request) {
	 io.WriteString(w, "<h1>/test1 或者/test2</h1>")
}

func SimpleServer(w http.ResponseWriter, request *http.Request) {
	io.WriteString(w, "<h1>hello, xiaomotong</h1>")
}

func FormServer(w http.ResponseWriter, request *http.Request) {
	w.Header().Set("Content-Type", "text/html")
	switch request.Method {
	case "GET":
		io.WriteString(w, form)
	case "POST":
		request.ParseForm()
		fmt.Println("request.Form[in]:", request.Form["in"])
		io.WriteString(w, request.Form["in"][0])
		io.WriteString(w, "\n")
		io.WriteString(w, request.Form["out"][0])
	}
}
func main() {
	http.HandleFunc("/", HomeServer)
	http.HandleFunc("/test1", SimpleServer)
	http.HandleFunc("/test2", FormServer)
	err := http.ListenAndServe(":9999", nil)
	if err != nil {
		fmt.Printf("http.ListenAndServe()函数执行错误,错误为:%v\n", err)
		return
	}
}

上述编码解析表单的逻辑是:

对于 POST、PUT 和P ATCH 请求,它会读取请求体并解析它,作为一个表单,会将结果放入r.PostFormr.Form

请求体 r.Form 中的参数优先于 URL 查询字符串值

先来看看 Request 的结构 ,参数会比较多

type Request struct {
	Method string
	URL *url.URL
   
	.... 此处省略多行 ...
    
	ContentLength int64

	//Form包含解析过的表单数据,包括URL字段的查询参数和PATCH、POST或PUT表单数据。
    //此字段仅在调用 ParseForm 后可用
	Form url.Values

	//PostForm包含来自 PATCH、POST或PUT主体参数的解析表单数据。
    //此字段仅在调用 ParseForm 后可用。
	PostForm url.Values

    //MultipartForm是解析的多部分表单,包括文件上传。
    //该字段仅在调用 parsemmultipartform 后可用。
	MultipartForm *multipart.Form

	Trailer Header
	RemoteAddr string
	RequestURI string
	TLS *tls.ConnectionState
	Cancel <-chan struct{}
	Response *Response
	ctx context.Context
}

下面是具体实现的源码,感兴趣的 xdm 可以打开 goland 看起来

实际处理逻辑在 func parsePostForm(r *Request) (vs url.Values, err error) {

这里需要注意

  • 请求提的大小上限为10MB , 需要注意请求体的大小是否会被 MaxBytesReader 限制

模板

听到 模板 这个名词应该不陌生了吧,很多组件或者语言里面都有模板的概念

感兴趣的可以琢磨一下,我们放在下一篇补充

欢迎点赞,关注,收藏

朋友们,你的支持和鼓励,是我坚持分享,提高质量的动力

好了,本次就到这里

技术是开放的,我们的心态,更应是开放的。拥抱变化,向阳而生,努力向前行。

我是阿兵云原生,欢迎点赞关注收藏,下次见~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/165436.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VMware Workstation 17 Pro的下载和安装

目录 一、下载 二、安装 三、检查网络连接 方式一&#xff08;简便版&#xff09; 方式二&#xff08;麻烦版&#xff09; 一、下载 下载地址&#xff1a; Windows 虚拟机 | Workstation Pro | VMware | CN 1、进入该网址后&#xff0c;往下翻&#xff0c;有两个选项&…

并查集是什么?怎么模拟实现?如何应用?

目录 一、什么是并查集&#xff1f; 二、并查集可以解决哪些问题&#xff1f; 三、并查集的模拟实现 3.1、并查集的定义 3.2、查询两个元素是否是同一个集合 3.3、合并两个集合 3.4、求集合个数 3.5、并查集完整代码 小结 一、什么是并查集&#xff1f; 我们可以想象这…

九、MySQL 常用函数汇总(2)

文章目录一、条件判断函数1.1 IF(expr,v1,v2)函数1.2 IFNULL(v1,v2)函数1.3 CASE函数二、系统信息函数2.1 获取MySQL版本号、连接数和数据库名的函数2.2 获取用户名的函数2.3 获取字符串的字符集和排序方式的函数2.4 获取最后一个自动生成的ID值的函数三、加密函数3.1 加密函数…

东宝商城项目(三)——用户注册功能的实现(后端)

本文是我做项目过程中记录的学习笔记&#xff0c;用于记录项目开发流程&#xff0c;第一次做项目有很多不懂的地方&#xff0c;本文可读性暂时很差。 我目前的学习目标是走完项目开发流程&#xff0c;知道独立开发一个项目并让项目上线需要经历哪些步骤&#xff0c;需要学到哪些…

java.util.ConcurrentModificationException: null异常

创作背景&#xff1a;在加强for循环中使用了remove操作 原因&#xff1a; 在官方文档中ConcurrentModificationException的介绍如下&#xff1a; public class ConcurrentModificationException extends RuntimeException 某个线程在 Collection 上进行遍历时&#xff0c;通…

Spring入门-IOC/DI注解管理与整合mybatis及Junit(2)

1&#xff0c;核心容器 前面已经完成bean与依赖注入的相关知识学习&#xff0c;接下来我们主要学习的是IOC容器中的核心容器。 这里所说的核心容器&#xff0c;大家可以把它简单的理解为ApplicationContext&#xff0c;前面虽然已经用到过&#xff0c;但是并没有系统的学习&a…

1.15日报

完成font.css global.css login.vue request.js 今天完成了前端与后端的联通&#xff0c;并成功响应请求。返回登录成功欣喜。 遇到的问题&#xff1a; 我的body设置了&#xff1a; margin:0; padding:0; 但是页面四周还有白色留边。原因&#xff1a;body设置无边框了&a…

用Scipy理解Gamma函数

文章目录Gamma函数对数Gamma函数复数域的Gamma函数Gamma函数 Γ\GammaΓ函数是阶乘的解析延拓&#xff0c;在概率论中非常常见&#xff0c;例如Gamma分布表示某个事件在某个时刻发生第nnn次的概率&#xff1a;Gamma分布详解 Γ\GammaΓ函数显含在Γ\GammaΓ分布中&#xff0c;其…

linux基本功系列之pwd命令实战

本文目录 文章目录一. pwd命令介绍二. 语法格式及常用选项2.1 语法格式2.2 常用参数三. 参考案例3.1 显示所在目录的完整路径3.2 显示符号链接的路径 -P 参数3.3 查看上一次所在的工作目录3.4 查看PWD的版本四. pwd的命令类型总结前言&#x1f680;&#x1f680;&#x1f680; …

7、redis数据库jedis省份缓存案例

Redis 1. 概念&#xff1a; redis是一款高性能的NOSQL系列的非关系型数据库 1.1.什么是NOSQL NoSQL(NoSQL Not Only SQL)&#xff0c;意即“不仅仅是SQL”&#xff0c;是一项全新的数据库理念&#xff0c;泛指非关系型的数据库。 随着互联网web2.0网站的兴起…

IO流练习(三)

1.编程题 Homework01.java (1)在判断e盘下是否有文件夹mytemp,如果没有就创建mytemp (2)在e:\\mytemp目录下&#xff0c;创建文件hello.txt (3)如果hello.txt已经存在&#xff0c;提示该文件已经存在&#xff0c;就不要再重复创建了。 &#xff08;4&#xff09;并且在hello.tx…

Java加解密(八)数字证书

目录数字证书1 定义2 证书组成结构3 公钥基础设施&#xff08;PKI&#xff09;3.1 PKI的组成3.2 PKI的相关标准3.3 信任模型4 证书的应用场景5 证书链6 生成证书6.1 通过CA生成可信证书6.1.1 国际权威认证机构6.1.2 生成CSR6.1.2.1 使用XCA生成CSR6.1.2.2 使用OpenSSL生成CSR6.…

每日一题-力扣(leetcode)2059. 转化数字的最小运算数

传送门 题目描述 给你一个下标从 0 开始的整数数组 nums &#xff0c;该数组由 互不相同 的数字组成。另给你两个整数 start 和 goal 。 整数 x 的值最开始设为 start &#xff0c;你打算执行一些运算使 x 转化为 goal 。你可以对数字 x 重复执行下述运算&#xff1a; 如果…

AtCoder Beginner Contest 284解题报告(A-D)

A - Sequence of Strings Problem Statement You are given N strings S1​,S2​,…,SN​ in this order. Print SN​,SN−1​,…,S1​ in this order. Constraints 1≤N≤10N is an integer.Si​ is a string of length between 1 and 10, inclusive, consisting of lowe…

【Redis】Redis实现分布式锁

【Redis】Redis实现分布式锁 文章目录【Redis】Redis实现分布式锁1. 分布式锁概念2. 为什么要实现分布式锁2.1 并发安全问题3. 分布式锁的实现方案3.1 Redis实现分布式锁3.1.1 定义分布锁接口和类3.1.2 编写lua脚本3.1.3 使用线程锁3.1.4 总结在实现分布式锁之前&#xff0c;首…

aardio - 升级bindConfig函数,支持多属性和多子组件

一、需求分析 aardio的 winform.bindConfig() 函数&#xff0c;绑定后&#xff0c;一个组件&#xff0c;只能保存一个属性。 有时候需要同时保存多个属性&#xff0c;比如一个comobox组件&#xff0c;需要保存项目列表&#xff0c;同时保存当前选中的项目索引。当前这个bindC…

代码随想录算法训练营第十八天二叉树 java : .106 从中序与后序遍历序列构造二叉树113. 路径总和ii 112 路径总和 513.找树左下角的值

文章目录前言LeetCode 513.找树左下角的值题目讲解思路那么如何找最左边的呢&#xff1f;Leetcode 112 路径总和题目讲解LeetCode 113. 路径总和ii题目讲解Leetcode 106 从中序与后序遍历序列构造二叉树题目讲解前言 人的不幸在于他们不想走自己的那条路&#xff0c;总想走别人…

大数据必学Java基础(一百二十四):Maven的常见插件

文章目录 Maven的常见插件 一、编辑器插件 二、资源拷贝插件 三、tomcat插件 Maven的常见插件

Dubbo 服务暴露

Dubbo 服务暴露 1. 服务暴露时序图 2. 源码分析 DubboBootstrap.exportServices 从配置管理器中获取到所有的ServiceConfig实例&#xff0c;遍历&#xff0c;然后一个一个的暴露。 ServiceConfig.export 如果DubboBootstrap为空&#xff0c;也就没有初始化&#xff0c;就初…

猴子都能看懂的噪声(noise)专题

背景 除了生成各种奇形怪状与自然景观&#xff0c;噪声也有其他美妙的用途&#xff01; 工作原因&#xff0c;经常接触与噪声相关的画面效果&#xff08;火焰啊&#xff0c;画面扰动啊之类的&#xff09;&#xff0c;做的时候一知半解&#xff0c;傻傻分不清楚各种形态的nois…