注册页面成功显示之后,我们开始实现注册功能~~
1、功能描述
(1)注册时需要填写信息:用户名、邮箱、密码、确认密码、选择角色
(2)用户角色分为普通用户、管理员、超级管理员,后期方便对用户进行权限管理
(3)邮箱要求唯一,注册成功发送欢迎邮件到注册邮箱(之后章节讲解)
(4)密码需要通过SSL进行加密之后,存入到数据库
(5)用户名的长度规定小于等于5个字符、密码和确认密码需要一致
2、编辑controller、view、路由
(1)在路由文件config/routes.rb中添加路由,通过此链接来提交在注册页面填写的信息
代码解析:
-
post 'create_account' => 'accounts#create_account'
下面我们会将signup.html.erb文件中form_for后面的链接改成/create_account
,这样点击注册页面的注册按钮,在注册页面填写的数据会通过/create_account链接提交,浏览器通过该链接在路由文件中的指向,找到accounts_controller.rb文件中的create_account方法,在注册页面填写的数据在create_account方法中处理
(2)编辑views/accounts/signup.html.erb文件,将form_for后面的链接改成/create_account
。
这样在点击注册按钮后,注册页面上填写的信息会提交到/create_account链接对应的create_account方法
(3)在app/controllers/accounts_controller.rb中添加create_account方法。
我们只添加方法,先不处理数据,下面我们在终端先看一下数据的传递形式。
再来强调一下网页请求路径:
用户在注册页面填写相关的信息后,点击注册按钮会将数据提交到/create_account链接对应在accounts_controller.rb文件中的create_account方法,在create_account方法中将提交的数据进行处理
PS:之所以多次强调网页请求路径,是因为理解了这个,你才知道为什么我们代码要这样写,才能对项目有一个大体的掌握
3、获取页面提交的数据,在数据库accounts表新建用户
(1)rails s启动项目,在浏览器中打开注册页面http://localhost:3000/signup(注意mac电脑是http://192.168.33.10:3000/signup)随便填写信息,点击注册按钮,回到终端,能看到终端返回的日志中包含以下params数据。
Parameters: {"utf8"=>"✓", "account"=>{"name"=>"xzn", "email"=>"xzn@stonescloud.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "role"=>"0"}, "commit"=>"注册”}
我们在controller中的create_account方法中操作的就是上面的数据,params是一个二级哈希,里面有两个key分别为utf8、account,其中account又指向了一个第二级的哈希,这个哈希里面有5个key分别为name、email、password、password_confirmation、role,在create_account方法中取出email的值应该像下面这样:
(2)回到sublime,编辑我们刚刚在accounts_controller.rb中添加的create_account方法,在方法中取params哈希里面的值,进行if判断、保存到数据库等操作
代码解析:
-
account = Account.find_by(email:email)
find_by我们在3.2节讲过,上面的语句相当于Account.where(email:email).first
。
Account.where(email:email)
返回的结果类型是ActiveRecord::Relation对象集合,而不是Account对象,不能直接使用,需要加first得到Account对象,才能调用Account对象里面的role、status等字段 -
@account = Account.new
先创建一个Account类的对象,命名为@account,@account是一个实例变量,里面保存了Account对象的内存地址,这个对象的name、email等字段我们会在下面赋值。为什么我们要在if name.blank? || email.blank?
语句的上面定义这条语句呢?因为if语句里面有render :signup
会用到@account这个实例变量。具体为什么会用到,在下面第四节讲解 -
flash.notice = "用户名或者邮箱不能为空"
3.3章第6节第(4)点,在app/views/layouts/application.html.erb文件中添加的代码,这些代码实现了flash.notice的效果。
flash.notice的用法:
在controller文件中给flash.notice赋值,后面一定要跟render语句或者redirect_to语句。因为flash.notice语句在加载下一个页面(render、redirect_to对应的页面)才生效。
-
render :signup
意思是回到注册页面,相当于代码:
render "signup"
render "signup.html.erb"
render action: :signup
render action: "signup.html.erb"
render template: "accounts/signup"
render template: "accounts/signup.html.erb"
这些情况不需要记住,知道有这种写法就行,以后碰到类似的代码避免疑惑。 -
redirect_to :login
注册成功后,重定向到登录页面,相当于代码:
redirect_to :action => 'login'
4、redirect_to和render的区别及应用
(1)区别
- redirect_to:浏览器会发送一个新的请求,执行这个请求对应的action(即controller中的实例方法),再渲染action对应的页面。
举例:**redirect_to :login
**的意思是会执行对应controller中的login方法(action),然后再渲染login.html.erb页面 - render:浏览器不会发送新的请求,不执行action,直接渲染action对应的页面。
举例:**render :login
**的意思是直接渲染login.html.erb页面,不会执行login方法(action)。
(2)我们来看一段create_account方法中的代码,为什么要在if控制结构外面先定义一个@account实例变量呢?
代码解析:
需要先看一下**render :signup
**对应的signup.html.erb文件里面,有这样一条语句:
<%= form_for @account,url: "/create_account" do |f| %>
这条语句中的@account实例变量是在action方法signup中定义的,然后再传给signup.html.erb文件。
signup方法如下:
根据我们上面讲的「区别」,render不会执行action方法直接渲染页面,所以执行render :signup之后,不会执行controller中signup方法,会直接渲染signup.html.erb页面,所以@account实例变量不会被定义,上面的语句就会报错。
所以我们在执行render :signup语句之前,需要先确保@account被定义了。
(3)什么时候用redirect_to,什么时候用render呢?我们再截取create_account方法中的一段代码来看一下
boolean为true时,说明account在数据库保存成功,为什么这时用**redirect_to :login
,而不是用render :login
**呢?
当使用 render ,用户提交表单之后紧接着刷新页面,此时表单就会重复被提交,数据可能会出现重复数据
当使用 redirect_to,用户点击注册按钮提交表单,提交表单之后紧接着刷新页面,此时浏览器的请求链接是服务器重定向到/login链接,而不是/create_account链接。刷新页面时的/create_account链接与重定向/login链接不重复,所以不会出现重复提交数据的情况。
当涉及到表单提交时,数据库保存成功时使用 redirect_to ,保存失败使用 render ,使用render除了避免表单重复提交之外,如果表单提交失败,之前表单填写的信息还会存在,提高用户体验。
5、密码加密保存
SSL信息加密:
就是把明码的输入文件用加密算法转换成加密的文件以实现数据的保密。加密的过程需要用到密钥来加密数据然后再解密。没有了密钥,就无法解开加密的数据。
加密解密方法的内容我们就不详细解释了,大家就知道这是通过密钥加密的技术就可以。之所以将这个文件在lib文件夹下新建,我们再1.3节中提到过,lib文件夹下放一些自定的module、class
(1)创建lib/des.rb文件,复制下面内容
(2)编辑accounts_controller.rb文件,create_account方法中
(3)修改config/application.rb配置文件,使程序能加载lib文件夹下的文件,添加下面代码
如果注册时候报错,NameError: uninitialized constant Des
那就是config.autoload_paths += %W(#{config.root}/lib)
这条语句没有生效,这个时候退出vagrant,运行vagrant ssh
重新登录vagrant就可以了。