-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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 require(_:) extension to Parameters. #2402
Conversation
The default error is up for debate here, perhaps a 400 Bad Request would be more appropriate? |
public extension Parameters { | ||
/// Grabs the named parameter from the parameter bag. | ||
/// An `Abort(.unprocessableEntity)` error is thrown if the parameter does not exist. | ||
func require(_ name: String, error: Error = Abort(.unprocessableEntity)) throws -> String { |
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.
I'd prefer the label of the error to be or
, so or error: Error
.
This follows the style used by EventLoopFuture.unwrap
seen here:
public func unwrap(or error: @autoclosure @escaping () -> Error) -> EventLoopFuture<Value.WrappedType> { |
/// Grabs the named parameter from the parameter bag, casting it to | ||
/// a `LosslessStringConvertible` type. | ||
/// An `Abort(.unprocessableEntity)` error is thrown if the parameter does not exist. | ||
func require<T>(_ name: String, as type: T.Type = T.self, error: Error = Abort(.unprocessableEntity)) throws -> T? |
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.
Same here
I believe |
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.
Agree with @Joannis' comments. As for the status code, IMO, a plain .badRequest
is a catchall for all 4xx
codes if you can't find a better suited subcode, which in this case we can. So in my opinion the choice would be between .unprocessableEntity
and .notFound
. I would lean to a .notFound
since there is an error with the actual location of the request, and not so much the entity/body sent along with the request, which .unprocessableEntity
is more geared towards
One point in favor of |
/// Grabs the named parameter from the parameter bag, casting it to | ||
/// a `LosslessStringConvertible` type. | ||
/// An `Abort(.unprocessableEntity)` error is thrown if the parameter does not exist. | ||
func require<T>(_ name: String, as type: T.Type = T.self, or error: Error = Abort(.unprocessableEntity)) throws -> T? |
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.
Why does this return an optional? Doesn't that negate the whole use of using require
?
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.
Done
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.
+1, thanks!
Regarding error to be thrown, that's kind of a tricky one. There are two situations in which get<T>
returns nil
:
- 1: The string cannot be converted to the desired type. 422 makes sense here as a default.
- 2: There is no route parameter with that name. IMO this should always be a 500 since it's a developer error.
We could detect these two different failure modes by only using the Parameters.get
method that returns a string. That will only ever return nil
if the parameter does not exist (and thus the developer misconfigured the routes).
I don't think this is a requirement to merge, but it could be helpful for debugging misspelled parameter names.
@@ -0,0 +1,24 @@ | |||
public extension Parameters { | |||
/// Grabs the named parameter from the parameter bag. | |||
/// An `Abort(.unprocessableEntity)` error is thrown if the parameter does not exist. |
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.
Docblock should have a - parameters:
list.
/// Grabs the named parameter from the parameter bag. | ||
/// An `Abort(.unprocessableEntity)` error is thrown if the parameter does not exist. | ||
func require(_ name: String, or error: Error = Abort(.unprocessableEntity)) throws -> String { | ||
guard let value = get(name) else { |
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.
This should be able to call the method below, right?
@@ -0,0 +1,24 @@ | |||
public extension Parameters { |
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.
Please put public
in front of each method instead of using the public extension
syntax (Vapor style).
@tanner0101 should the errors for both scenarios be configurable, or just hard-code them? Having two parameters feels a bit overkill, and there's precedent for hard-coded statuses, e.g in |
51d329f
to
bccb440
Compare
@tanner0101 went ahead and hard-coded the errors, since that feels the most natural to me. |
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.
Overall LGTM, just 2 nits
|
||
/// Grabs the named parameter from the parameter bag, casting it to a `LosslessStringConvertible` type. | ||
/// If the parameter does not exist, `Abort(.internalServerError)` is thrown. | ||
/// If the parameter value cannot be converted to the generic type, `Abort(.unprocessableEntity)` is thrown. |
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.
Would rename this to the required type
/// If the parameter value cannot be converted to the generic type, `Abort(.unprocessableEntity)` is thrown. | |
/// If the parameter value cannot be converted to the required type, `Abort(.unprocessableEntity)` is thrown. |
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.
Done
Thanks! |
These changes are now available in 4.20.0 |
Adds a
require(_:)
helper to Parameters, allowing for more succinct parameter handling. (#2402)Previously the optional case needed to be handled explicitly:
Now it can be written as: