如何使用Rails 5.2和Faraday gem构建复杂的json到POST到web服务



我的应用程序通过REST API将数据发送到元数据存储库。我选择了Faraday来处理HTTP请求。我基本上设置了一些头、一个json数据集和到web服务的POST。以下代码发生在skills_controller中,当用户决定发布变量的定义时会触发:

### Create the variable for the BusinessArea, get the location header in return
connection = Faraday.new("https://sis-sms-r.application.opendataquality.ch", :ssl => {:verify => false})
request_body = {
definedVariableType: @skill.skill_type.property,
description: {
en: @skill.description_translations.where(language: :en).take!,
de: @skill.description_translations.where(language: :de_OFS).take!,
fr: @skill.description_translations.where(language: :fr_OFS).take!
},
identifier: "Variable TEST 10",
name: {
en: @skill.name_translations.where(language: :en).take!,
de: @skill.name_translations.where(language: :de_OFS).take!,
fr: @skill.name_translations.where(language: :fr_OFS).take!
},
pattern: nil,
pseudonymized: true,
validFrom: Time.now,
validTo: Time.now + 1.year,
version: "1",
responsibleDeputy: {
identifier: @skill.deputy.email,
name: @skill.deputy.external_directory_id
},
responsibleOrgUnit: {
identifier: @skill.organisation.code,
name: @skill.organisation.external_reference
},
responsiblePerson: {
identifier: @skill.responsible.email,
name: @skill.responsible.external_directory_id
}
}

puts "--- body"
puts request_body
response = connection.post("/api/InformationFields/#{business_area.uuid}/definedVariables") do |req|
req.body = request_body.to_json
req.headers['Content-Type'] = 'application/json'
req.headers['Accept'] =  'application/json'
req.headers['Authorization'] = "Bearer #{token}"
end
puts "--- response"
puts response.status               # Status 201 => successful request
puts response.body                 # Message
puts response.headers["location"]  # uuid of new object

然后,该方法将呈现技能的"显示"视图的更新部分及其更新状态。

只要请求主体非常简单,这就可以正常工作。但我想处理可变数量的翻译,在某些情况下,还将子记录发送到web服务:即实现循环、嵌套json对象,可能还有部分。

我读过Jbuilder为视图创建复杂json的功能。有没有类似的东西我可以在控制器中使用?或者有没有一种方法可以创建json视图(和部分(并将其呈现到Faraday的请求体中?哪种架构是构建此功能的好架构?你知道有什么文章可以描述这一点吗?

非常感谢你给我带路。

首先创建一个触及应用程序边界的对象:

class JSONClient
attr_reader :connection
def initialize(base_uri, **opts, &block)
@connection = Faraday.new(
base_uri, 
**opts
) do |f|
f.request :json # encode req bodies as JSON
f.response :json # decode response bodies as JSON
yield f if block_given?
end
end
end
class BusinessAreaClient < JSONClient
def initialize(**opts)
super(
"https://sis-sms-r.application.opendataquality.ch", 
ssl: { verify: false},
**opts
)
end
def defined_variables(skill:, uiid:  token:)
response = connection.post(
"/api/InformationFields/#{uuid}/definedVariables"
SkillSerializer.serialize(skill),
{
'Authorization' => "Bearer #{token}"
}
)
if response.success?
response 
else
# handle errors
end
end
end
response = BusinessAreaClient.new
.defined_variables(
skill: skill,
uuid: business_area.uuid,
token: token
)

这为您提供了一个可以在隔离状态下进行测试和剔除的对象。它也是唯一应该了解API的异常和特殊性的对象,从而在应用程序发生变化时限制对应用程序的影响。

虽然最初使用视图听起来是个好主意,但您基本上是在使用一个非常笨拙的DSL来生成基本的数据结构,如将1-1映射到JSON的数组和散列。jBuilder也很慢。

作为重构的第一步,您可以提取将Skill转换为JSON并转换为自己的PORO:

class SkillSerializer < SimpleDelegator
LANG_MAPPING = {
en: :en,
de: :de_OFS,
fr: :fr_OFS
}.freeze

def serialize
{
definedVariableType: skill_type.property,
description: translate(description_translations),
identifier: "Variable TEST 10",
name: translate(name_translations),
pattern: nil,
pseudonymized: true,
validFrom: Time.now,
validTo: Time.now + 1.year,
version: "1",
responsibleDeputy: {
identifier: deputy.email,
name: deputy.external_directory_id
},
responsibleOrgUnit: {
identifier: organisation.code,
name: organisation.external_reference
},
responsiblePerson: {
identifier: responsible.email,
name: responsible.external_directory_id
}
}
end
def self.serialize(object)
new(object).serialize
end
private 
# should probally be refactored to not cause an excessive amount of queries
def translate(relation)
LANG_MAPPING.dup.transform_values do |lang|
relation.where(language: lang).take!
end
end
end

ActiveModel::序列化程序也是一个选项。

最新更新