记录一下使用RestClient这个Gem时遇到的一个坑.
版本: rest-client (2.1.0)
TLDR
如果直接使用封装过的 RestClient.get
/RestClient.put
/RestClient.post
等方法, 当遇到异常的响应时(比如400 bad request), 得不到任何有用的信息
应该使用带块的方式调用, 用块参数接收 response, request 和 result, 不使用块会导致异常时丢失信息
示例
用rails准备一个简单的接口, 响应 400, 并返回错误的信息
1 | # config/routes.rb |
可以看到, 用块的这个可以获取到响应里返回的详细信息
这个区别是在调用JIRA7和企业微信的API时发现的, 现象是: 用restclient调用api只返回了400, 用postman调试却能得到错误信息, 使用net/http调试, 也能得到错误信息
经过调试和阅读文档才意识到, 信息是被RestClient给吞了
看源码可以看到, 4XX和5XX的状态吗, 如果响应里有信息, RestClient是不会解析和返回的, 只会包装一个对应的异常
看这注释的意思, 这是个feature, 不是个bug, 但是假如确实有人在 4XX 响应里返回了信息(就像JIRA7和企业微信机器人接口那样), 那使用RestClient, 就得小心了…
1 | # lib/restclient/abstract_response.rb |
结论: 如果使用RestClient, 一定要使用块; 如果用其他lib, 需要注意一下有没有类似的问题
思路.1 使用Request.execute(:method => :get, :url => url, :headers => headers, &block)
封装自己的请求
思路.2 使用RestClient.get
等封装后的方法, 并使用块
示例:
1 | def make_qywxrobot_request(url, payload) |
思路.3 用ruby自带的Net::HTTP自己封装吧
目前没看出RestClient有什么优势…(可能是没遇到很复杂的http请求的场景)
反思
又仔细读一下文档, 发现文档里还是提供了些头绪的, 只是遇到问题时没读懂
这个”信息被吞”的问题, 和Error Handling有点像, 很多时候异常被吞掉是很恼人的事情, 一定要想清楚再决定是否
rescue
- 这里有几点体会:
- 代码里应该尽量少写begin, rescue, 尤其是rescue所有Exception更要少些, 代码的可读性会有提升, 可维护性也会变好一些, 因为出错时会崩, 崩了能找到源头; 如果满篇rescue, 那么排查起来就会很费劲了
- 一定要理解
rescue Exception => e
和rescue => e
的区别, 多数情况下前者是万万不可的- ref: https://www.mikeperham.com/2012/03/03/the-perils-of-rescue-exception/
- ref: https://www.honeybadger.io/blog/ruby-exception-vs-standarderror-whats-the-difference/
- [20220615 update] add
Lint/RescueException
to your .rubocop.yml and runbundle exec rubocop
- 必要的地方要做容错处理, 不能崩; 但是这种地方如果崩了, 要能及时发出告警, 记录好现场数据以备排查和修复, 绝对不能简单吞了完事
- 捕获的异常越具体, 或者说处理异常的代码越少, 说明写代码时考虑的越周到(前提是这种异常确实会发送), 代码会干净很多, 这样的代码无论是使用、阅读还是维护, 都会很舒服
没必要时, 可以不考虑使用lib(例入写gem时, 要尽量少的引入依赖)
使用开源lib时, 最好能先了解它, 不要拿来就用, 不然遇到了奇怪的问题时会很头疼; 如果有安全问题也会很麻烦的, 甚至会有巨大的损失