Rails接收带前导零数字的JSON POST参数解析报错问题
问题:Rails API解析带前导零数字的JSON请求时报错
我开发的Rails API接收JSON格式POST请求,正常请求可正常处理,但当JSON中数字字段(如zip)带前导零时,会触发参数解析错误,错误栈如下:
eval error: Error occurred while parsing request parameters /home/danniel/.rvm/gems/ruby-2.7.5/gems/actionpack-7.0.4/lib/action_dispatch/http/parameters.rb:96:in `rescue in parse_formatted_parameters' /home/danniel/.rvm/gems/ruby-2.7.5/gems/actionpack-7.0.4/lib/action_dispatch/http/parameters.rb:92:in `parse_formatted_parameters' /home/danniel/.rvm/gems/ruby-2.7.5/gems/actionpack-7.0.4/lib/action_dispatch/http/request.rb:390:in `block in POST' /home/danniel/.rvm/gems/ruby-2.7.5/gems/rack-2.2.4/lib/rack/request.rb:69:in `fetch' /home/danniel/.rvm/gems/ruby-2.7.5/gems/rack-2.2.4/lib/rack/request.rb:69:in `fetch_header' /home/danniel/.rvm/gems/ruby-2.7.5/gems/actionpack-7.0.4/lib/action_dispatch/http/request.rb:389:in `POST' /home/danniel/.rvm/gems/ruby-2.7.5/gems/actionpack-7.0.4/lib/action_dispatch/http/parameters.rb:55:in `parameters' /home/danniel/.rvm/gems/ruby-2.7.5/gems/actionpack-7.0.4/lib/action_controller/metal/strong_parameters.rb:1232:in `params' (rdbg)//home/danniel/workspace/check-my-cep/app/controllers/api/customers_controller.rb:1:in `create'
正常请求示例:
curl -d '[{"zip": 1001000,"name": "JOSE"}, {"zip": 1001900,"name": "FRANCISCO"}]' http://localhost:3000/api/customers --header "Accept: application/json" --header "Content-Type: application/json"
触发错误的请求示例(zip带前导零):
curl -d '[{"zip": 01001000,"name": "JOSE"}, {"zip": 01001900,"name": "FRANCISCO"}]' http://localhost:3000/api/customers --header "Accept: application/json" --header "Content-Type: application/json"
控制器代码:
class Api::CustomersController < ApplicationController before_action :set_customer, only: %i[ destroy ] def create ... end def destroy @customer.destroy end private def set_customer @customer = Customer.find(params[:id]) end def customer_params params.permit(_json: [:cep, :name]).require(:_json) end end
补充:无法控制请求格式,必须处理这类带前导零的数字JSON。
解决方案
原因分析
标准JSON规范(RFC 8259)明确规定:数字不能有前导零(仅0本身例外)。Rails默认使用的JSON解析器会严格遵守该规范,因此遇到带前导零的数字时会抛出解析错误。
方案1:全局替换宽松JSON解析器
通过修改Rails的参数解析逻辑,允许非标准的前导零数字格式。
用json gem的宽松模式
在config/initializers/json_parser.rb中添加:
require 'json' ActionDispatch::Request.parameter_parsers[:json] = -> (raw_post) { data = JSON.parse(raw_post, allow_nan: true, quirks_mode: true) data.is_a?(Hash) ? data : { _json: data } }
quirks_mode: true会允许前导零数字、尾随逗号等非标准JSON语法。
用oj gem的兼容模式
如果项目已使用oj gem,可配置兼容模式:
在config/initializers/oj_parser.rb中添加:
require 'oj' ActionDispatch::Request.parameter_parsers[:json] = -> (raw_post) { data = Oj.load(raw_post, mode: :compat) data.is_a?(Hash) ? data : { _json: data } }
:compat模式兼容多种非标准JSON语法,包括前导零数字。
方案2:单控制器手动处理请求体
如果不想全局修改解析规则,可在目标控制器中手动读取并解析请求体:
修改create方法:
def create raw_body = request.raw_post # 宽松模式解析JSON json_data = JSON.parse(raw_body, quirks_mode: true) # 手动筛选合法参数(替换原customer_params逻辑) valid_data = json_data.map { |item| item.slice(:zip, :name) } # 后续业务逻辑,比如创建记录 Customer.create!(valid_data) head :created rescue JSON::ParserError => e render json: { error: "无效JSON格式: #{e.message}" }, status: :bad_request end
该方式仅影响当前控制器的create动作,不会全局改变Rails解析行为。
注意事项
- 宽松解析会引入一定安全风险(如允许
NaN、Infinity等非标准值),建议解析后对参数做严格校验(比如检查zip字段格式)。 - 若
zip本质是字符串(如邮政编码无需数值运算),优先建议客户端传字符串格式,但因无法控制请求格式,此方案不适用。
内容的提问来源于stack exchange,提问作者Danniel Rodrigues
相关产品推荐
相关产品推荐

