You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Given a POST route handler that expects an URL-encoded form body, when a client sends an empty request body, calling Request.content.decode(...) causes a 422 Unprocessable Entity abort to be thrown.
This is an issue, because browsers typically omit empty values when submitting a HTML form without JavaScript being involved. If every input is on their default/empty value, this means the request body could become an empty string.
In other words, a form body with Content-Length: 0 is valid, but throws an obscure error now.
To Reproduce
Start a Vapor project, e.g. by cloning template-bare.
Start the application and navigate to http://localhost:8080/form.
Select one or more car makes, press Submit.
Observe that the response contains Received cars: '(comma-separated strings)'.
Navigate back to the form and reload the page to clear the selection.
Now, without selecting any of the makes, press Submit.
Observe a 422 error is returned.
Expected behavior
The decode call should succeed, return a FormData struct with a nil array inside, and ultimately the route should return the string Received cars: 'NULL'.
Environment
Vapor Framework version: 4.77.0
Vapor Toolbox version: irrelevant
OS version: macOS Ventura 13.4.1 (but also irrelevant)
Browser: Chrome 114
Additional context
I did some debugging, originally thinking that the issue is somewhere inside URLEncodedFormDecoder. That was ultimately a bust, execution does not reach the decoder class at all! Instead, the error is thrown from here. In case of Content-Length: 0, the bodyStorage is none, which triggers this check.
Workaround
Adding a hidden input with an arbitrary (ignored) value will cause the browser to always include this key-value pair in the body, so it is no longer empty even if the user-facing inputs are, avoiding the 422 error.
<inputtype="hidden" name="_foo" value="foo"/>
Hidden inputs are pretty common for CSRF and other purposes, which might be why this issue went unnoticed for so long.
The text was updated successfully, but these errors were encountered:
I think this a same issue I’ve faced a while back. #2606
But it’s closed.
I was thinking if a body contents is nil, can it be replaced with an empty body contents, that validations can be run, so the correct validation error thrown?
That indeed looks like the same issue, as validations also go through Content (Codable).
I'm more and more inclined to think that the empty body check is just wrong.
Describe the bug
Given a POST route handler that expects an URL-encoded form body, when a client sends an empty request body, calling
Request.content.decode(...)
causes a 422 Unprocessable Entity abort to be thrown.This is an issue, because browsers typically omit empty values when submitting a HTML form without JavaScript being involved. If every input is on their default/empty value, this means the request body could become an empty string.
In other words, a form body with
Content-Length: 0
is valid, but throws an obscure error now.To Reproduce
Start a Vapor project, e.g. by cloning template-bare.
Add the following two route handlers:
Start the application and navigate to
http://localhost:8080/form
.Select one or more car makes, press Submit.
Observe that the response contains
Received cars: '(comma-separated strings)'
.Navigate back to the form and reload the page to clear the selection.
Now, without selecting any of the makes, press Submit.
Observe a 422 error is returned.
Expected behavior
The
decode
call should succeed, return a FormData struct with a nil array inside, and ultimately the route should return the stringReceived cars: 'NULL'
.Environment
Additional context
I did some debugging, originally thinking that the issue is somewhere inside URLEncodedFormDecoder. That was ultimately a bust, execution does not reach the decoder class at all! Instead, the error is thrown from here. In case of
Content-Length: 0
, thebodyStorage
isnone
, which triggers this check.Workaround
Adding a hidden input with an arbitrary (ignored) value will cause the browser to always include this key-value pair in the body, so it is no longer empty even if the user-facing inputs are, avoiding the 422 error.
Hidden inputs are pretty common for CSRF and other purposes, which might be why this issue went unnoticed for so long.
The text was updated successfully, but these errors were encountered: