首页 > 其他分享 >go 表单

go 表单

时间:2022-12-19 15:22:18浏览次数:39  
标签:http string Form fmt 表单 go return

 处理表单的输入

  用户在表单中输入的数据会以键值的形式记录在请求的主体(body)中,然后以HTTP POST请求的形式发送至服务器。服务器在接收浏览器发送的表单请求后,还需要对这些数据进行语法分析(ParseForm()),从而提取出数据中记录的键值对。

先来看一个表单递交的例子,我们有如下的表单内容,命名成文件login.html(放入当前新建项目的目录里面)

<html>
<head>
<title></title>
</head>
<body>
<form action="/login" method="post">
    用户名:<input type="text" name="username">
    密码:<input type="password" name="password">
    <input type="submit" value="登录">
</form>
</body>
</html>

上面递交表单到服务器的/login,当用户输入信息点击登录之后,会跳转到服务器的路由login里面,我们首先要判断这个是什么方式传递过来,POST还是GET呢?是GET的话先返回界面,然后客户端提交(POST)。

package main

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

func main() {
    http.HandleFunc("/", sayHello)           //设置访问的路由
    http.HandleFunc("/login", login)         //设置访问的路由
    err := http.ListenAndServe(":8080", nil) //设置监听的端口
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

func sayHello(w http.ResponseWriter, r *http.Request) {
    r.ParseForm()
    fmt.Println(r.Form)
    fmt.Println("Path", r.URL.Path)
    fmt.Println("scheme", r.URL.Scheme)

    for k, v := range r.Form {
        fmt.Println(k+":", strings.Join(v, " "))
    }
    fmt.Fprintf(w, "Hello")
}

func login(w http.ResponseWriter, r *http.Request) {
    fmt.Println("method:", r.Method) //获取请求的方法
    if r.Method == "GET" {
        t, _ := template.ParseFiles("login.html")
        log.Println(t.Execute(w, nil))
    } else {
        //请求的是登录数据,那么执行登录的逻辑判断
        fmt.Println("username:", r.Form["username"])
        fmt.Println("password:", r.Form["password"])
    }
}

 

提交后:

 

 

发现空白,因为login中,没有r.ParseForm()分析表单数据,加入后:

 

 Form

Form为url.Value类型

 

 

 

 除了Form还有PostForm,两者的区别是Form除了有主体中的键值对数据,还有url中的(a=b&c=d)的键值对数据,而PostForm中只有主体body中的数据。

//还可以使用下面两个函数返回key对应的值,只不过两个函数只能返回一个value,即使一个key对应多个value。
func (r *Request) FormValue(key string) string
func (r *Request) PostFormValue(key string) string

验证表单的输入

必填字段

你想要确保从一个表单元素中得到一个值,例如前面小节里面的用户名,我们如何处理呢?Go有一个内置函数len可以获取字符串的长度,这样我们就可以通过len来获取数据的长度,例如:

if len(r.Form["username"][0])==0{
    //为空的处理
}

r.Form对不同类型的表单元素的留空有不同的处理, 对于空文本框、空文本区域以及文件上传,元素的值为空值,而如果是未选中的复选框和单选按钮,则根本不会在r.Form中产生相应条目,如果我们用上面例子中的方式去获取数据时程序就会报错。所以我们需要通过r.Form.Get()来获取值,因为如果字段不存在,通过该方式获取的是空值。但是通过r.Form.Get()只能获取单个的值,如果是map的值,必须通过上面的方式来获取。

数字

你想要确保一个表单输入框中获取的只能是数字,例如,你想通过表单获取某个人的具体年龄是50岁还是10岁,而不是像“一把年纪了”或“年轻着呢”这种描述如果我们是判断正整数,那么我们先转化成int类型,然后进行处理:

getint,err:=strconv.Atoi(r.Form.Get("age"))
if err!=nil{
    //数字转化出错了,那么可能就不是数字
}

//接下来就可以判断这个数字的大小范围了
if getint >100 {
    //太大了
}

还有一种方式就是正则匹配的方式:

if m, _ := regexp.MatchString("^[0-9]+$", r.Form.Get("age")); !m {
    return false
}

对于性能要求很高的用户来说,这是一个老生常谈的问题了,他们认为应该尽量避免使用正则表达式,因为使用正则表达式的速度会比较慢。但是在目前机器性能那么强劲的情况下,对于这种简单的正则表达式效率和类型转换函数是没有什么差别的。如果你对正则表达式很熟悉,而且你在其它语言中也在使用它,那么在Go里面使用正则表达式将是一个便利的方式。

中文

有时候我们想通过表单元素获取一个用户的中文名字,但是又为了保证获取的是正确的中文,我们需要进行验证,而不是用户随便的一些输入。对于中文我们目前有两种方式来验证,可以使用 unicode 包提供的 func Is(rangeTab *RangeTable, r rune) bool 来验证,也可以使用正则方式来验证,这里使用最简单的正则方式,如下代码所示

if m, _ := regexp.MatchString("^\\p{Han}+$", r.Form.Get("realname")); !m {
    return false
}

英文

我们期望通过表单元素获取一个英文值,例如我们想知道一个用户的英文名,应该是astaxie,而不是asta谢。

我们可以很简单的通过正则验证数据:

if m, _ := regexp.MatchString("^[a-zA-Z]+$", r.Form.Get("engname")); !m {
    return false
}

手机号码

你想要判断用户输入的手机号码是否正确,通过正则也可以验证:

if m, _ := regexp.MatchString(`^(1[3|4|5|8][0-9]\d{4,8})$`, r.Form.Get("mobile")); !m {
    return false
}

下拉菜单

如果我们想要判断表单里面<select>元素生成的下拉菜单中是否有被选中的项目。有些时候黑客可能会伪造这个下拉菜单不存在的值发送给你,那么如何判断这个值是否是我们预设的值呢?

我们的select可能是这样的一些元素:

<select name="fruit">
<option value="apple">apple</option>
<option value="pear">pear</option>
<option value="banana">banana</option>
</select>

那么我们可以这样来验证(遍历一个包含下拉菜单所有数据的切片):

slice:=[]string{"apple","pear","banana"}

v := r.Form.Get("fruit")
for _, item := range slice {
    if item == v {
        return true
    }
}

return false

单选按钮

如果我们想要判断radio按钮是否有一个被选中了,我们页面的输出可能就是一个男、女性别的选择,但是也可能一个15岁大的无聊小孩,一手拿着http协议的书,另一只手通过telnet客户端向你的程序在发送请求呢,你设定的性别男值是1,女是2,他给你发送一个3,你的程序会出现异常吗?因此我们也需要像下拉菜单的判断方式类似,判断我们获取的值是我们预设的值,而不是额外的值。

slice:=[]string{"1","2"}

for _, v := range slice {
    if v == r.Form.Get("gender") {
        return true
    }
}
return false

复选框

有一项选择兴趣的复选框,你想确定用户选中的和你提供给用户选择的是同一个类型的数据。

<input type="checkbox" name="interest" value="football">足球
<input type="checkbox" name="interest" value="basketball">篮球
<input type="checkbox" name="interest" value="tennis">网球

对于复选框我们的验证和单选有点不一样,因为接收到的数据是一个slice:

slice:=[]string{"football","basketball","tennis"}
a:=Slice_diff(r.Form["interest"],slice)  
//r.Form["interest"]中的值如果slice中没有,则加入到a中。
if a == nil{ return true } return false

表单编码

application/x-www-form-urlencoded   表示在发送前编码所有字符(默认)
multipart/form-data      不对字符编码。在使用包含文件上传控件的表单时,必须使用该值。
text/plain      空格转换为 "+" 加号,但不对特殊字符编码。

如果表单使用multipart/form-data编码时,表单数据将被存储在MultipartForm字段而不是以上两个字段,但url的数据存储在Form中。

MultipartForm字段

MultipartForm    *multipart.Form

type Form struct {
    Value map[string][]string
    File  map[string][]*FileHeader
}

type FileHeader struct {
    Filename string
    Header   textproto.MIMEHeader
    Size     int64
    content  []byte
    tmpfile  string
}
type MIMEHeader map[string][]string
A MIMEHeader represents a MIME-style header mapping keys to sets of values.
方法对象: (MIMEHeader):
Add(key string, value string)
Set(key string, value string)
Get(key string) string
Values(key string) []string
Del(key string)

要使表单能够上传文件,首先第一步就是要添加form的enctype属性(multipart/form-data):

<html>
<head>
    <title>上传文件</title>
</head>
<body>
<form enctype="multipart/form-data" action="/upload" method="post">
  <input type="file" name="uploadfile" />
  <input type="hidden" name="token" value="{{.}}"/>
  <input type="submit" value="upload" />
</form>
</body>
</html>

在服务器端,我们增加一个handlerFunc:

http.HandleFunc("/upload", upload)

// 处理/upload 逻辑
func upload(w http.ResponseWriter, r *http.Request) {
    fmt.Println("method:", r.Method) //获取请求的方法
    if r.Method == "GET" {
        crutime := time.Now().Unix()
        h := md5.New()
        io.WriteString(h, strconv.FormatInt(crutime, 10))
        token := fmt.Sprintf("%x", h.Sum(nil))

        t, _ := template.ParseFiles("upload.gtpl")
        t.Execute(w, token)
    } else {
        r.ParseMultipartForm(32 << 20)
        file, handler, err := r.FormFile("uploadfile")
        if err != nil {
            fmt.Println(err)
            return
        }
        defer file.Close()
        fmt.Fprintf(w, "%v", handler.Header)
        f, err := os.OpenFile("./test/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)  // 此处假设当前目录下已存在test目录
        if err != nil {
            fmt.Println(err)
            return
        }
        defer f.Close()
        io.Copy(f, file)
    }
}

  通过上面的代码可以看到,处理文件上传我们需要调用r.ParseMultipartForm,里面的参数表示maxMemory,调用ParseMultipartForm之后,上传的文件存储在maxMemory大小的内存里面,如果文件大小超过了maxMemory,那么剩下的部分将存储在系统的临时文件中。我们可以通过r.FormFile获取上面的文件句柄,然后实例中使用了io.Copy来存储文件。

  获取其他非文件字段信息的时候就不需要调用r.ParseForm,因为在需要的时候Go自动会去调用ParseForm。而且ParseMultipartForm调用一次之后,后面再次调用不会再有效果。

通过上面的实例我们可以看到我们上传文件主要三步处理:

  1. 表单中增加enctype="multipart/form-data"
  2. 服务端调用r.ParseMultipartForm,把上传的文件存储在内存和临时文件中
  3. 使用r.FormFile获取文件句柄,然后对文件进行存储等处理。

 

标签:http,string,Form,fmt,表单,go,return
From: https://www.cnblogs.com/dadishi/p/16992263.html

相关文章

  • django Q查询、orm查询优化、事务、常用字段、三种多对多,Ajax
    目录今日内容详细Q查询进阶操作ORM查询优化ORM事务操作ORM常用字段类型ORM常用字段参数AjaxContent-Typeajax携带文件数据今日内容详细Q查询进阶操作fromdjango.db.mod......
  • Golang 语言开发规范(JD)
    Go语言开发规范指南本规范制定主要用于开发及代码review时进行参考,保证平台开发的一致性与规范性。1命名规范命名的规范性包括普通变量、结构体、指针类型等。1.1普通变量......
  • ABAP:MIGO收货冲销BAPI
    MIGO收货冲销BAPIFUNCTIONzsdfu014.*"----------------------------------------------------------------------*"*"本地接口:*"IMPORTING*"VALUE(I_ID)TYP......
  • 【Django--中间件】
    一、什么是中间件Django中间件是是介于request与response处理之间的一道处理过程,是修改Djangorequest或者response对象的钩子,相对比较轻量级,并且在全局上改变django的......
  • ABAP:MIGO收货BAPI
    MIGO收货BAPI:DATA:lt_zsdt008TYPETABLEOFzsdt008,ls_zsdt008LIKELINEOFlt_zsdt008,ls_zsdt008_2LIKELINEOFlt_zsdt008,ls_z......
  • golang---进程、线程、goroutine
    创建进程os包及其子包os/exec提供了创建进程的方法。一般的,应该优先使用os/exec包。因为os/exec包依赖os包中关键创建进程的API,为了便于理解,我们先探讨os包中和......
  • 转录因子扰乱GO分析 | EnrichR
     目的比较直接:我有个DEG基因集,我想要知道那个TF最有可能调控这个基因集。Transcriptionfactorperturbationgeneontologyanalysis(EnrichR) oftop100genesupr......
  • python调go dll库构造函数方法
    调用方法最主要的是 fromctypesimport* 里面包含了windll调用加载方法,具体用windll还是cdll加载动态库见百度接下来就是classGoString(Structure):_fields_=[(......
  • MongoDB 索引类型介绍
    转载请注明出处:目录1.单字段索引2.复合索引3.多key索引4.其他类型索引5.索引额外属性6.MongoDB索引相关的常用sql命令Mo......
  • django周报
    Django请求生命周期用户在浏览器输入url发送一个get请求方法的request请求django中封装了wsgi服务器用来监听端口接受request请求接收到的请求封装后传入中间件然后中......