GO 语言 Web 开发实战一

简介: GO 语言 Web 开发实战一

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 限制

模板

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

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

欢迎点赞,关注,收藏

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

好了,本次就到这里

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

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

相关文章
|
8月前
|
Linux Go iOS开发
Go语言100个实战案例-进阶与部署篇:使用Go打包生成可执行文件
本文详解Go语言打包与跨平台编译技巧,涵盖`go build`命令、多平台构建、二进制优化及资源嵌入(embed),助你将项目编译为无依赖的独立可执行文件,轻松实现高效分发与部署。
1323 162
|
7月前
|
算法 Java Go
【GoGin】(1)上手Go Gin 基于Go语言开发的Web框架,本文介绍了各种路由的配置信息;包含各场景下请求参数的基本传入接收
gin 框架中采用的路优酷是基于httprouter做的是一个高性能的 HTTP 请求路由器,适用于 Go 语言。它的设计目标是提供高效的路由匹配和低内存占用,特别适合需要高性能和简单路由的应用场景。
586 4
|
8月前
|
存储 前端开发 Java
【JAVA】Java 项目实战之 Java Web 在线商城项目开发实战指南
本文介绍基于Java Web的在线商城技术方案与实现,涵盖三层架构设计、MySQL数据库建模及核心功能开发。通过Spring MVC + MyBatis + Thymeleaf实现商品展示、购物车等模块,提供完整代码示例,助力掌握Java Web项目实战技能。(238字)
990 0
|
8月前
|
存储 前端开发 JavaScript
Go语言实战案例-项目实战篇:编写一个轻量级在线聊天室
本文介绍如何用Go语言从零实现一个轻量级在线聊天室,基于WebSocket实现实时通信,支持多人消息广播。涵盖前后端开发、技术选型与功能扩展,助你掌握Go高并发与实时通信核心技术。
884 158
|
9月前
|
数据采集 数据挖掘 测试技术
Go与Python爬虫实战对比:从开发效率到性能瓶颈的深度解析
本文对比了Python与Go在爬虫开发中的特点。Python凭借Scrapy等框架在开发效率和易用性上占优,适合快速开发与中小型项目;而Go凭借高并发和高性能优势,适用于大规模、长期运行的爬虫服务。文章通过代码示例和性能测试,分析了两者在并发能力、错误处理、部署维护等方面的差异,并探讨了未来融合发展的趋势。
929 0
|
8月前
|
存储 JavaScript 安全
Web渗透-XSS漏洞深入及xss-labs靶场实战
XSS(跨站脚本攻击)是常见的Web安全漏洞,通过在网页中注入恶意脚本,窃取用户信息或执行非法操作。本文介绍其原理、分类(反射型、存储型、DOM型)、测试方法及xss-labs靶场实战案例,帮助理解与防御XSS攻击。
2525 1
Web渗透-XSS漏洞深入及xss-labs靶场实战
|
7月前
|
存储 安全 Java
【Golang】(4)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法
结构体可以存储一组不同类型的数据,是一种符合类型。Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个传统OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。
372 2
|
7月前
|
开发框架 前端开发 Go
【GoGin】(0)基于Go的WEB开发框架,GO Gin是什么?怎么启动?本文给你答案
Gin:Go语言编写的Web框架,以更好的性能实现类似Martini框架的APInet/http、Beego:开源的高性能Go语言Web框架、Iris:最快的Go语言Web框架,完备的MVC支持。
605 1
|
9月前
|
负载均衡 监控 Java
微服务稳定性三板斧:熔断、限流与负载均衡全面解析(附 Hystrix-Go 实战代码)
在微服务架构中,高可用与稳定性至关重要。本文详解熔断、限流与负载均衡三大关键技术,结合API网关与Hystrix-Go实战,帮助构建健壮、弹性的微服务系统。
871 1
微服务稳定性三板斧:熔断、限流与负载均衡全面解析(附 Hystrix-Go 实战代码)
|
8月前
|
安全 Linux PHP
Web渗透-命令执行漏洞-及常见靶场检测实战
命令执行漏洞(RCE)指应用程序调用系统命令时,用户可控制输入参数,导致恶意命令被拼接执行,从而危害系统安全。常见于PHP的system、exec等函数。攻击者可通过命令连接符在目标系统上执行任意命令,造成数据泄露或服务瘫痪。漏洞成因包括代码层过滤不严、第三方组件缺陷等。可通过参数过滤、最小权限运行等方式防御。本文还介绍了绕过方式、靶场测试及复现过程。
1736 0