camelCase JSON responses and snake_case parameters #144
-
Hello, First of all, thank you for creating this library! We have a JSON API newly set up with Rodauth. It accepts requests from a separate frontend app. Since the json standard is {
"email": "[email protected]",
"password": "mypassword",
"firstName": "My",
"lastName":"Name"
} Then, the parameters should be intercepted and converted to Here is what I've tried:
class ApplicationController < ActionController::API
before_action :snake_case_params
def snake_case_params
request.parameters.deep_transform_keys!(&:underscore)
end
end
ActionDispatch::Request.parameter_parsers[:json] = lambda {|raw_post|
data = ActiveSupport::JSON.decode(raw_post)
if data.is_a?(Array)
data.map {|item| item.deep_transform_keys!(&:underscore) }
else
data.deep_transform_keys!(&:underscore)
end
data.is_a?(Hash) ? data : {'_json': data}
}
module CamelAndBack
require "rack"
class Middleware
def initialize(app)
@app = app
end
def call(env)
# Transforming request
request = Rack::Request.new(env)
if request.get? && !request.GET.empty?
request.GET&.deep_transform_keys! {|k| k.is_a?(String) ? k.underscore : k }
end
if request.post? && !env["rack.input"].read.blank?
env["rack.input"].seek(0)
puts "Request before #{env['rack.input'].read}"
env["rack.input"].seek(0)
string_io = StringIO.new
string_io.puts(JSON.generate(JSON.parse(env["rack.input"].read).deep_transform_keys! {|k|
k.is_a?(String) ? k.underscore : k
}))
env.update("rack.input" => string_io)
env["rack.input"].seek(0)
puts "Request after #{env['rack.input'].read}"
env["rack.input"].seek(0)
end
# Transforming response
@app.call(env).tap do |_status, headers, response|
if headers["Content-Type"] =~ %r{application/json}
response.each do |res|
begin
new_response = JSON.parse(res)
rescue JSON::ParserError => e
raise "Badly formated input -- json parse error"
end
if new_response.is_a?(Array)
new_response.map! {|h| h.deep_transform_keys! {|k| k.camelize(:lower) } }
elsif !new_response.nil?
new_response.deep_transform_keys! {|k| k.camelize(:lower) }
end
res.replace(new_response.to_json)
end
end
end
end
end
end Unfortunately, none of these 3 options worked. Rodauth keeps saying things along the lines of "required parameter 'first_name' not provided". I believe the reason is discussed here:
Is there any way right now to access and modify params before they reach Rodauth then access and modify Rodauth's response? We just need the params keys to be snake cased and the response keys to be camel cased. Any hints/context would be useful 🙂 Thank you! |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
Interesting use case, I can understand the wish to decouple naming conventions. Normally you could rename Rodauth parameters via FYI, the reason 1️⃣ didn't work was because it's a copy of Rack parameters cached under a Rails-specific env key, so Rodauth doesn't detect the change. As for 2️⃣, Rodauth uses Roda's JSON parser for parsing the request body, so Action Dispatch doesn't get called here. I'm not sure why 3️⃣ doesn't work; the only important thing is that For Rodauth, I recommend overriding Roda's JSON request body parser and JSON response body serializer, and apply the transformation on that level: # app/misc/rodauth_app.rb
class RodauthApp < Rodauth::Rails::App
plugin :json_parser, parser: -> (json) {
JSON.parse(json).deep_transform_keys { |k| k.underscore.dasherize }
}
plugin :json, serializer: -> (hash) {
hash.deep_transform_keys { |k| k.underscore.camelize(:lower) }.to_json
}
# ...
end I needed to account for Rodauth using a dasherized param naming convention. You can of course change it to underscored versions by overriding |
Beta Was this translation helpful? Give feedback.
Interesting use case, I can understand the wish to decouple naming conventions. Normally you could rename Rodauth parameters via
*_param
settings.FYI, the reason 1️⃣ didn't work was because it's a copy of Rack parameters cached under a Rails-specific env key, so Rodauth doesn't detect the change. As for 2️⃣, Rodauth uses Roda's JSON parser for parsing the request body, so Action Dispatch doesn't get called here. I'm not sure why 3️⃣ doesn't work; the only important thing is that
Rodauth::Rails::Middleware
gets inserted after this middleware, which it should given that it gets inserted after user-defined initializers.For Rodauth, I recommend overriding Roda's JSON request body parser and…