-
Notifications
You must be signed in to change notification settings - Fork 152
Description
I find that Req response handling requires a lot of boilerplate to handle due to the fact that all HTTP statuses are returned in the form of {:ok, %Req.Response{status: some_http_status, ...}}. While from the perspective of the library, Req has done its job, from the perspective of a user using Req, a non-200 HTTP status is often a failure case.
Example illustrating the point that all HTTP statuses are returned as {:ok, ...}:
iex(tp@127.0.0.1)1> Req.post("https://httpbin.org/bearer")
{:ok,
%Req.Response{
status: 405,
headers: %{
"access-control-allow-credentials" => ["true"],
"access-control-allow-origin" => ["*"],
"allow" => ["GET, OPTIONS, HEAD"],
"connection" => ["keep-alive"],
"content-type" => ["text/html"],
"date" => ["Wed, 12 Mar 2025 18:17:58 GMT"],
"server" => ["gunicorn/19.9.0"]
},
body: "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n<title>405 Method Not Allowed</title>\n<h1>Method Not Allowed</h1>\n<p>The method is not allowed for the requested URL.</p>\n",
trailers: %{},
private: %{}
}}This means that when I write code, I generally have to have 3 branches of pattern matching for response handling:
case Req.post("https://httpbin.org/bearer") do
{:ok, %Req.Response{status: 200, body: body}} ->
# I do something with the body
{:ok, result}
{:ok, %Req.Response{status: status, body: body}} ->
# I have to rewrap this response
{:error, "Super useful error message or Exception struct"}
{:error, exception} ->
{:error, exception}
endNow, if Req allowed to specify what the expected success status(es) are, this could end up being as simple as:
with {:ok, %Req.Response{status: 200, body: body}} <- Req.post("https://httpbin.org/bearer", success_http_codes: [200]) do
# I do something with the body
endAssuming that Req.post("https://httpbin.org/bearer", success_http_codes: [200]) would now return something like{:error, %Req.HTTPError{status: 405, body: body}}.