Ruby调用Discord API移除打印语句后JSON解析报类型错误
问题背景
分页调用Discord API拉取服务器成员数据,接口单次最多可返回1000条用户记录,需循环请求直到获取全部用户。
代码中保留p ["BEFORE PARSE", response.body]打印response.body的语句时,程序可正常运行,顺利完成全部17轮分页请求;若移除该打印语句,在对用户对象调用dig方法时会抛出json no implicit conversion of String into Integer (TypeError)错误:第10轮迭代的返回结果中出现了['global', false]结构,而非预期的用户实例对象。
相关实现代码如下:
def initialize @role_mapping = {} @students = {} @connection = Faraday.new( url: 'https://discord.com/api/v10', params: {}, headers: { 'Content-Type' => 'application/json', 'Authorization' => 'Bot 1234abcd' } ) end ... def request_members(guild_id, after = "0", limit = 1000) response = connection.get("guilds/#{guild_id}/members") do |req| req.params['limit'] = limit req.params['after'] = after end p ["BEFORE PARSE", response.body] parsed_response = JSON.parse(response.body) updated_after = nil updated_after = parsed_response[-1]['user']['id'] if parsed_response[-1] parsed_response.each do |user| user_id = user.dig('user', 'id') roles = user['roles'].filter_map do |role_id| @role_mapping[role_id] if @role_mapping[role_id] end if @students[user_id] @students[user_id] = @students[user_id] + roles else @students[user_id] = roles end end request_members(guild_id, updated_after) if updated_after end
根本原因
- 核心问题是代码未做API响应状态校验,移除打印语句后请求无间隔触发了Discord的速率限制。
- 保留打印语句时,
p执行控制台输出是同步IO操作,会在每轮请求之间自然增加几十到上百毫秒的延迟,请求频率没有触发Discord API的限流阈值,接口正常返回成员列表数组,后续解析逻辑可以正常运行。 - 移除打印语句后,递归请求没有任何等待间隔,请求速度过快,第10轮请求触发了Discord的429速率限制,此时接口返回的不是成员列表数组,而是限流响应的JSON对象,结构如下:
{ "global": false, "message": "You are being rate limited.", "retry_after": 0.214 } - 此时
JSON.parse(response.body)得到的是Ruby Hash对象,而非预期的数组。代码没有做类型校验直接遍历:- 对Hash调用
parsed_response[-1]时,因为Hash不存在整数键-1,返回nil,所以取最后一个用户ID的逻辑不会执行 - 对Hash调用
each遍历,Ruby会逐组返回键值对构成的数组,第一个遍历到的元素就是['global', false],也就是观察到的异常结构 - 后续对这个长度为2的数组调用
dig('user', 'id')时,数组索引只接受整数类型,传入字符串'user'就会抛出no implicit conversion of String into Integer的类型错误。
- 对Hash调用
修复方向
- 给Faraday配置状态码校验中间件,遇到4xx、5xx响应时做对应处理,不要直接把错误响应当成正常业务数据解析
- 不要用无间隔递归的方式发请求,严格遵循Discord API的限流规则:如果收到429响应,按返回的
retry_after字段等待对应时间再重试,也可以主动在每轮请求之间增加合理延迟 - 解析响应前先校验数据结构,确认返回的是成员数组后再走后续遍历逻辑,避免异常结构进入业务代码。
内容的提问来源于stack exchange,提问作者Sean Magyar
相关产品推荐
相关产品推荐

