Safely extract and validate Vapor models from requests.
Update your Package.swift
file.
.Package(url: "https://github.com/nodes-vapor/sanitized.git", majorVersion: 1)
import Sanitized
Before you're able to extract your model from a request it needs to conform to the protocol Sanitizable
. To be conferment all you need to do is add a [String]
named permitted
with a list of keys you wish to allow.
struct User: Model, Sanitizable {
var id: Node?
var name: String
var email: String
// will only allow the keys `name` and `email`
static var permitted = ["name", "email"]
init(node: Node, in context: Context) throws {
id = node["id"]
name = try node.extract("name")
email = try node.extract("email")
}
//...
}
Now that you have a conforming model, you can safely extract it from a Request
.
{
"id": 10,
"name": "Brett",
"email": "test@tested.com"
}
drop.post("users") { req in
var user: User = try req.extractModel()
print(user.id == nil) // prints `true`
try user.save()
return user
}
Just like model extraction, securely updating a model with data from a request is a trivial process.
drop.post("users", User.self) { req, user in
var updatedUser = try req.patchModel(user)
try updatedUser.save()
}
If you don't have an instance of the model you wish to update you can have Sanitize
fetch and update the model for you.
drop.post("users", Int.self) { req, userId in
var user: User = try req.patchModel(userId)
try user.save()
}
This package doesn't specifically provide any validation tools, but it is capable of running your validation suite for you. Thusly, simplifying the logic in your controllers. Sanitized has two ways of accomplishing this: pre and post validation.
This type of validation is run before the model is initialized and is checked against the request's JSON. This type of field is useful for when you only want to check if a field exists before continuing.
Create a preValidation
check by overriding the default implementation in your Sanitizable
model.
static func preValidate(data: JSON) throws {
// we only want to ensure that `name` exists/
guard data["name"] != nil else {
throw MyError.invalidRequest("Name not provided.")
}
}
This type of validation is run after the model has been initialized is useful for checking the content of fields while using Swift-native types.
Create a postValidation
check by overriding the default implementation in your Sanitizable
model.
func postValidate() throws {
guard email.count > 8 else {
throw Abort.custom(
status: .badRequest,
message: "Email must be longer than 8 characters."
)
}
}
The error thrown by a failed Node.extract
will be turned into a 500 Internal Server Error
if not caught and changed before being caught by Vapor's AbortMiddleware. By default, this package will catch that error and convert it into a 400 Bad Request
. If you wish to disable this for development environments or throw your own error, you can override the following default implementation:
static func updateThrownError(_ error: Error) -> AbortError {
// recreates the default behavior of `AbortMiddleware`.
return Abort.custom(
status: .internalServerError,
message: "\(error)"
)
}
This package is developed and maintained by the Vapor team at Nodes. The package owner for this project is Brett.
This package is open-sourced software licensed under the MIT license