Ruby on Rails 通过邮箱来发送内容为6位随机数的验证码
发邮件
rails提供了发邮件的功能,跟着文档来即可
- 创建 Mailer
bin/rails generate mailer User
- 指定发送者邮箱
# app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
default from: "chili@x.com"
layout "mailer"
end
- 添加 welcome_email 的方法
该方法将向用户注册的电子邮件地址发送电子邮件
# app/mailers/user_mailer.rb
class UserMailer < ApplicationMailer
def welcome_email(code)
@code = code
mail(to: "apple@x.com", subject: 'hi')
end
end
- 编辑邮箱正文
# views/user_mailer/welcome_email.html.erb
<!DOCTYPE html>
<html>
<head>
<meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
</head>
<body>
你正在登录池理记账,验证码为:<code><%= @code %></code>
</body>
</html>
- 配置邮箱参数
在qq邮箱的设置界面开启第三方服务可获取授权码。
由于授权码不可公开,需要将授权码添加至密钥
# config/environments/$RAILS_ENV.rb
config.action_mailer.raise_delivery_errors = true
config.action_mailer.perform_caching = false
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
address: 'smtp.qq.com',
port: 587,
domain: 'smtp.qq.com',
user_name: 'chili@x.com',
password: Rails.application.credentials.email_password,
authentication: 'plain',
enable_starttls_auto: true,
open_timeout: 10,
read_timeout: 10
}
- 测试 开启服务
bin/rails c
发送验证码
UserMailer.welcome_email(1234).deliver
控制台不报错,查看邮箱是否收到验证码。
请求接口生成验证码
- modal 创建模型 确定字段和限制
bin/rails g model ValidationCode email:string kind:string used_at:datetime
同步数据库
bin/rails db:migrate
- router
Rails.application.routes.draw do
resources :validation_codes, only: [:create]
end
- controller
使用SecureRandom生成随机字符串
bin/rails g controller validation_codes create
class ValidationCodesController < ApplicationController
def create
code = SecureRandom.random_number.to_s[2..7]
validation_code = ValidationCode.new email: params[:email], kind: 'sign_in', code: code
if validation_code.save
head 200
else
render json: {
errors: validation_code.errors
}
end
end
end
- 测试
- 使用curl
curl -X POST http://127.0.0.1:3000/api/v1/validation_codes\?email\='apple@x.com' -v
- rspec request
bin/rails generate rspec:request validation_codes
# spec/requests/validation_codes_spec.rb
require 'rails_helper'
RSpec.describe "ValidationCodes", type: :request do
describe "create validationCode" do
it "send" do
post '/validation_codes', params: {email: 'apple@x.com'}
expect(response).to have_http_status(200)
end
end
end
验证
bundle exec rspec
将验证码发送到邮箱
- 找最新的 code
# app/mailers/user_mailer.rb
class UserMailer < ApplicationMailer
def welcome_email(email)
validation_code = ValidationCode.order(created_at: :desc).find_by_email(email)
@code = validation_code.code
mail(to: email, subject: '池理记账验证码')
end
end
- 60s不能重复发验证码并且返回状态码429
# app/controllers/api/v1/validation_codes_controller.rb
class ValidationCodesController < ApplicationController
def create
if ValidationCode.exists?(email: params[:email], kind:'sign_in',created_at: 1.minute.ago..Time.now)
render status: :too_many_requests
return
end
code = SecureRandom.random_number.to_s[2..7]
validation_code = ValidationCode.new email: params[:email], kind: 'sign_in', code: code
if validation_code.save
UserMailer.welcome_email(validation_code.email)
render status: 200
else
render json: {
errors: validation_code.errors
}
end
end
end
- 测试
使用 rspec request
# spec/requests/validation_codes_spec.rb
require 'rails_helper'
RSpec.describe "ValidationCodes", type: :request do
describe "验证码" do
it "发送太频繁就会返回 429" do
post '/api/v1/validation_codes', params: {email: 'chili_sauce@qq.com'}
expect(response).to have_http_status(200)
post '/api/v1/validation_codes', params: {email: 'chili_sauce@qq.com'}
expect(response).to have_http_status(429)
end
end
end
验证
rspec
也可通过控制台验证
bin/rails c
validation_code = ValidationCode.new email: 'chili_sauce@qq.com', kind: 'sign_in', code: 1111
validation_code.save
UserMailer.welcome_email('chili_sauce@qq.com').deliver
重构
使用勾子函数 简化 controller
# app/models/validation_code.rb
class ValidationCode < ApplicationRecord
# email必填
validates :email, presence: true
enum kind: {sign_in: 0, reset_password: 1}
# before_create 只在创建数据库项目时执行
before_create :generate_code
after_create :send_email
def generate_code
self.code = SecureRandom.random_number.to_s[2..7]
end
def send_email
UserMailer.welcome_email(self.email)
end
end
# app/controllers/api/v1/validation_codes_controller.rb
class ValidationCodesController < ApplicationController
def create
if ValidationCode.exists?(email: params[:email], kind:'sign_in',created_at: 1.minute.ago..Time.now)
render status: :too_many_requests
return
end
validation_code = ValidationCode.new email: params[:email], kind: 'sign_in'
if validation_code.save
render status: 200
else
render json: {errors: validation_code.errors}, status: 400
end
end
end
如何添加密码?
执行
EDITOR="code --wait" bin/rails credentials:edit
在新文件中写入 email_password: xxx,
通过Rails.application.credentials.email_password 获取对应的值。