New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a custom body reader option to Parsers #698
Add a custom body reader option to Parsers #698
Conversation
82afda2
to
7d0aef6
Compare
@josevalim said (in the issue mentioned in OP):
So, there are still other ways we could consider doing this. |
7d0aef6
to
1de0ed7
Compare
@ryanwinchester I like this as it is the most generic way I would implement this. The only concern is that you cannot pass anonymous functions at compile time. This wouldn't work:
That's why we usually use MFAs. Should we use that instead here? Something like:
|
@josevalim would you say then, something more like this? # parsers.ex
def read_body(conn, {module_name, function_name, extra_args}, opts) do
apply(module_name, function_name, [conn | opts] ++ extra_args)
end # parsers/json.ex
def init(opts) do
{decoder, opts} = Keyword.pop(opts, :json_decoder)
{body_reader, opts} = Keyword.pop(opts, :body_reader, {Plug.Conn, :read_body, []})
# ...
{body_reader, decoder, opts}
end
def parse(conn, "application", subtype, _headers, {body_reader, decoder, opts}) do
if subtype == "json" or String.ends_with?(subtype, "+json") do
conn
|> Plug.Parsers.read_body(body_reader, opts)
|> decode(decoder)
else
{:next, conn}
end
end Or just stick to forcing the specific parsers applying the MFAs on their own? Be as nit-picky as you like, I've still got a lot to learn ;) |
I don't think we need a helper function. Here is what I would do: def init(opts) do
{decoder, opts} = Keyword.pop(opts, :json_decoder)
body_reader = Keyword.get(opts, :body_reader, {Plug.Conn, :read_body, [opts]})
{body_reader, decoder}
end
def parse(conn, "application", subtype, _headers, {{mod, fun, args}, decoder}) do
if subtype == "json" or String.ends_with?(subtype, "+json") do
apply(mod, fun, [conn | args]) |> decode(decoder)
else
{:next, conn}
end
end |
ea8e5d0
to
d25ee7a
Compare
Cool, updated. 😊 My concern now is Since we used to (and still do in the default case) pass the remaining Thoughts? Not a concern because we can specify what we want in the MFA? |
@ryanwinchester we would ever only prepend arguments, because that's the convention, but i understand your concerns. If you want to always pass the options, that's fine by me, but it has to be |
test/plug/parsers/json_test.exs
Outdated
@@ -20,6 +24,13 @@ defmodule Plug.Parsers.JSONTest do | |||
end | |||
end | |||
|
|||
defmodule BodyReader do | |||
def read_body(conn, opts \\ []) do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is better to remove the optional arguments so it is clear which arity the test is invoking.
@ryanwinchester let's go with your suggestion and always pass both |
08f784d
to
3ac844a
Compare
Use MFA for body reader options in parsers
3ac844a
to
94e19c4
Compare
060eb3d
to
e7c4937
Compare
Beautiful, I have simplified the docs to require less contextual information, and everything else is perfect. Thank you! |
❤️ 💚 💙 💛 💜 |
Submitting a possible solution as discussed in #691, for further discussion.
Since the body can only be read once, this becomes an issue for anybody who needs the raw body for whatever reason in their application.
This is a common problem and recurring theme that people run into when they need to verify their requests with HTTP Signature Verification oin the body.
This issue effects anybody who uses webhooks to integrate with third-party services like GitHub, Stripe, Dropbox, Samsung SmartThings, and many many more.
By passing in an optional read_body function, the developer can now hook in signature verifications to be used on the raw body before it is discarded.
I'm opening this PR in the hopes of pushing progress on this issue. I am 100% open to completely changing the implementation, and also I will not feel snubbed if somebody opens a better PR.