当前位置:网站首页>Go Web 编程入门:验证器
Go Web 编程入门:验证器
2022-06-22 12:49:00 【51CTO】
前言
网络验证可能是一个难题。 有句话在 Web 开发中流传很广的原则:
我们不能相信来自客户端用户表单的任何内容。
所以我们必须在使用这些数据之前验证所有传入数据。实现 REST API 是 Go 应用程序的典型用例。 API 接受的格式错误的数据可能会导致系统其他部分出现严重错误。
最好的情况是您的数据库有一些机制来防止存储格式错误的数据。如果不这样做,这些数据可能会导致您面向客户的应用程序出现错误和意外行为(比如 SQL 注入)。
在这篇文章中,我们将介绍如何在 Go 中验证发送到 REST API 的数据。
手动验证输入
简易的 REST API
这是一个简单的 REST API 示例,使用 gorilla/mux 包构建。它是一个很棒的 HTTP 路由器,特别是对于 REST API。 API 为一个端点提供路径 /user。为简单起见,它只接受所有用户的 HTTP GET 和创建用户的 HTTP Post。此外,它没有持久性数据库,而是用切片将用户存储在内存中。
package
main
import (
"encoding/json"
"log"
"net/http"
"strings"
"github.com/gorilla/mux"
)
type
User
struct {
ID
int
FirstName
string
LastName
string
FavouriteVideoGame
string
Email
string
}
func
main() {
router :
=
mux
.
NewRouter()
router
.
HandleFunc(
"/user",
PostUser)
.
Methods(
http
.
MethodPost)
router
.
HandleFunc(
"/user",
GetUsers)
.
Methods(
http
.
MethodGet)
log
.
Fatal(
http
.
ListenAndServe(
":8081",
router))
}
var
users
= []
User{}
var
id
=
0
func
validateEmail(
email
string)
bool {
// This is obviously not a good validation strategy for email addresses
// pretend a complex regex here
return
!
strings
.
Contains(
email,
"@")
}
func
PostUser(
w
http
.
ResponseWriter,
r
*
http
.
Request) {
user :
=
User{}
json
.
NewDecoder(
r
.
Body)
.
Decode(
&
user)
// We don't want an API user to set the ID manually
// in a production use case this could be an automatically
// ID in the database
user
.
ID
=
id
id
++
users
=
append(
users,
user)
w
.
WriteHeader(
http
.
StatusCreated)
}
func
GetUsers(
w
http
.
ResponseWriter,
r
*
http
.
Request) {
w
.
Header()
.
Add(
"Content-Type",
"application/json")
if
err :
=
json
.
NewEncoder(
w)
.
Encode(
users);
err
!=
nil {
log
.
Println(
err)
w
.
WriteHeader(
http
.
StatusInternalServerError)
return
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
现在让我们看看如何手动验证在请求正文中提供给此 API 的 POST 处理程序的输入。
手动验证输入
有时我们要求用户输入一些字段,但他们未能完成该字段。例如在上一节中,当我们需要用户名时。您可以使用 len 函数来获取字段的长度,以确保用户输入了某些内容。
假设我们想在使用 Post 处理程序创建用户时根据需要设置 FirstName、LastName 和 Email。此外,我们希望电子邮件字段是有效的电子邮件地址。一种简单的方法是手动验证字段,如下所示:
if
user
.
FirstName
==
"" {
errs
=
append(
errs,
fmt
.
Errorf(
"Firstname is required")
.
Error())
}
if
user
.
LastName
==
"" {
errs
=
append(
errs,
fmt
.
Errorf(
"LastName is required")
.
Error())
}
if
user
.
Email
==
""
||
validateEmail(
user
.
Email) {
errs
=
append(
errs,
fmt
.
Errorf(
"A valid Email is required")
.
Error())
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
完整示例:
package
main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strings"
"github.com/gorilla/mux"
)
type
User
struct {
ID
int
FirstName
string
LastName
string
FavouriteVideoGame
string
Email
string
}
func
main() {
router :
=
mux
.
NewRouter()
router
.
HandleFunc(
"/user",
PostUser)
.
Methods(
http
.
MethodPost)
router
.
HandleFunc(
"/user",
GetUsers)
.
Methods(
http
.
MethodGet)
log
.
Fatal(
http
.
ListenAndServe(
":8081",
router))
}
var
users
= []
User{}
var
id
=
0
func
validateEmail(
email
string)
bool {
// That's obviously not a good validation strategy for email addresses
// pretend a complex regex here
return
!
strings
.
Contains(
email,
"@")
}
func
PostUser(
w
http
.
ResponseWriter,
r
*
http
.
Request) {
user :
=
User{}
json
.
NewDecoder(
r
.
Body)
.
Decode(
&
user)
errs :
= []
string{}
if
user
.
FirstName
==
"" {
errs
=
append(
errs,
fmt
.
Errorf(
"Firstname is required")
.
Error())
}
if
user
.
LastName
==
"" {
errs
=
append(
errs,
fmt
.
Errorf(
"LastName is required")
.
Error())
}
if
user
.
Email
==
""
||
validateEmail(
user
.
Email) {
errs
=
append(
errs,
fmt
.
Errorf(
"A valid Email is required")
.
Error())
}
if
len(
errs)
>
0 {
w
.
Header()
.
Add(
"Content-Type",
"application/json")
w
.
WriteHeader(
http
.
StatusBadRequest)
if
err :
=
json
.
NewEncoder(
w)
.
Encode(
errs);
err
!=
nil {
}
return
}
// We don't want an API user to set the ID manually
// in a production use case this could be an automatically
// ID in the database
user
.
ID
=
id
id
++
users
=
append(
users,
user)
w
.
WriteHeader(
http
.
StatusCreated)
}
func
GetUsers(
w
http
.
ResponseWriter,
r
*
http
.
Request) {
w
.
Header()
.
Add(
"Content-Type",
"application/json")
if
err :
=
json
.
NewEncoder(
w)
.
Encode(
users);
err
!=
nil {
log
.
Println(
err)
w
.
WriteHeader(
http
.
StatusInternalServerError)
return
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
可以看到这个验证方法非常冗长。我们必须定义一个自定义函数来验证常见的东西,比如电子邮件地址。让我们看看如何改进这一点。
上面只是简单通过 validateEmail 函数验证邮箱中是否含有 @ 字符:
其实更好的方式是通过正则表达式来验证 E-mail 的有效性:
使用结构标签验证输入
在 Go 中验证结构的一种更惯用的方法是使用结构标签。有许多通过结构标签进行结构验证的包。我们将在这里使用 https://github.com/go-playground/validator:该 验证器基于标签实现结构和单个字段的值验证。

使用 go get github.com/go-playground/validator/v10 进行安装。
这不仅使我们能够使用结构标签进行验证,而且还提供了许多预定义的验证方法,例如电子邮件地址。
我们将对结构执行此验证,但不探讨如何填充结构。 我们可以假设数据将通过解析 JSON 有效负载、从表单输入显式填充或其他方法来填充。
如果您的数据需要其他验证器,请查看验证器包的文档。您需要的验证器很有可能在软件包提供的 80 多个验证器之下。
package
main
import (
"encoding/json"
"log"
"net/http"
"github.com/go-playground/validator/v10"
"github.com/gorilla/mux"
)
type
User
struct {
ID
int
`validate:"isdefault"`
FirstName
string
`validate:"required"`
LastName
string
`validate:"required"`
FavouriteVideoGame
string
Email
string
`validate:"required,email"`
}
func
main() {
router :
=
mux
.
NewRouter()
router
.
HandleFunc(
"/user",
PostUser)
.
Methods(
http
.
MethodPost)
router
.
HandleFunc(
"/user",
GetUsers)
.
Methods(
http
.
MethodGet)
log
.
Fatal(
http
.
ListenAndServe(
":8081",
router))
}
var
users
= []
User{}
var
id
=
0
func
PostUser(
w
http
.
ResponseWriter,
r
*
http
.
Request) {
user :
=
User{}
json
.
NewDecoder(
r
.
Body)
.
Decode(
&
user)
validate :
=
validator
.
New()
err :
=
validate
.
Struct(
user)
if
err
!=
nil {
validationErrors :
=
err
.(
validator
.
ValidationErrors)
w
.
Header()
.
Add(
"Content-Type",
"application/json")
w
.
WriteHeader(
http
.
StatusBadRequest)
responseBody :
=
map[
string]
string{
"error":
validationErrors
.
Error()}
if
err :
=
json
.
NewEncoder(
w)
.
Encode(
responseBody);
err
!=
nil {
}
return
}
// We don't want an API user to set the ID manually
// in a production use case this could be an automatically
// ID in the database
user
.
ID
=
id
id
++
users
=
append(
users,
user)
w
.
WriteHeader(
http
.
StatusCreated)
}
func
GetUsers(
w
http
.
ResponseWriter,
r
*
http
.
Request) {
w
.
Header()
.
Add(
"Content-Type",
"application/json")
if
err :
=
json
.
NewEncoder(
w)
.
Encode(
users);
err
!=
nil {
log
.
Println(
err)
w
.
WriteHeader(
http
.
StatusInternalServerError)
return
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
上面的 validationError.Error() 返回一个字符串,该字符串总结了结构中每个失败的验证。所以 BadRequest 响应仍然有非常详细的信息说明出了什么问题。
我们将验证更改为使用验证器包,现在根据以下规则进行验证:
- ID 字段不应该由用户设置,所以我们验证它有 int 的默认值,即 0
- FullName 和 LastName 是必需的
- 电子邮件字段是必需的,并使用预定义的电子邮件验证器进行验证
使用自定义验证器验证输入
所以现在我们使用验证器包并使用结构标签验证结构。但是我们如何验证一个无法通过库提供的标签验证的结构字段呢?
假设我们想将某些视频游戏列入黑名单。因为我们不希望我们的系统中有喜欢 PUBG 或 Fortnite 等游戏的用户。在这种情况下,我们可以定义一个自定义的 validate 标记值并让 validate 包像这样使用它:
首先我们定义一个验证函数:
然后我们用验证器实例注册函数和相应的标签。
现在我们在 User 结构的定义中添加标签。
推荐验证库
Awesome Go 项目下有用于验证的库。这里推荐如下:

- checkdigit - Provide check digit algorithms (Luhn, Verhoeff, Damm) and calculators (ISBN, EAN, JAN, UPC, etc.).
- gody - :balloon: A lightweight struct validator for Go.
- govalid - Fast, tag-based validation for structs.
- govalidator - Validators and sanitizers for strings, numerics, slices and structs.
- govalidator - Validate Golang request data with simple rules. Highly inspired by Laravel’s request validation.
- jio - jio is a json schema validator similar to joi.
- ozzo-validation - Supports validation of various data types (structs, strings, maps, slices, etc.) with configurable and extensible validation rules specified in usual code constructs instead of struct tags.
- terraform-validator - A norms and conventions validator for Terraform.
- validate - Go package for data validation and filtering. support validate Map, Struct, Request(Form, JSON, url.Values, Uploaded Files) data and more features.
- validate - This package provides a framework for writing validations for Go applications.
- validator - Go Struct and Field validation, including Cross Field, Cross Struct, Map, Slice and Array diving.
- Validator - A lightweight model validator written in Go.Contains VFs:Min, Max, MinLength, MaxLength, Length, Enum, Regex.
根据介绍赶紧为自己的项目挑一个吧~
总结
验证 REST API 输入对于防止应用程序出现格式错误的数据至关重要。您可以编写自己的验证逻辑,但在大多数情况下,最好使用维护良好的验证包,例如上面的推荐。
这使您可以在结构中使用标签来配置验证,并使运行验证的逻辑保持简单。如果您有一个需要不常见验证功能的特殊用例,您仍然可以为验证器包定义自己的扩展。
- Package validatorValidation
- Awesome Go:https://awesome-go.com/validation/
- Client-side form validation
- Validate REST Input in Go
- Verification of inputs
边栏推荐
- 华为这份关于专利的会议纪要,都说了什么?(内含华为十大发明彩蛋)
- epoch_num和predict_num的换算
- 融云:让银行轻松上“云”
- Redis password modification, startup, view and other operations
- Aliyundrive fuse that allows jellyfin to directly mount alicloud disks
- Oracle stored procedure 2
- uniapp app 端截屏且保存到本地
- Oracle cursor
- What does Huawei's minutes on patents say? (including Huawei's top ten inventions)
- Eureka的InstanceInfoReplicator类(服务注册辅助类)
猜你喜欢

My suggestions on SAP ABAP transformation

程序员要不要选择软件人才外包公司?

Acwing 241 Loulan totem (detailed explanation of tree array)
![[Nacos cloud native] the first step of reading the source code is to start Nacos locally](/img/f8/d9b848593cf7380a6c99ee0a8158f8.png)
[Nacos cloud native] the first step of reading the source code is to start Nacos locally

Stephencovey's tips for efficient work for young people

STM32F1与STM32CubeIDE编程实例-光断续传感器驱动

Simple integration of client go gin IX create

融云:让银行轻松上“云”

JSP based library management system, including source code, database script, video tutorial for project operation, and video tutorial for thesis writing

Stored procedures in MySQL
随机推荐
性能相关指标
Interpretation of the thesis -- factorization meets the neighborhood: a multifaceted collaborative filtering model
融云:让银行轻松上“云”
Oceanbase database helps the ideal automobile intelligent production line to realize automatic recovery within 30 seconds
Instanceinforeplicator class of Eureka (service registration auxiliary class)
SQL Server 常用函数
史蒂芬·柯维写给年轻人的高效工作秘笈
Getting started with shell Basics
技术实践 | 场景导向的音视频通话体验优化
3dMax建模笔记(一):介绍3dMax和创建第一个模型Hello world
高薪程序员&面试题精讲系列114之Redis缓存你熟悉吗?Redis的key如何设计?内存淘汰机制你熟悉吗?
Chapter 1 overview of naturallanguageprocessing and deep learning
HW is around the corner. Can't you read the danger message?
别再用 System.currentTimeMillis() 统计耗时了,太 Low,StopWatch 好用到爆!
leetcode 1130. Minimum cost spanning tree of leaf value
Oracle cursor
12306 ticket grabbing tutorial
Talk about row storage and column storage of database
My suggestions on SAP ABAP transformation
Screenshot of the uniapp app and save it locally
