golang表单提交与服务器的交互

ice_201507 · · 6510 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。
网络编程
socket,http,rpc,json处理


前端是Web+移动,后端是Linux(命令行centos)+开源


http编程
服务器和客户端(浏览器也是客户端)
socket编程
rpc编程


------------------------------------------------------------------------------server.go


package main


//服务端开发,特别是Web开发,基本上全是处理HTTP请求的处理。
//根据具体用途分为两种:Web页面开发和API接口开发。
//Web页面开发也完全可以看成是API接口开发,只是它的两个主要部分,
//页面和ajax请求,一个是返回html,另外一个可以返回html,也可以返回其他格式的而已。
//API接口开发是针对有客户端产品而言的。可能是移动设备,可能是PC应用等。
import (
"fmt"
"io"
"net/http"
)


func handler(w http.ResponseWriter, r *http.Request) {
http.SetCookie(w, &http.Cookie{Name: "pa", Value: "dps"})
r.Header.Add("server", "go/1.0")
io.WriteString(w, "myserver is running!")
fmt.Printf("r.header=%+v,r.usr.string=%+v,r.url.path=%+v\n", r.Header, r.URL.String(), r.URL.Path)
r.ParseForm()
d := r.Form
fmt.Println(d)


}


func main() {
http.HandleFunc("/agent", handler)


http.ListenAndServe(":8080", nil)
}




------------------------------------------------------------------------------------get.go
自己写客户端,get请求
代替浏览器的地址栏里输入的所有信息




func main() {
resp, err := http.Get("http://localhost:8080/agent?name=幻刺&password=123456#stuff")
if err != nil {
fmt.Println("resp err=", err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("body err=", err)
}
fmt.Println(string(body))
fmt.Println(resp.StatusCode)
fmt.Println(resp.Proto, resp.ProtoMajor, resp.ProtoMinor)
}




---------------------------------------------------------------------------------post.go
自己写客户端 post请求
post适合提交有密码的信息,因为参数都存储在body中,设置对应的bodytype,对应在浏览器地址栏不会显示


浏览器默认是get请求,向服务器索取数据的一种请求
post是想服务器提交数据的一种请求,要提交的信息位于信息头后面的实体中(数据体),在地址栏看不到。
HTTP协议是以ASCII码传输,建立在TCP/IP协议之上的应用层规范,规范http请求格式
method request-URL version
headers
空一行
entity-body


POST提交的数据必须放在消息主体entity-body中,但没有规定编码方式,但要服务端解析成功才有意义。服务器一般根据 请求头中 Content-Type字段获得body的编码方式
POST(URL,bodytype,[]byte)


(1)application/x-www-form-urlencoded  最常见的POST提交数据的方式,浏览器的原生form表单。后面可以跟charset=utf-8
(2)multipart/form-data
(3)application/json
(4)text/xml    XML-RPC远程调用


//第三个参数的简单形式 (url.Values{"title":{"sss"},"content":{"article body"}})


func main() {
v := url.Values{}
v.Add("name", "幻刺")
v.Set("password", "123456")
u := ioutil.NopCloser(strings.NewReader(v.Encode()))
r, err := http.Post("http://localhost:8080/agent", "application/x-www-form-urlencoded", u)
if err != nil {
fmt.Println("http post err:", err)
}
defer r.Body.Close()
fmt.Println(r.StatusCode)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
fmt.Println("http body err:", err)
}
fmt.Println(string(body))
}


---------------------------------------------------------------------------------client.go
请求头的单词一定不能拼错,都是对应服务端语言去解析的


func main() {
client := &http.Client{}
req, err := http.NewRequest("POST", "http://localhost:8080/agent", strings.NewReader("name=敌法,password=32984"))
if err != nil {
fmt.Println("newRequest err:", err)
}
req.Header.Set("Connection", "keep-alive")
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Cookie", "幻刺")
req.Header.Set("User-Agent", "幽鬼")


resp, err := client.Do(req)
if err != nil {
fmt.Println("client do err:", err)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("body read err:", err)
}
fmt.Println(string(body))
}






------------------------------------------------socket编程


