建设网站现在免费吗,网络商城平台怎么做,最珠海app下载安卓版,前端开发规范用户邮箱地址有效性验证在上一节中#xff0c;我们编写了用于用户注册的代码。但是当时并没有为用户的Email添加有效性验证#xff0c;导致Email被设置为任何字符串都能注册成功。所以在本节初#xff0c;我们首先来为Email添加一个有效性验证。首先还是来编写测试数据…用户邮箱地址有效性验证在上一节中我们编写了用于用户注册的代码。但是当时并没有为用户的Email添加有效性验证导致Email被设置为任何字符串都能注册成功。所以在本节初我们首先来为Email添加一个有效性验证。首先还是来编写测试数据打开我们上一节创建的test_user_data.json向其中添加一个测试用的用户信息。代码清单3-1models/test_user_data.json[
.
.
. {email:email8exam_ple.com,password: password8,username: username8}
]我们添加的这个新的用户信息的Email是一个无效的邮箱地址。接着我们打开user_test.go也向其中增加一个测试case。代码清单3-2models/user_test.gofunc TestUserCreate(t *testing.T) {
.
.
.err users[7].Create() // 测试Email为无效的邮箱地址的情况if err nil {t.Error(expected get an error but no error occured.)} else if err.Error() ! Invalid email address {t.Errorf(expected get Invalid email address but got %sn, err.Error())}
}
然后打开user.go我们向其中增加一个isValidEmail()方法用来检测设置的用户Email是否有效然后再在向数据库插入数据之前检查邮箱地址是否有效。如果无效的话我们返回一个“Invalid email address”的错误。代码清单3-3models/user.gopackage modelsimport (errorsregexptime
)type User struct {ID intEmail *string gorm:not null;unique_indexPassword *string gorm:not nullUsername *string gorm:not null;unique_indexMessage stringCreatedAt time.TimeUpdatedAt time.Time
}func (u *User) Create() (err error) {if u.Email ! nil !u.isValidEmail() {err errors.New(Invalid email address)return err}if u.Password nil {err errors.New(Error occured when creating user)return err}plain : *u.Passwordencrypt : Encrypt(plain)u.Password encrypterr DB.Create(u).Errorif err ! nil {err errors.New(Error occured when creating user)}return
}func (u *User) isValidEmail() bool {pat : (?i)A[w-.][a-zd-.].[a-z]zemail : u.Emailok, _ : regexp.MatchString(pat, *email)return ok
}
在isValidEmail()方法里我们使用了正则表达式去匹配Email属性。如果能匹配上则返回true否则就会返回false。所使用的正则表达式的含义如下完成后我们新打开一个命令行工具cd到models文件夹下运行go testC:Userssxu37GosrcGoWebcd models
C:Userssxu37GosrcGoWebmodelsgo test结果如下PASS
ok GoWeb/models 4.101s测试Pass说明我们添加的用于测试邮箱地址是否有效的代码是正确的。查找用户既然我们已经有了创建用户的功能接下来我们就来实现简单的用户查找的功能。在创建用户模型的时候我们在数据库中将用户的id列设置为了primary key并且在email和username都创建了index这就意味着通过ID、Email和Username这三者我们都可以快速检索到一个用户的信息。首先我们还是来编写查找用户的测试代码。打开user_test.go向其中增加下面三个函数。代码清单3-4models/user_test.
.
.
func TestFindUserByID(t *testing.T) {id1, id2 : 5, 4user1, err : FindUserByID(id1)if err nil {t.Error(expected find no user but got:, user1)}user2, err : FindUserByID(id2)if err ! nil {t.Error(got an unexpected error:, err)} else if user2.ID ! id2 {t.Errorf(expected find user which id is %d but got user which id is: %dn, id2, user2.ID)} else if user2.Email ! nil || user2.Password ! nil {t.Error(expected do not get user email and password but got them now)}
}func TestFindUserByEmail(t *testing.T) {email1, email2 : email6exam.com, email5exam.comuser1, err : FindUserByEmail(email1)if err nil {t.Error(expected find no user but got:, user1)}user2, err : FindUserByEmail(email2)if err ! nil {t.Error(got an unexpected error:, err)} else if *user2.Email ! email2 {t.Errorf(expected find user which email is %s but got user which email is: %s, email2, *user2.Email)}
}func TestFindUserByUsername(t *testing.T) {username1, username2 : username6, username5user1, err : FindUserByUsername(username1)if err nil {t.Error(expected find no user but got:, user1)}user2, err : FindUserByUsername(username2)if err ! nil {t.Error(got an unexpected error:, err)} else if *user2.Username ! username2 {t.Errorf(expected find user which username is %s but got user which username is: %s, username2, *user2.Username)} else if user2.Email ! nil || user2.Password ! nil {t.Error(expected do not get user email and password but got them now)}
}
在上面的测试代码中我们分别测试了通过ID、Email和Username来查找用户的情况。因为这三个属性在数据库中都是唯一的所以我们每条测试case最多只能找到一条数据。所以我们只测试了找到的这条数据的某一项值是否是我们所期待的值。如果是就表明这条数据就是我们需要的数据如果不是说明我们查找用户的功能代码有问题。我们还检查了查找到的数据中是否包含用户的Email和Password如果包含了的话也不能通过测试。接下来我们就来编写查找用户的代码。因为在查找用户之前我们通常不会事先得到一个用户的实例所以我们直接将查找用户的代码作为函数来编写的。另外我们为FindUserByID()和FindUserByUsername()添加了Select()函数对查询到的数据进行筛选不从数据库中获取用户的email和password信息以保证用户账号的安全。代码清单3-5models/user.go.
.
.
var QueryKey string id, username, message
func FindUserByID(id int) (user User, err error) {DB.Where(id ?, id).Select(QueryKey).Find(user)if user.ID 0 {err errors.New(Cannot find such user)}return
}func FindUserByEmail(email string) (user User, err error) {DB.Where(email ?, email).Find(user)if user.ID 0 {err errors.New(Cannot find such user)}return
}func FindUserByUsername(username string) (user User, err error) {DB.Where(username ?, username).Select(QueryKey).Find(user)if user.ID 0 {err errors.New(Cannot find such user)}return
}
清空数据库然后打开命令行cd到models目录下运行go test。C:Userssxu37GosrcGoWebmodelsgo test
.
.
.
PASS
ok GoWeb/models 3.781s接下来我们为查找用户编写一个接口。在我们的App中我们不会将FindUserByEmail()公开。这个函数将只在App内部被使用。FindUserByUsername()可以作为通过用户名查找用户的功能被公开但是我们目前并不打算先实现这个功能。我们首先实现通过ID来查找用户然后我们通过访问类似“users/1”的路径来获取某个具体的用户的信息。我们先编写测试代码。向users_controller_test.go中增加如下的测试代码代码清单3-6controllers/users_controller_test.gofunc TestFindUserByID(t *testing.T) {id : 1controller : UsersController{}user, err : controller.Show(id)if user.ID 0 || err ! nil {t.Error(expected to show user but error occured:, user, err)}
}
这个测试case很简单就是指定id为1看能不能返回id为1的用户。接着我们就应用代码应用代码也很简单。代码清单3-7controllers/users_controller.gofunc (c *UsersController) BeforeActivation(b mvc.BeforeActivation) {middleware : func(ctx iris.Context) {ctx.Application().Logger()ctx.Next()}b.Handle(POST, /users/new, Create, middleware)b.Handle(GET, /users/{id:int}, Show, middleware)
}func (c *UsersController) Create(ctx iris.Context) (user models.User, err error) {...
}func (c *UsersController) Show(id int) (user models.User, err error) {user, err models.FindUserByID(id)return
}
注意在设置路径的时候我们用/users/{id:int}的形式设置了一个动态路径。int值会通过{id:int}作为id参数传入到Show()方法中。加入我们访问/users/1这个路径那么访问就会被转发到Show()方法并且参数为1即Show(1)。清空数据库cd到controllers目录运行go testC:Userssxu37GosrcGoWebcontrollersgo test
.
.
.
PASS
ok GoWeb/controllers 3.124s接着我们再试一试接口。代码清单3-8main_test.go.
.
.func TestUsersShowRoute(t *testing.T) {app : weiboApp()e : httptest.New(t, app)request : e.Request(GET, /users/1)response : request.Expect()response.Status(httptest.StatusOK)
}
C:Userssxu37GosrcGoWebgo test
.
.
.
PASS
ok GoWeb 2.655s现在我们不仅能创建用户还能根据用户的ID查询一个用户。查询到用户之后我们可以实现对这些用户的资料进行更新和删除。但是仅仅这样操作就会产生一个问题任何用户只要知道其他用户的ID就可以随意地向服务器发送请求去更改、删除其他用户的信息。这明显是很不安全的所以我们需要为更新和删除操作增加一点安全性。大多数App在进行更新和删除操作的时候都需要有先进行登录有些严格的App还需要登录的用户拥有这些操作的权限才可以。在这里我们实现只要用户登录就能对自己的账号进行更新和删除。用户登录那么接下来我们来实现用户登录。用户登录功能的实现最简单的方式就是利用session通过下面几个步骤来实现1. 客户端通过request将用户的邮箱和密码提交到服务器。2. 服务器取得request的数据后会首先从数据库读取该用户的信息并判断数据库和request提交上来的信息是否一致。3. 如果一致的话服务器将该用户的信息记录到session里并将session的标识返回给客户端。4. 客户端收到session的标识之后将它添加到之后每一次request的header里服务器通过识别request的header里的session标识来判断该客户端是否已经登录。一般还会给服务器的session 设置一个有效期一旦某个session生成后超过了一个固定期限即视为过期过期的session也是无效的。在上面的登录过程中我们User模型需要做的就是进行登录验证对客户端request过来的邮箱和密码进行验证并返回一个布尔值来表示验证结果是否通过。首先我们来编写登录验证的测试代码。打开models文件夹新建一个test_auth_data.json然后将下列测试数据保存到该文件中。代码清单3-9models/test_auth_data.json[{email: wrongemail4example.com,password: password4},{email: email4exam.com,password: wrongpassword4},{email: email4exam.com,password: password4}
]然后打开test_user.go稍微修改一下setup()函数然后向其中增加一个TestUserAuthenticate()函数。代码清单3-10models/test_user.go.
.
.
func setup(filename string) (users []User) {file, _ : os.Open(filename)defer file.Close()data, _ : ioutil.ReadAll(file)json.Unmarshal(data, users)return users
}func TestUserCreate(t *testing.T) {users : setup(test_user_data.json)
.
.
.}func TestUserAuthenticate(t *testing.T) {users : setup(test_auth_data.json)if _, err : users[0].Authenticate(); err nil {if err.Error() ! Invalid email or password {t.Errorf(expected get Invalid email or password but got %sn, err.Error())}t.Error(expected authentication fail but it passed)}if _, err : users[1].Authenticate(); err nil {if err.Error() ! Invalid email or password {t.Errorf(expected get Invalid email or password but got %sn, err.Error())}t.Error(expected authentication fail but it passed)}if user, err : users[2].Authenticate(); err ! nil {t.Error(expected authentication pass but it failed cause:, err)} else if user.ID ! 3 {t.Errorf(expected user id to be 3 but got %dn, user.ID)}}
在这份测试代码中我们分别测试了三种情况email不正确、password不正确和两者都正确。前两种情况我们除了验证不通过以外我们还得到了一个Invalid email or password的错误信息。只有email和password都正确的时候我们确认验证通过此时错误为空。接下来我们就为User模型添加Authenticate()方法。代码清单3-11models/user.go.
.
.
func (u *User) Authenticate() (user User, err error) {user, err FindUserByEmail(*u.Email)if err ! nil {return}if user.ID 0 || *user.Password ! Encrypt(*u.Password) {user User{}err errors.New(Invalid email or password)return}return
}
登录验证的代码中我们通过FindUserByEmail()函数寻找数据库中email和待验证的用户的Email一致的数据。将找到的数据的password和待验证的用户的Password进行比较。如果找不到用户或者找到的记录和待验证的用户两者的password不一致那么我们就判定为验证失败并返回一个Invalid email or password错误。如果既能找到记录而且password也一致我们就判定为验证通过。登录验证的代码就编写完成就可以进行测试了。我们首先还是清空数据库DROP TABLE users;然后打开命令行工具cd到models目录下运行go test。C:Userssxu37GosrcGoWebmodelsgo test
.
.
.
PASS
ok GoWeb/models 4.417s说明我们登录验证的代码的行为符合预期。接下来我们还需要在控制器里面添加一个Login()方法我们希望用这个Login()方法来处理用户登录。如果用户登录验证成功Login()方法会返回登录成功的用户信息。如果登录验证失败我们就返回从User#Authenticate()得到的错误。首先我们还是来编写测试代码。打开users_controller_test.go向里面添加下面的函数。代码清单3-12controllers/users_controller_test.go.
.
.
func TestUserLogin(t *testing.T) {app : iris.New()ctx : context.NewContext(app)// 向新创建的ctx中添加一个ResponseWriter用来写入session的信息w : context.AcquireResponseWriter()hw : httptest.NewRecorder()w.BeginResponse(hw)ctx.ResetResponseWriter(w)// 向新创建的ctx中添加一个Request并将文件中的数据读取到Request的Body中file, _ : os.Open(sample_login_user.json)defer file.Close()newRequest, _ : http.NewRequest(POST, /login, nil)newRequest.ContentLength 500newRequest.Body filectx.ResetRequest(newRequest)// 创建一个UsersController的实例并设置该实例的Session属性controller : UsersController{}cookie : http.Cookie{Name: sample_cookie_uuid, Value: }ctx.SetCookie(cookie)sess : sessions.New(sessions.Config{Cookie: weibo_app_cookie})controller.Session sess.Start(ctx)// 调用UsersController实例的Login()方法进行测试user, err : controller.Login(ctx)if err ! nil {t.Error(expected no error, but an error occured:, err)}if user.ID ! 1 {t.Errorf(expected returned user id to be 1, but got %dn:, user.ID)}id, _ : controller.Session.GetInt(userID)if id ! 1 {t.Errorf(expected user id in session to be 1, but got %dn, id)}
}
在上面的代码中我们会从一个叫sample_login_user.json中读取测试数据然后用测试数据作为参数调用Login()方法模拟登录。登录后我们首先检查登录过程有无err以及被登录的用户是否是我们的测试用户接着我们访问Session并用GetInt获得Session里的userID字段。检查Session的userID字段的值是否和测试数据的id是一致的。接着我们还是在controllers目录下新建一个sample_login_user.json文件用来编写测试数据。代码清单3-13controllers/sample_login_user.json{email: email1sample.com,password: password1
}接下来我们打开users_controller.go为UsersController新增一个Session属性和一个Login()方法代码清单3-14controllers/users_controller.gopackage controllersimport (goweb/modelsgithub.com/kataras/irisgithub.com/kataras/iris/mvcgithub.com/kataras/iris/sessions
)type UsersController struct {Session *sessions.Session
}
.
.
.
func (c *UsersController) Login(ctx iris.Context) (user models.User, err error) {if err ctx.ReadJSON(user); err ! nil {ctx.StatusCode(iris.StatusBadRequest)return}if user, err user.Authenticate(); err ! nil {return}c.Session.Set(userID, user.ID)return
}
在Login()方法中我们读取客户端request中的json格式的数据然后将这些数据映射为一个User实例。调用这个User实例的Authenticate()方法进行验证如果验证能够通过我们就将该用户的ID记录到session里并且将验证通过的用户保存到前面我们声明的全局变量里。接下来我们在main.go里将session注册到WeiboApp里。代码清单3-15main.gopackage mainimport (github.com/kataras/irisgithub.com/kataras/iris/middleware/loggergithub.com/kataras/iris/middleware/recovergithub.com/kataras/iris/mvcgithub.com/kataras/iris/sessionsgoweb/controllers
)func main() {app : weiboApp()app.Run(iris.Addr(:8080))
}func weiboApp() *iris.Application {app : iris.New()app.Use(recover.New())app.Use(logger.New())weiboApp : mvc.New(app)expiresTime, _ : time.ParseDuration(168h)sess : sessions.New(sessions.Config{Cookie: weibo_app_cookie, Expires: expiresTime})weiboApp.Register(sess.Start,)helloWorldController : new(controllers.HelloWorldController)usersController : new(controllers.UsersController)weiboApp.Handle(helloWorldController)weiboApp.Handle(usersController)return app
}
我们在创建session的时候设置的session的名字“weibo_app_cookie”和过期的时间168个小时即一个星期。接下来我们清空数据库打开命令行工具cd到controllers目录运行go testC:Userssxu37Gocd srcgowebcontrollers
C:Userssxu37GosrcGoWebcontrollersgo test
.
.
.
PASS
ok GoWeb/controllers 8.059s说明我们用户登录的代码也是编写正确的。接着我们只需要再测试一下”/login“这个路径是否可以用”POST“方法访问即可。代码清单3-16main_test.go.
.
.
func TestLoginRoute(t *testing.T) {app : weiboApp()e : httptest.New(t, app)request : e.Request(POST, /login)request.WithJSON(map[string]interface{}{email: email1example.com, password: password1})response : request.Expect()response.Status(httptest.StatusOK)
}清空数据库然后打开命令行工具cd到项目根目录下运行go testC:Userssxu37GosrcGoWebgo test
.
.
.
PASS
ok GoWeb 3.684s用户登录的功能我们也成功实现了。 我们也还是可以使用crul命令测试一下C:Userssxu37GosrcGoWebcurl -i -X POST -d {email:email1example.co
m,password:password1} “http://localhost:8080/login”服务器按照预期返回了登录成功的用户信息。在开始下一节之前我们还是先把代码push到Github上保管。C:Userssxu37GosrcGoWebgit add –A
C:Userssxu37GosrcGoWebgit commit -m user login
C:Userssxu37GosrcGoWebgit push