多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
# 7.4 注册成功 处理完提交无效数据的情况,本节我们要完成注册表单的功能,如果提交的数据有效,就把用户存入数据库。我们先尝试保存用户,如果保存成功,用户的数据会自动存入数据库,然后在浏览器中重定向,转向新注册用户的资料页面,页面中还会显示一个欢迎消息,构思图如[图 7.19](#fig-signup-success-mockup) 所示。如果保存用户失败了,就交由上一节实现的功能处理。 ![signup success mockup bootstrap](https://box.kancloud.cn/2016-05-11_5732bd165d829.png)图 7.19:注册成功后显示的页面构思图 ## 7.4.1 完整的注册表单 要完成注册表单的功能,我们要把[代码清单 7.17](#listing-create-action-strong-parameters) 中的注释换成适当的代码。现在,提交有效数据时也不能正确处理,如[图 7.20](#fig-valid-submission-error) 所示,因为 Rails 动作的默认行为是渲染对应的视图,而 `create` 动作不对应视图。 ![valid submission error](https://box.kancloud.cn/2016-05-11_5732bd1678e77.png)图 7.20:提交有效注册信息后显示的错误页面 成功注册后,我们不需要渲染页面,而要重定向到另一个页面。按照习惯,我们要重定向到新注册用户的资料页面,不过转到根地址也行。为此,在应用代码中要使用 `redirect_to` 方法,如[代码清单 7.23](#listing-user-create-action) 所示。 ##### 代码清单 7.23:`create` 动作的代码,处理保存和重定向操作 app/controllers/users_controller.rb ``` class UsersController < ApplicationController . . . def create @user = User.new(user_params) if @user.save redirect_to @user else render 'new' end end private def user_params params.require(:user).permit(:name, :email, :password, :password_confirmation) end end ``` 注意,我们写的是: ``` redirect_to @user ``` 不过,也可以写成: ``` redirect_to user_url(@user) ``` Rails 看到 `redirect_to @user` 后,知道我们想重定向到 `user_url(@user)`。 ## 7.4.2 闪现消息 有了[代码清单 7.23](#listing-user-create-action) 中的代码后,注册表单已经可以使用了。不过在提交有效数据注册之前,我们要添加一个 Web 应用中经常使用的增强功能:访问随后的页面时显示一个消息(这里,我们要显示一个欢迎新用户的消息),如果再访问其他页面,或者刷新页面,这个消息要消失。 在 Rails 中,短暂显示一个消息使用“闪现消息”(flash message)实现。按照 Rails 的约定,操作成功时使用 `:success` 键表示,如[代码清单 7.24](#listing-signup-flash) 所示。 ##### 代码清单 7.24:用户注册成功后显示一个闪现消息 app/controllers/users_controller.rb ``` class UsersController < ApplicationController . . . def create @user = User.new(user_params) if @user.save flash[:success] = "Welcome to the Sample App!" redirect_to @user else render 'new' end end private def user_params params.require(:user).permit(:name, :email, :password, :password_confirmation) end end ``` 把一个消息赋值给 `flash` 之后,我们就可以在重定向后的第一个页面中将其显示出来了。我们要遍历 `flash`,在网站布局中显示所有相关的消息。你可能还记得 [4.3.3 节](chapter4.html#hashes-and-symbols)在控制台中遍历哈希那个例子,当时我故意把变量命名为 `flash`: ``` $ rails console >> flash = { success: "It worked!", danger: "It failed." } => {:success=>"It worked!", danger: "It failed."} >> flash.each do |key, value| ?> puts "#{key}" ?> puts "#{value}" >> end success It worked! danger It failed. ``` 按照上述方式,我们可以使用如下的代码在网站的全部页面中显示闪现消息的内容: ``` <% flash.each do |message_type, message| %> <div class="alert alert-<%= message_type %>"><%= message %></div> <% end %> ``` (这段代码很乱,混用了 HTML 和 ERb,[7.7 节](#sign-up-exercises)中有一题会要求你把它变得好看一些。) 其中 ``` alert-<%= message_type %> ``` 为各种类型的消息指定一个 CSS 类,因此,`:success` 消息的类是 `alert-success`。(`:success` 是个符号,ERb 会自动把它转换成字符串 `"success"`,然后再插入模板。) 为不同类型的消息指定不同的 CSS 类,可以为不同类型的消息指定不同的样式。例如,[8.1.4 节](chapter8.html#rendering-with-a-flash-message)会使用 `flash[:danger]` 显示登录失败消息。[[11](#fn-11)](其实,在[代码清单 7.19](#listing-errors-partial) 中为错误消息区域指定样式时,已经用过 `alert-danger`。)Bootstrap 提供的 CSS 支持四种闪现消息样式,分别为 `success`,`info`,`warning` 和 `danger`,在开发这个演示应用的过程中,我们会找机会全部使用一遍。 消息也会在模板中显示,如下代码: ``` flash[:success] = "Welcome to the Sample App!" ``` 得到的完整 HTML 是: ``` <div class="alert alert-success">Welcome to the Sample App!</div> ``` 把前面的 ERb 代码放入网站的布局中,得到的布局如[代码清单 7.25](#listing-layout-flash) 所示。 ##### 代码清单 7.25:在网站的布局中添加闪现消息 app/views/layouts/application.html.erb ``` <!DOCTYPE html> <html> . . . <body> <%= render 'layouts/header' %> <div class="container"> <% flash.each do |message_type, message| %> <div class="alert alert-<%= message_type %>"><%= message %></div> <% end %> <%= yield %> <%= render 'layouts/footer' %> <%= debug(params) if Rails.env.development? %> </div> . . . </body> </html> ``` ## 7.4.3 首次注册 现在我们可以注册一个用户,看看到目前为止所实现的功能。用户的名字使用“Rails Tutorial”,电子邮件地址使用“example@railstutorial.org”,如[图 7.21](#fig-first-signup) 所示。注册成功后,页面中显示了一个友好的欢迎消息,如[图 7.22](#fig-signup-flash) 所示。消息的样式是由 [5.1.2 节](chapter5.html#bootstrap-and-custom-css)集成的 Bootstrap 框架提供的 `.success` 类实现。(如果无法注册,提示电子邮件地址已经使用,确保按照 [7.2 节](#signup-form)的说明,执行了 `db:migrate:reset` Rake 任务,而且重启了开发服务器。)刷新用户资料页面后,闪现消息会消失,如[图 7.23](#fig-signup-flash-reloaded) 所示。 ![first signup](https://box.kancloud.cn/2016-05-11_5733304b95cb8.png)图 7.21:首次注册时填写的信息![signup flash 3rd edition](https://box.kancloud.cn/2016-05-11_5733304bb36eb.png)图 7.22:注册成功后显示有闪现消息的页面![signup flash reloaded 3rd edition](https://box.kancloud.cn/2016-05-11_5733304bcadbd.png)图 7.23:刷新页面后资料页面中的闪现消息不见了 我们还可以检查一下数据库,确保真得创建了新用户: ``` $ rails console >> User.find_by(email: "example@railstutorial.org") => #<User id: 1, name: "Rails Tutorial", email: "example@railstutorial.org", created_at: "2014-08-29 19:53:17", updated_at: "2014-08-29 19:53:17", password_digest: "$2a$10$zthScEx9x6EkuLa4NolGye6O0Zgrkp1B6LQ12pTHlNB..."> ``` ## 7.4.4 注册成功的测试 在继续之前,我们要编写测试,确认提交有效信息后应用的表现正常,并捕获可能出现的回归。和 [7.3.4 节](#a-test-for-invalid-submission)中注册失败的测试一样,我们主要是检查数据库中的内容。这一次,我们要提交有效的数据,确认创建了一个用户。类似[代码清单 7.21](#listing-a-test-for-invalid-submission) 中使用的 ``` assert_no_difference 'User.count' do post users_path, ... end ``` 这里我们要使用对应的 `assert_difference` 方法: ``` assert_difference 'User.count', 1 do post_via_redirect users_path, ... end ``` 和 `assert_no_difference` 一样,`assert_difference` 的第一个参数是字符串 `'User.count'`,目的是比较块中的代码执行前后 `User.count` 的变化。第二个参数可选,指定变化的数量(这里是 1)。 把 `assert_difference` 加入[代码清单 7.21](#listing-a-test-for-invalid-submission) 后,得到的测试如[代码清单 7.26](#listing-a-test-for-valid-submission) 所示。注意,请求用户的资料页面时,使用的是 `post_via_redirect` 方法,目的是提交数据后继续跟踪重定向,渲染 `users/show` 模板。(针对闪现消息的测试留作[练习](#sign-up-exercises)。) ##### 代码清单 7.26:注册成功的测试 GREEN test/integration/users_signup_test.rb ``` require 'test_helper' class UsersSignupTest < ActionDispatch::IntegrationTest . . . test "valid signup information" do get signup_path name = "Example User" email = "user@example.com" password = "password" assert_difference 'User.count', 1 do post_via_redirect users_path, user: { name: name, email: email, password: password, password_confirmation: password } end assert_template 'users/show' end end ``` 注意,这个测试还确认了成功注册后会渲染 `show` 视图。如果想让测试通过,用户资源的路由([代码清单 7.3](#listing-users-resource)),用户控制器中的 `show` 动作([代码清单 7.5](#listing-user-show-action)),以及 `show.html.erb` 视图([代码清单 7.8](#listing-user-show-view-with-gravatar))都得能正常使用才行。所以, ``` assert_template 'users/show' ``` 这一行代码就能测试用户资料页面几乎所有的相关功能。这种对应用中重要功能的端到端覆盖展示了集成测试的重大作用。