tcp tcp4 tcp6 udp udp4 udp6 ip ip4 ip6 简洁的抽象了网络层和传输层
conn,err:=net.Dial("tcp","192.168.0,10:8080)
conn,err:=net.Dial("udp","192.168.0,10:8080)
conn,err:=net.Dial("ip4:icmp","192.168.0,10:8080)
conn,err:=net.Dial("ip4:1","192.168.0,10:8080)


用tcp写个聊天测试,分客户端和服务端
------------------------------------Dial()


server.go




func main() {
addr, err := net.ResolveTCPAddr("tcp", ":8080")
checkErr(err)
listen, err := net.ListenTCP("tcp", addr)
checkErr(err)
fmt.Println("Start server...")
for {
conn, err := listen.Accept()
checkErr(err)
go Handle(conn) // 每次建立一个连接就放到单独的线程内做处理
}
}


const BufLength = 128


var users map[string]net.Conn = make(map[string]net.Conn, 10)


func Handle(conn net.Conn) {
conn.Write([]byte("欢迎加入2B聊天组~"))
for {
data := make([]byte, 0) //此处做一个输入缓冲以免数据过长读取到不完整的数据
buf := make([]byte, BufLength)
for {
n, err := conn.Read(buf)
if err != nil && err != io.EOF {
checkErr(err)
}
data = append(data, buf[:n]...)
if n != BufLength {
break
}
}


cmd := strings.Split(string(data), "|")
fmt.Println("命令:", cmd)


switch cmd[0] {
case "nick":
fmt.Println("注册名称:" + cmd[1])
users[cmd[1]] = conn
case "say":
for k, v := range users {
if k != cmd[1] {
fmt.Println("给" + k + "发送消息:" + cmd[2])
v.Write([]byte(cmd[1] + ":[" + cmd[2] + "]"))
}
}
}
}
}


func checkErr(err error) {
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
}




------------------------------------client.go


var nick string = ""


func main() {
addr, err := net.ResolveTCPAddr("tcp", ":8080")
checkErr(err)
conn, err := net.DialTCP("tcp", nil, addr)
checkErr(err)
// 读取提示
data := make([]byte, 1024)
conn.Read(data)
fmt.Println(string(data))
// 输入昵称
fmt.Print("输入昵称:")
fmt.Scanf("%v", &nick)
fmt.Println("Hello " + nick)
conn.Write([]byte("nick|" + nick))


go Handle(conn)


for {
someTex := ""
fmt.Scanf("%v", &someTex)
conn.Write([]byte("say|" + nick + "|" + someTex))
}
}


const BufLength = 128


func Handle(conn net.Conn) {
for {
data := make([]byte, 1024)
buf := make([]byte, BufLength)
for {
n, err := conn.Read(buf)
if err != nil && err != io.EOF {
checkErr(err)
}
data = append(data, buf[:n]...)
if n != BufLength {
break
}
}


fmt.Println(string(data))
}
}


func checkErr(err error) {
if err != nil {
fmt.Println(err)
os.Exit(-1)
}
}




----------------------------------------------------------------------rpc编程


RPC--romote procedure call protocol远程调用,类似调用本地函数一样调用服务器的函数,不需要了解底层网络细节的应用程序通信协议。RPC构建与TCP或UDP,或者是HTTP之上。采用客户端/服务器的工作模式,
当执行一个远程过程调用时,客户端首先发送一个带有参数的调用信息到服务端,等待服务器的响应。在服务端,服务进程保存睡眠状态直到客户端的调用信息到达为止。组合客户端接收来自服务端的应答信息,获得进程结果
必须格式:
func (t *T) MethodName(argType T1,replyType *T2)error{}
第一个参数是RPC客户端要传入的参数;第二个参数是要返回给RPC客户端的结果。
服务端一般 通过TCP或HTTP在某个网络地址上进行监听来创建该服务
net/rpc   rpc.Dail() 和 rpc.DialHTTP() 方法来与指定的RPC服务端建立连接。
调用RPC客户端的  Call()方法进行同步处理  Go()进行异步处理,即rpc客户端不用等待服务端的结果即可执行后面的程序
若在rpc传输过程中不指定编码解码器,默认使用go标准库的 encoding/gob




-----------------------------------------rpcserver.go


package server
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
type Arith int
func (t *Arith) Multiply(args *Args, reply *int) error {
*reply = args.A * args.B
return nil
}
func (t *Arith) Divide(args *Args, quo *Quotient) error {
if args.B == 0 {
return errors.New("divide by zero")
}
quo.Quo = args.A / args.B
quo.Rem = args.A % args.B
return nil
}
//注册服务对象并开启该 RPC 服务的代码如下:
arith := new(Arith)
rpc.Register(arith)
rpc.HandleHTTP()
l, e := net.Listen("tcp", ":1234")
if e != nil {
log.Fatal("listen error:", e)
}
go http.Serve(l, nil)


此时, RPC 服务端注册了一个Arith类型的对象及其公开方法Arith.Multiply()和
Arith.Divide()供 RPC 客户端调用。 RPC 在调用服务端提供的方法之前,必须先与 RPC 服务
端建立连接,如下列代码所示:
client, err := rpc.DialHTTP("tcp", serverAddress + ":1234")
if err != nil {
log.Fatal("dialing:", err)
}
在建立连接之后, RPC 客户端可以调用服务端提供的方法。首先,我们来看同步调用程序顺
序执行的方式:
args := &server.Args{7,8}
var reply int
err = client.Call("Arith.Multiply", args, &reply)
if err != nil {
log.Fatal("arith error:", err)
}
fmt.Printf("Arith: %d*%d=%d", args.A, args.B, reply)


此外,还可以以异步方式进行调用,具体代码如下:
quotient := new(Quotient)
divCall := client.Go("Arith.Divide", args, &quotient, nil)
replyCall := <-divCall.Done


---------------------------------
package main


import (
"fmt"
"log"
"net"
"net/http"
"net/rpc"
)


type Echo int


//被注册的对象至少要有一个方法满足下面这个特征,才会被导出到rpc服务接口
func (t *Echo) Hi(args string, reply *string) error {
*reply = "echo" + args
return nil
}


func main() {
//注册rpc服务,默认名字为对象的类型Echo,rpc.RegisterName指定特殊名字
rpc.Register(new(Echo))
//指定rpc的传输协议,这里用http作为rpc调用的载体。也可用rpc.ServeConn接口,定制自己的传输协议
rpc.HandleHTTP()
l, e := net.Listen("tcp", ":1234")
if e != nil {
log.Fatal("listen error:", err)
}
http.Serve(l, nil)
}


/*
客户端的调用方式
func main(){
client,_:=rpc.DialHTTP("tcp", "127.0.0.1:1234")
var args="hello rpc"
var reply string
client.Call("Echo.Hi", args, &reply)
fmt.Printf("arith:%d*%d=%d\n", args.A,args.B,reply)
}
*/


----------------------------------------
type Watcher int


func (w *Watcher) GetInfo(arg int, result *int) error {
*result = 1
return nil
}


func main() {


http.HandleFunc("/login", Handler)


watcher := new(Watcher)
rpc.Register(watcher)
rpc.HandleHTTP()


l, err := net.Listen("tcp", ":1234")
if err != nil {
fmt.Println("监听失败,端口可能已经被占用")
}
fmt.Println("正在监听1234端口")
http.Serve(l, nil)
}


func Handler(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "<html><body>传输的html文档</body></html>")
}
---------------
func main() {
client, err := rpc.DialHTTP("tcp", "127.0.0.1:1234")
if err != nil {
fmt.Println("链接rpc服务器失败:", err)
}
var reply int
err = client.Call("Watcher.GetInfo", 1, &reply)
if err != nil {
fmt.Println("调用远程服务失败", err)
}
fmt.Println("远程服务返回结果:", reply)
}




web服务器开发,处理客户端请求


(1)表单
在Javascript 中,页面上的每一对<form> 标记都解析为一个对象,即form 对象
每个表单元素应当尽量使用<label>标签来提高用户体验;
每个表单元素应当分配 name 属性  和 id 属性。
name 属性:用来将数据提交到服务器;
id 属性:用来在客户端做相应的操作;
使用name属性更易获得元素值,更方便向服务器传数据


html可以接受表单内容,但是接受了也只能是用JAVASCRIPT进行显示出来,无法保存在后台数据库里面。
可以设置WEB服务器,把所有的.HTML文件都作为ASP或者PHP等脚本进行解释执行,这样别人看起来是个HTML,实际上里面却是ASP或者PHP。
3中提交表单的方法:
<form name=”form” method=”post” action=”#">
    <input type=”submit” name=”submit” value=”提交">
</form>
<input type=”image” name=”submit” src=”btnSubmit.jpg”>
<a href=”javascript:form.submit();”>提交</a> 调用一个js函数来提交表单,可以加到任何一个标签的onclick事件中


在form里设置enctype="multipart/form-data",这样才能在提交表单时,将文件以二进制流的形式传输到服务器
enctype:设置或获取表单的多用途网际邮件扩展(MIME) 编码。
这个属性的默认值为:application/x-www-form-urlencoded
如果要上传文件,则应该设置为:multipart/form-data


用submit提交按钮不会进行表单验证,
<form onsubmit="return(qbg()")>
<input type="submit" value="提交"/>
<input type="button" value="提交" onclick="myFunction()"/>


利用js创建一个表单
1.get方式
get方式提交的话,表单项都保存在http header中,格式是
http://localhost:8080/hello.do?name1=value1&name2=value2这样的字符串。server端通过request.getParameter是可以取到值的。


2.post方式(enctype为缺省的application/x-www-form-urlencoded)
表单数据都保存在http的正文部分,格式类似于下面这样:用request.getParameter是可以取到数据的


name1=value1&name2=value2


3.post方式(enctype为multipart/form-data,多用于文件上传)
表单数据都保存在http的正文部分,各个表单项之间用boundary隔开。格式类似于下面这样:用request.getParameter是取不到数据的,这时需要通过request.getInputStream来取数据


html表单数据 ->  处理程序 ->数据库
你访问显示数据的页面 -> 处理程序 ->查询数据库 ->反馈信息给你


使用js提交数据(数据少集中的话用form表单,大量的数据或者比较分散用javascript来提交)


纯html提交数据,分散在网页的各个地方,很难集中在一个form标签下
javascript结合DOM(内置在浏览器中的对象模型)获得我们要上传的数据,将其组装在一个form中,再用post方式提交到服务器处理,
有超链接形式,按钮形式


function submit() {  
 var textValue = document.getElementById("id").value; //这里的id是文本输入框的id  
//使用下面的方法进行页面跳转,页面跳转的时候可以带参数,所以就可以提交数据  
window.location.href="result.jsp?text="+textValue;   
//这里的地址可以是任意的地址,包括提交路径等! 


package main


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


func Handle(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "welcome to my server!")
fmt.Println(r.Header)
fmt.Println(r.UserAgent())
fmt.Println(r.URL.RequestURI())
r.ParseForm()
val := r.FormValue("key")
fmt.Println("key=", val)
w.Write([]byte(val))
}


func Handle1(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
val := r.FormValue("key")
fmt.Println("key=", val)
w.Write([]byte(val))
}
func Handle2(w http.ResponseWriter, r *http.Request) {
_, err := r.MultipartReader()
if err != nil {
println("handle2 error=", err)
return
}
val := r.FormValue("key")
fmt.Println("key:=", val)
w.Write([]byte(val))
}
func main() {
http.HandleFunc("/one", Handle1)
http.HandleFunc("/two", Handle2)
fmt.Println("start...")
err := http.ListenAndServe(":8080", nil)
if err != nil {
println("error:", err)
}
}




提交表单,服务器接收数据
---index.html
<body>
<form id="uploadForm" method="post" enctype="multipart/form-data" action="/upload">
<p>Golang upload</p>
<input type="FILE" id="file" name="file" />
<input type="SUBMIT" value="upload">
</form>
</body>


---main.go
var uploadTemplate = template.Must(template.ParseFiles("index.html"))


func indexHandle(w http.ResponseWriter, r *http.Request) {
if err := uploadTemplate.Execute(w, nil); err != nil {
log.Fatal("Execute: ", err.Error())
return
}
}


func uploadHandle(w http.ResponseWriter, r *http.Request) {
file, _, err := r.FormFile("file")
if err != nil {
log.Fatal("FormFile: ", err.Error())
return
}
defer func() {
if err := file.Close(); err != nil {
log.Fatal("Close: ", err.Error())
return
}
}()


bytes, err := ioutil.ReadAll(file)
if err != nil {
log.Fatal("ReadAll: ", err.Error())
return
}


w.Write(bytes)
}


func main() {
http.HandleFunc("/", indexHandle)
http.HandleFunc("/upload", uploadHandle)
http.ListenAndServe(":8080", nil)
}


-------------------
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>服务器响应给客户端的html文件</title>
<style type="text/css">
body {margin:0px;background:#A29C9C;border:0px;padding:0px;font-size:15px;}
.contain {width:1000px;margin:auto;}
.header {width:1000px;height:70px;margin:0 auto;font-size:20px;text-align:center;background: #D0CDCD;line-height:70px;}
.content {width:1000px;margin:5px,auto;text-align:center;font-size:18px;padding:5px;}
</style>
</head>
<body>
<div class="contain">
<div class="header">
<h1>服务器传输的网页</h1>
</div>
<div class="content">
<form action="/upload" method="post" name="uploadForm">
<label for="txt1">姓名:</label>
<input type="text" id="txt1" name="myname" />
<br />
<label for="txt2">密码:</label>
<input type="text" id="txt2" name="password">
<br />
<!--<input type="FILE" id="file" name="file" />-->
<input type="button" value="提交" />
</form>
</div>
</div>
</body>
</html>


----------
func uploadHandle(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
val1 := r.PostFormValue("post_data")
val2 := r.PostFormValue("password")
str := "姓名:" + val1 + "\n" + "密码:" + val2
w.Write([]byte(str))
//http.Redirect(w, r, urlStr, code)
}


--------js提交表单,禁用反复提交
<form name="frm" method="post" action="javascript:alert('提交成功!');">
     <input type="button" value="提交功能" 
            onclick="document.forms['frm'].submit();">
     <input type="button" value="禁用反复提交" 
            onclick="this.disabled=true; this.form.submit();">
  </form>


-------js创建一个表单
var myForm = document.createElement("form"); 
myForm.method="get" ; 
myForm.action = "line_ticket.aspx" ; 
var myInput = document.createElement("input") ; 
myInput.setAttribute("name", "id") ; 
myInput.setAttribute("value", idStr); 
myForm.appendChild(myInput) ;
var myInput2 = document.createElement("input") ; 
myInput2.setAttribute("name", "fid") ; 
myInput2.setAttribute("value", fid); 
myForm.appendChild(myInput2) ;
var myInput3 = document.createElement("input") ; 
myInput3.setAttribute("name", "unlock") ; 
myInput3.setAttribute("value", unlock); 
myForm.appendChild(myInput3) ;
var myInput4 = document.createElement("input") ; 
myInput4.setAttribute("name", "Option") ; 
myInput4.setAttribute("value", "del"); 
myForm.appendChild(myInput4) ; 
document.body.appendChild(myForm) ; 
myForm.submit() ; 
document.body.removeChild(myForm) ;


------js创建一个表单




----go对静态文件的处理,加载css、js文件
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
目录结构
main.go
static---css(main.css)+js(main.js)
view--index.html


建立一个静态服务,才能访问静态文件,且模板文件不要和静态文件放在一起
<!-- index.html -->
<link rel="stylesheet" href="/static/css/main.css" type="text/css">
<script type="text/javascript" src="/static/js/main.js"></script>




func indexHandle(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("view/index.html")
if err := t.Execute(w, nil); err != nil {
log.Fatal("Execute: ", err.Error())
return
}
}


func uploadHandle(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
val1 := r.PostFormValue("myname")
val2 := r.PostFormValue("password")
str := "姓名:" + val1 + "\n" + "密码:" + val2
w.Write([]byte(str))
//http.Redirect(w, r, urlStr, code)
}


func main() {
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
http.HandleFunc("/", indexHandle)
http.HandleFunc("/upload", uploadHandle)
http.ListenAndServe(":8080", nil)
}
--------部分js操作

本文来自:CSDN博客

感谢作者:ice_201507

查看原文:golang表单提交与服务器的交互

入群交流(和以上内容无关):Go中文网 QQ 交流群:798786647 或加微信入微信群:274768166 备注:入群;关注公众号:Go语言中文网

6510 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传