前言
前面已经介绍了如何基于HTTP协议搭建服务端和客户端。但是明显发现Server与Client是彼此独立的,不能产生交互。本文就主要介绍一下如何通过客户端访问api时传入不同的请求参数,服务端响应得到不同的内容。
正文
HTTP常用的请求方式分别为GET请求和POST请求。下面分别展开介绍:
(1)带参数的GET请求示例
/// ===== Server =====
package main
import (
"encoding/json"
. "fmt"
"net/http"
"time"
)
type NewBaseJsonBean struct {
Code int `json:"code"`
Data interface{} `json:"data"`
Message string `json:"message"`
}
func Task(w http.ResponseWriter, r *http.Request) {
Printf(" ------ here is Task ------- \n")
defer r.Body.Close()
// 解析请求参数
// queryParam := r.URL.Query()
// Println("user is ", queryParam.Get("user")) //get the first value associated with the given key
// Println("pass is ", queryParam.Get("password"))
r.ParseForm()
paramUser, bFound1 := r.Form["user"]
paramPswd, bFound2 := r.Form["password"]
if !(bFound1 && bFound2) {
Println("Query illegal!")
Fprintf(w, "Error! DO NOT ILLEAGEL ACCESS!")
return
}
Printf("User is %s, password is %s\n", paramUser, paramPswd)
// 业务处理
result := NewBaseJsonBean{}
if "hb" == paramUser[0] && "123456" == paramPswd[0] {
result.Code = 0
result.Message = "Login success!"
} else {
result.Code = -1
result.Message = "Login failed! Please check your username or password"
}
// 模拟延时
time.Sleep(time.Second * 1)
// 响应客户端的请求
content, _ := json.Marshal(result)
Fprintf(w, string(content))
}
func main() {
Println("===== This is http server =====")
http.HandleFunc("/get", Task)
err := http.ListenAndServe("127.0.0.1:8090", nil)
if err != nil {
Println("ListenAndServe error! err is ", err.Error())
}
}
/// ===== Client =====
package main
import (
"fmt"
. "fmt"
"io/ioutil"
"net/http"
"net/url"
"time"
)
func main() {
apiUrl := "http://127.0.0.1:8090/get"
// set URL param
data := url.Values{}
data.Set("user", "hb")
//data.Add("user", "bobo")
data.Set("password", "123456")
uri, err := url.ParseRequestURI(apiUrl)
if err != nil {
Println("Parse url failed! err is ", err.Error())
return
}
// URL param encode
Printf("uri.String is '%s'\n", uri.String())
uri.RawQuery = data.Encode()
Printf("uri.param is '%s'\n", uri.RawQuery)
// param, _ := url.ParseQuery(uri.RawQuery)
// Println("param is", param)
// for k, v := range param {
// Println(k, v)
// }
// 模拟一个Get提交请求
resp, err := http.Get(uri.String())
if err != nil {
Println("Get failed! err is ", err.Error())
return
}
// 关闭连接
defer resp.Body.Close()
// 读取报文中所有内容
content, err := ioutil.ReadAll(resp.Body)
if err != nil {
Println("Read from Body failed!, err is ", err.Error())
return
}
// 输出内容
fmt.Println(string(content))
time.Sleep(time.Second * 20)
}
测试方式1:
先启动Server.exe,再启动Client.exe。运行结果:
测试方式2:
启动Server.exe,通过网页访问url。运行结果:
(A)访问http://127.0.0.1:8090/get?user=hb&password=123456 (正确的url)
(B)访问http://127.0.0.1:8090/get?user=hb&password=1234567 (password错误的url)
(C)访问http://127.0.0.1:8090/get?user=hb&passwordr=123456 (字段错误的url)
(2)带参数的POST请求示例
/// ===== Server =====
package main
import (
"encoding/json"
. "fmt"
"io/ioutil"
"net/http"
"time"
)
type NewBaseJsonBean struct {
Code int `json:"code"`
Data interface{} `json:"data"`
Message string `json:"message"`
}
type LoginInfo struct {
User string `json:"user"`
Password string `json:"password"`
}
func Task(w http.ResponseWriter, r *http.Request) {
Printf(" ------ here is Task ------- \n")
defer r.Body.Close()
// 解析请求参数
// // 1.请求类型是application/x-www-form-urlencoded时解析form数据
// r.ParseForm()
// Println("r.PostForm is ", r.PostForm)
// // paramUser := r.PostForm.Get("user")//get the first value associated with the given key
// // paramPswd := r.PostForm.Get("password")
// paramUser, bFound1 := r.Form["user"]
// paramPswd, bFound2 := r.Form["password"]
// if !(bFound1 && bFound2) {
// Println("Query illegal!")
// Fprintf(w, "Error! DO NOT ILLEAGEL ACCESS!")
// return
// }
// Printf("User is %s, password is %s\n", paramUser, paramPswd)
// 业务处理
// result := NewBaseJsonBean{}
// if "hb" == paramUser[0] && "123456" == paramPswd[0] {
// result.Code = 0
// result.Message = "Login success!"
// } else {
// result.Code = -1
// result.Message = "Login failed! Please check your username or password"
// }
// 2.请求类型是application/json时从r.Body中读取数据
param, err := ioutil.ReadAll(r.Body)
if err != nil {
Println("Read from Body failed!, err is ", err.Error())
return
}
Println(string(param))
loginInfo := LoginInfo{}
err = json.Unmarshal(param, &loginInfo)
if err != nil {
Println("Unmarshal failed! err is ", err.Error())
Fprintf(w, "Error! DO NOT ILLEAGEL ACCESS!")
return
}
// 业务处理
result := NewBaseJsonBean{}
if "hb" == loginInfo.User && "123456" == loginInfo.Password {
result.Code = 0
result.Message = "Login success!"
} else {
result.Code = -1
result.Message = "Login failed! Please check your username or password"
}
// 模拟延时
time.Sleep(time.Second * 1)
// 响应客户端的请求
content, _ := json.Marshal(result)
Fprintf(w, string(content))
}
func main() {
Println("===== This is http server =====")
http.HandleFunc("/post", Task)
err := http.ListenAndServe("127.0.0.1:8090", nil)
if err != nil {
Println("ListenAndServe error! err is ", err.Error())
}
}
/// ===== Client =====
package main
import (
"fmt"
. "fmt"
"io/ioutil"
"net/http"
"strings"
"time"
)
func main() {
apiUrl := "http://127.0.0.1:8090/post"
// 表单数据
// contentType := "application/x-www-form-urlencoded"
// data := "user=hb&password=123456&password=123321"
contentType := "application/json"
data := `{"user":"hb","password":"123456"}`
// 模拟一个Post提交请求
resp, err := http.Post(apiUrl, contentType, strings.NewReader(data))
if err != nil {
Println("Post failed! err is ", err.Error())
return
}
// 关闭连接
defer resp.Body.Close()
// 读取报文中所有内容
content, err := ioutil.ReadAll(resp.Body)
if err != nil {
Println("Read from Body failed!, err is ", err.Error())
return
}
// 输出内容
fmt.Println(string(content))
time.Sleep(time.Second * 20)
}
测试:
先启动Server.exe,再启动Client.exe。运行结果:
说明
当请求类型是 application/x-www-form-urlencoded 时,解析表单数据有两种方式,这两种方式是有一定区别的:
(1)方式一
// 请求类型是application/x-www-form-urlencoded时解析form数据
// 方式一:当传入多个同名key时,所有value都能解析到
r.ParseForm()
Println("r.PostForm is ", r.PostForm)
paramUser, bFound1 := r.Form["user"]
paramPswd, bFound2 := r.Form["password"]
if !(bFound1 && bFound2) {
Println("Query illegal!")
Fprintf(w, "Error! DO NOT ILLEAGEL ACCESS!")
return
}
Printf("User is %s, password is %s\n", paramUser, paramPswd)
// 业务处理
result := NewBaseJsonBean{}
if "hb" == paramUser[0] && "123456" == paramPswd[0] { //由于client端传入的是两个同名password,上边解析时两个password都解析到了数组paramPswd,故这里使用时需要下标取值
result.Code = 0
result.Message = "Login success!"
} else {
result.Code = -1
result.Message = "Login failed! Please check your username or password"
}
执行结果:
(2)方式二
// 请求类型是application/x-www-form-urlencoded时解析form数据
// 方式二:当传入多个同名key时,只能解析到第一个value
r.ParseForm()
Println("r.PostForm is ", r.PostForm)
paramUser := r.PostForm.Get("user")
paramPswd := r.PostForm.Get("password")//get the first value associated with the given key
Printf("User is %s, password is %s\n", paramUser, paramPswd)
// 业务处理
result := NewBaseJsonBean{}
if "hb" == paramUser && "123456" == paramPswd {
result.Code = 0
result.Message = "Login success!"
} else {
result.Code = -1
result.Message = "Login failed! Please check your username or password"
}
执行结果:
后言
好了,到这里为止已经介绍完了通过HTTP服务搭建服务端与客户端的完整流程,快开启你愉快的秃头之旅吧~