ruby-on-rails - 使用 ActiveModel 和 Forms 在 Rails 中丢失和不准确的有效负载
问题描述
我有一个Organisation
类似的模型
class Organisation
include ActiveModel::Model
attr_accessor :orguid,
:title, :firstname, :lastname, :role, :telephone, :extension, :email,
:name, :branch, :address1, :address2, :address3, :city, :state, :country, :zip
end
在我的控制器中,我有以下操作:
# frozen_string_literal: true
require 'cgi'
require 'json'
class OrganisationsController < ApplicationController
include Secured
before_action :set_api, only: %i[dashboard create]
before_action :user_info, only: %i[dashboard register]
def dashboard
@registration = @api.registered?
end
def register
@organisation = Organisation.new
end
def create
organisation_params
register_data = params[:organisation].to_h
register_data['oruid'] = org_uid
@api.register(register_data)
end
private
def set_api
@api = CoreApi.new(org_uid)
end
def user_info
@user_info = session[:userinfo].to_h
end
def org_uid
CGI.escape(user_info['uid'])
end
def organisation_params
params.require(:organisation).permit!
end
end
在我的register.html.erb
我有:
<h1> Register Your Organisation</h1>
<%= form_with model: @organisation, url: org_register_path do |f| %>
<div class="container">
<h2>Your Details</h2>
<div class="form-row">
<div class="form-group col-md-2">
<%= f.label :title %>
<%= f.text_field :title, class: 'form-control' %>
</div>
<div class="form-group col-md-5">
<%= f.label :first_name %>
<%= f.text_field :firstname, class: 'form-control' %>
</div>
<div class="form-group col-md-5">
<%= f.label :last_name %>
<%= f.text_field :lastname, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-12">
<%= f.label :role %>
<%= f.text_field :role, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<%= f.label :telephone %>
<%= f.telephone_field :telephone, class: 'form-control' %>
</div>
<div class="form-group col-md-2">
<%= f.label :extension %>
<%= f.text_field :extension, class: 'form-control' %>
</div>
<div class="form-group col-md-6">
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control', readonly:'', value: @user_info['info']['name'] %>
</div>
</div>
</div>
<div class="container">
<h2>Organisation Details</h2>
<div class="form-row">
<div class="form-group col-md-6">
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
</div>
<div class="form-group col-md-6">
<%= f.label :branch %>
<%= f.text_field :branch, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-12">
<%= f.label :address_line_1 %>
<%= f.text_field :address1, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-12">
<%= f.label :address_line_2 %>
<%= f.text_field :address2, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-12">
<%= f.label :address_line_3 %>
<%= f.text_field :address3, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<%= f.label :city %>
<%= f.text_field :city, class: 'form-control' %>
</div>
<div class="form-group col-md-4">
<%= f.label :state %>
<%= f.text_field :state, class: 'form-control' %>
</div>
<div class="form-group col-md-4">
<%= f.label :country %>
<%= f.text_field :country, class: 'form-control' %>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-2">
<%= f.label :zip %>
<%= f.text_field :zip, class: 'form-control' %>
</div>
</div>
</div>
<div class="container">
<div class="form-row">
<div class="form-group col-md-12">
<%= f.button :Register, class: 'btn btn-primary' %>
</div>
</div>
</div>
<% end %>
最后register
我的方法core_api.rb
是这样的:
def register(data)
body = data.to_json
puts ">> >> >> >> #{body.class} :: #{body}"
options = { headers: { 'Content-Type' => 'application/json' }, body: body }
response = self.class.post('/organisations', options)
#puts ">>>>>>>>>>>> #{response}"
end
最后我的routes.rb
文件包含:
Rails.application.routes.draw do
get '/' => 'home#show'
get '/auth/auth0/callback' => 'auth0#callback'
get '/auth/failure' => 'auth0#failure'
get '/logout', to: 'logout#logout', as: 'logout'
get '/organisations/dashboard', to: 'organisations#dashboard', as: 'org_dashboard'
get '/organisations/register', to: 'organisations#register', as: 'org_register'
post '/organisations/register', to: 'organisations#create'
root 'home#show'
end
现在,当我运行服务器并在日志中提交表单时,我得到:
>> >> >> >> String :: {"title":"","firstname":"","lastname":"","role":"","telephone":"","extension":"","email":"alijy3@yahoo.com","name":"we","branch":"we","address1":"we","address2":"","address3":"","city":"we","state":"","country":"we","zip":"","oruid":"auth0%7C5e5388493d670c11be833bca","contact_id":0}
在我看来,这很合适json
。但是,由于 api 响应一直不成功,我用Postman拦截了传出的帖子,以查看正在发送的有效负载。令我惊讶的是,有效载荷不是平面 json,而是这样的:
我有两个问题:
- 该 api 接受诸如
address1
,address2
,city
等之类的项目。我相信我应该发送那些而不是当前显示organisation[address1]
的 ,organisation[address2]
等。 - 第二个问题是我在提交表单之后和调用/发布到 api 之前添加了 orguid。但是,虽然我可以在日志消息中看到它,但我没有在邮递员有效负载中看到任何形式的 orguid。
我在服务器上没有任何数据库。一切都是通过 api 获取/发布/保存的。我一直在阅读有关如何使用 Activemodel 和表单的信息,但我还没有设法解决这个问题。任何帮助或解释将不胜感激。
解决方案
没有冒犯,但这是火车残骸。您不需要仅仅因为在这种特定情况下没有使用 ActiveRecord 就打破每个 rails 约定。
从使用ActiveModel::Attributes#attribute
而不是 Ruby 内置的attr_accessor
.
class Organisation
include ActiveModel::Model
include ActiveModel::Attributes
[:orguid, :title, :firstname, :lastname, :role, :telephone,
:extension, :email, :name, :branch,
:address1, :address2, :address3, :city, :state, :country, :zip]
.each do |name|
attribute name
end
# @todo write validations!
end
这会创建类似于 ActiveRecord 属性的属性,您可以使用@organization.as_json
.
然后让我们在那个控制器上重新开始,因为它的气味太多了,不值得打捞。
# routes.rb
resources :organisations, only: [:new, :create]
class OganizationsController < ApplicationController
# GET /organizations/new
def new
@organization = Organization.new
end
# POST /organizations
def create
# You never manually parse out incoming params - thats Rack's job.
# also since you have a model - USE IT!
@organization = Organization.new(organization_params) do |o|
o.orguid = org_uid
end
# validate the user input before you send it to an external API
if @organization.valid? && @api.register(@organization)
redirect_to '/somewhere'
else
render :new
end
end
private
# use monads here instead of callbacks!
def user_info
# Rails will serialize/deserialize hashes automatically
# from the session
session[:userinfo]
end
def org_uid
# Have no clue what the heck you're doing with CGI escape.
@org_uid ||= user_info['uid']
end
def api
@api ||= CoreApi.new(org_uid)
end
def organization_params
# You don't have any reason to use 'permit!' and give
# yourself a potential mass assignment vunerablity
params.require(:organization)
.permit(
:title, :firstname, :lastname, :role, :telephone,
:extension, :email, :name, :branch,
:address1, :address2, :address3, :city,
:state, :country, :zip
)
end
end
重命名视图/organizations/new.html.rb
。此时,您应该能够存根 API 并使用有效和无效输入进行集成测试。
整个session[:userinfo]
事情仍然闻起来很糟糕 - 如果您从 OAuth 获取响应并将其推送到会话中,那么您将自己设置为非常糟糕的时间,因为这可能会导致 cookie 溢出。同样在 Rails 中,如果您曾经手动进行强制转换/序列化,那么这是一个非常好的迹象,表明您做错了什么。
不知道您的 CoreApi 类中发生了什么,但如果您使用的是 HTTParty,则不应执行任何手动 JSON 编码。
# @fixme name is way to generic.
class CoreApi
include HTTParty
format :json # sets content type and encodes the content
# ...
def register(organization)
response = self.class.post('/organisations', @organization.as_json)
if response.success?
true
else
@organization.errors.add(:base, 'Could not be registered')
false
end
end
end
推荐阅读
- c++ - 无法理解 new int(2) 的含义
- javascript - Vigenère Cipher 大多数测试通过了编码,但有些不起作用
- python - 对每个标签值进行分组
- spring-boot - 如何创建 Spring Boot 测试套件
- rust - 如何在 Wasm 出现恐慌后触发 Rust Mutex 的释放,以便将来的调用正常?
- react-native - react native提交后如何退出webview
- matlab - 如何通过 MATLAB GUI 中的滑块移动轴中的垂直线?
- mysql - 如何按降序排序 MySql 的 Varchar 日期?
- java - 无法保存所有小数位
- python - 如何从文本文件的列表中获取随机单词?