-
Notifications
You must be signed in to change notification settings - Fork 266
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
Cannot read request body more than once #449
Comments
You are correct that it is possible to read the body more than once if you implement some kind of buffer but it is normally not recommended to read the body more than once like this. The problem is that ASP.NET Core, particularly in C#, makes it difficult to do what you need, but when you take one step back and actually think about it from a holistic point of view, it is kind of strange and almost wrong to have to read a request body twice. A web server retrieves a HTTP request and reads the body once and then performs any work which is necessary to return a response. This is how it should be and if a framework makes this difficult then IMHO the developer has picked the wrong web framework. However, having said that, luckily Giraffe makes this kind of implementation relatively easy. You can define an auth handler which accepts another handler to process the request on a successful authentication (in your case signature verification). For example: let hash (value : string) : string =
value // Do your hashing algorithm
let authHandler (successHandler : string -> HttpHandler) : HttpHandler =
fun (next : HttpFunc) (ctx : HttpContext) ->
task {
let! body = ctx.ReadBodyFromRequestAsync()
let header = ctx.Request.Headers.["Auth-Signature"].ToString()
let hashed = hash body
return!
match header = hashed with
| true -> successHandler body next ctx
| false -> setStatusCode 401 earlyReturn ctx
}
let handleWeekhook (verifiedData : string) : HttpHandler =
fun next ctx ->
task {
// Do something with the body (=verifiedData)
// ...
return! setStatusCode 200 next ctx
}
let someApp : HttpHandler =
POST >=> choose [
route "/webhook" >=> authHandler handleWeekhook
] This is much more elegant in my opinion and you can get away without having to implement suboptimal hacks like buffering the response, especially since you wouldn't even need to do this every single time. I am sure there are many routes which don't require the body to be signed and these handlers would have to take an unnecessary performance hit if the buffering was implemented in a central place. Besides that, the above pattern will make it much harder to implement wrong routes. For example, in your original example you could prepend the auth handler in front of a chain where no handler afterwards actually needs to access the body. Possibly some copy paste mistake from another route and so you'd do an unnecessary step of validating a signature and buffering the body when it's not required at all. In my example such a "copy and paste" mistake would be much harder to make, because the auth handler itself requires another handler which actually relies on having a verified body, otherwise what's the point in verifying it. These are just my two cents and I know it is very opinionated so I'm sure some developers will disagree with me, but that would be my advice and hence why I'd rather not implement functionality to buffer the request body when I think that it's not really required in most use cases, at least not in Giraffe I believe. EDIT: Anyways, let me know what you think. I realise my answer sounds very strongly opinionated and I don't mean that there is no room for buffering the body, just wanted to point out an alternative which I personally think is actually a better solution but let me know how you feel about this approach and if that sufficiently solves your issue or if you still feel like there is a need to implement such a helper function :) |
Thanks for your response and great work on this project. I agree this is an odd use case. It's unusual for the request body to be read more than once after it hits the server. Thank you for your example code, but I do prefer having separate handlers which are composed together via
I haven't investigated the performance implications for buffering. Any idea what the hit would be? I agree that would be a concern. I can work on some benchmarks for this. Anyhow, these are the reasons why I thought it would be a useful change and why I think it fits with the philosophy of Giraffe. I agree this is largely a matter of opinion - let me know what you think. |
Ok, I've added a new extension method which I think will solve this for you: Would you be able to copy this into your application and see if it sufficiently addresses your use case? |
Thank you, that's perfect! |
This is known behavior of ASP.Net - specifically the behavior of
Stream
/StreamReader
.When connecting multiple
HttpHandler
's in Giraffe, sometimes more than one handler will need to read the request body. This results in subsequent reads of the request body from returning an empty string.An Example
You may have an 'authHandler' which reads and hashes the request body.
Later on, another handler may need to read the body:
They would be connected:
In this example, the
webhookHandler
will read an empty body fromctx.ReadBodyFromRequestAsync()
. I would expect this to return the full request body.I'm getting this behavior using
Giraffe 4.1.0
on anet5.0
web project.Possible solution
As per this blog post, the
HttpRequest
can be set up to be read more than once. This can be done in thectx.ReadBodyFromRequestAsync()
extension.I'd be happy to put together a PR for this.
Thanks,
The text was updated successfully, but these errors were encountered: