企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
# 7.2 注册表单 用户资料页面已经可以访问了,但内容还不完整。下面我们要为网站创建一个注册表单。如[图 5.9](chapter5.html#fig-new-signup-page) 和[图 7.10](#fig-blank-signup-page-recap) 所示,“注册”页面还没有什么内容,无法注册新用户。本节会实现如[图 7.11](#fig-signup-mockup) 所示的注册表单,添加注册功能。 ![user show sidebar css 3rd edition](https://box.kancloud.cn/2016-05-11_5732bd11e853d.png)图 7.9:添加侧边栏和 CSS 后的用户资料页面![new signup page 3rd edition](https://box.kancloud.cn/2016-05-11_5732bd05dca6b.png)图 7.10:注册页面现在的样子 因为我们要实现通过网页创建用户的功能,那么就把 [6.3.4 节](chapter6.html#creating-and-authenticating-a-user)在控制台中创建的用户删除吧。最简单的方法是使用 `db:migrate:reset` 命令: ``` $ bundle exec rake db:migrate:reset ``` 在某些系统中可能还要重启 Web 服务器才能生效。 ![signup mockup bootstrap](https://box.kancloud.cn/2016-05-11_5732bd08d8ea7.png)图 7.11:用户注册页面的构思图 ## 7.2.1 使用 `form_for` 注册页面的核心是一个表单,用于提交注册相关的信息(名字,电子邮件地址,密码和密码确认)。在 Rails 中,创建表单可以使用 `form_for` 辅助方法,传入 Active Record 对象后,使用该对象的属性构建一个表单。 注册页面的地址是 /signup,由用户控制器的 `new` 动作处理([代码清单 5.33](chapter5.html#listing-signup-route))。首先,我们要创建传给 `form_for` 的用户对象,然后赋值给 `@user` 变量,如[代码清单 7.12](#listing-new-action-with-user) 所示。 ##### 代码清单 7.12:在 `new` 动作中添加 `@user` 变量 app/controllers/users_controller.rb ``` class UsersController < ApplicationController def show @user = User.find(params[:id]) end def new @user = User.new end end ``` 表单的代码参见[代码清单 7.13](#listing-signup-form)。[7.2.2 节](#signup-form-html)会详细分析这个表单,现在我们先添加一些 SCSS,如[代码清单 7.14](#listing-form-css) 所示。(注意,这里重用了[代码清单 7.2](#listing-mixin-and-debug) 中的混入。)添加样式后的注册页面如[图 7.12](#fig-signup-form) 所示。 ##### 代码清单 7.13:用户注册表单 app/views/users/new.html.erb ``` <% provide(:title, 'Sign up') %> <h1>Sign up</h1> <div class="row"> <div class="col-md-6 col-md-offset-3"> <%= form_for(@user) do |f| %> <%= f.label :name %> <%= f.text_field :name %> <%= f.label :email %> <%= f.email_field :email %> <%= f.label :password %> <%= f.password_field :password %> <%= f.label :password_confirmation, "Confirmation" %> <%= f.password_field :password_confirmation %> <%= f.submit "Create my account", class: "btn btn-primary" %> <% end %> </div> </div> ``` ##### 代码清单 7.14:注册表单的样式 app/assets/stylesheets/custom.css.scss ``` . . . /* forms */ input, textarea, select, .uneditable-input { border: 1px solid #bbb; width: 100%; margin-bottom: 15px; @include box_sizing; } input { height: auto !important; } ``` ![signup form 3rd edition](https://box.kancloud.cn/2016-05-11_5732bd12a7786.png)图 7.12:用户注册页面 ## 7.2.2 注册表单的 HTML 为了能更好地理解[代码清单 7.13](#listing-signup-form) 中定义的表单,可以分成几段来看。我们先看外层结构——开头在 ERb 中调用 `form_for` 方法,结尾是 `end`: ``` <%= form_for(@user) do |f| %> . . . <% end %> ``` 这段代码中有关键字 `do`,说明 `form_for` 方法可以接受一个块,而且有一个块变量 `f`(代表表单)。 我们一般无需了解 Rails 辅助方法的内部实现,但是对于 `form_for` 来说,我们要知道 `f` 对象的作用是什么:在这个对象上调用[表单字段](http://www.w3schools.com/html/html_forms.asp)(例如,文本字段、单选按钮和密码字段)对应的方法,生成的字段元素可以用来设定 `@user` 对象的属性。也就是说: ``` <%= f.label :name %> <%= f.text_field :name %> ``` 生成的 HTML 是一个有标注(label)的文本字段,用来设定用户模型的 `name` 属性。 在浏览器中按右键,然后选择“审查元素”,会看到页面的源码,如[代码清单 7.15](#listing-signup-form-html) 所示。下面花点儿时间介绍一下表单的结构。 ##### 代码清单 7.15:[图 7.12](#fig-signup-form) 中表单的源码 ``` <form accept-charset="UTF-8" action="/users" class="new_user" id="new_user" method="post"> <input name="utf8" type="hidden" value="&#x2713;" /> <input name="authenticity_token" type="hidden" value="NNb6+J/j46LcrgYUC60wQ2titMuJQ5lLqyAbnbAUkdo=" /> <label for="user_name">Name</label> <input id="user_name" name="user[name]" type="text" /> <label for="user_email">Email</label> <input id="user_email" name="user[email]" type="email" /> <label for="user_password">Password</label> <input id="user_password" name="user[password]" type="password" /> <label for="user_password_confirmation">Confirmation</label> <input id="user_password_confirmation" name="user[password_confirmation]" type="password" /> <input class="btn btn-primary" name="commit" type="submit" value="Create my account" /> </form> ``` 先看表单里的结构。比较一下[代码清单 7.13](#listing-signup-form) 和[代码清单 7.15](#listing-signup-form-html),我们发现,下面的 ERb 代码 ``` <%= f.label :name %> <%= f.text_field :name %> ``` 生成的 HTML 是 ``` <label for="user_name">Name</label> <input id="user_name" name="user[name]" type="text" /> ``` 下面的 ERb 代码 ``` <%= f.label :password %> <%= f.password_field :password %> ``` 生成的 HTML 是 ``` <label for="user_password">Password</label> <input id="user_password" name="user[password]" type="password" /> ``` 如[图 7.13](#fig-filled-in-form) 所示,文本字段(`type="text"`)会直接显示填写的内容,而密码字段(`type="password"`)基于安全考虑会遮盖输入的内容。 ![filled in form bootstrap 3rd edition](https://box.kancloud.cn/2016-05-11_5732bd12c5540.png)图 7.13:在表单的文本字段和密码字段中填写内容 [7.4 节](#successful-signups)会介绍,之所以能创建用户,全靠 `input` 元素的 `name` 属性: ``` <input id="user_name" name="user[name]" - - - /> . . . <input id="user_password" name="user[password]" - - - /> ``` [7.3 节](#unsuccessful-signups)会介绍,Rails 会以这些 `name` 属性的值为键,用户输入的内容为值,构成一个名为 `params` 的哈希,用来创建用户。 另外一个重要的标签是 `form`。Rails 使用 `@user` 对象创建这个 `form` 元素,因为每个 Ruby 对象都知道它所属的类([4.4.1 节](chapter4.html#constructors)),所以 Rails 知道 `@user` 所属的类是 `User`,而且,`@user` 是一个新用户,Rails 知道要使用 `post` 方法——这正是创建新对象所需的 HTTP 请求(参见[旁注 3.2](chapter3.html#aside-get-etc)): ``` <form action="/users" class="new_user" id="new_user" method="post"> ``` 这里的 `class` 和 `id` 属性并不重要,重要的是 `action="/users"` 和 `method="post"`。设定这两个属性后,Rails 会向 /users 发送 `POST` 请求。接下来的两节会介绍这个请求的效果。 你可能还会注意到,`form` 标签中有下面这段代码: ``` <div style="display:none"> <input name="utf8" type="hidden" value="&#x2713;" /> <input name="authenticity_token" type="hidden" value="NNb6+J/j46LcrgYUC60wQ2titMuJQ5lLqyAbnbAUkdo=" /> </div> ``` 这段代码不会在浏览器中显示,只在 Rails 内部有用,所以你并不需要知道它的作用。简单来说,这段代码首先使用 Unicode 字符 `&#x2713;`(对号 ✓)强制浏览器使用正确的字符编码提交数据,然后是一个“真伪令牌”(authenticity token),Rails 用它抵御“跨站请求伪造”(Cross-Site Request Forgery,简称 CSRF)攻击。[[8](#fn-8)]