feat(validation.md): added custom validator documentation (#1016)

I created a [PR](https://github.com/vapor/vapor/pull/3263) earlier
today, adding in a custom validator called `Custom`. This allows users
to easily create custom validations, it is simpler to use and shorter in
syntax compared to the current [Custom
Validators](https://docs.vapor.codes/basics/validation/#custom-validators).

This PR also resolves [this
issue](https://github.com/vapor/docs/issues/1015)

---------

Co-authored-by: Tim Condon <0xTim@users.noreply.github.com>
This commit is contained in:
William Adonis 2025-01-01 19:15:56 +02:00 committed by GitHub
parent a0d7615b8d
commit 0946bf8f8b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 48 additions and 48 deletions

View File

@ -136,51 +136,6 @@ struct MyError: DebuggableError {
`DebuggableError` has several other properties like `possibleCauses` and `suggestedFixes` that you can use to improve the debuggability of your errors. Take a look at the protocol itself for more information. `DebuggableError` has several other properties like `possibleCauses` and `suggestedFixes` that you can use to improve the debuggability of your errors. Take a look at the protocol itself for more information.
## Stack Traces
Vapor includes support for viewing stack traces for both normal Swift errors and crashes.
### Swift Backtrace
Vapor uses the [SwiftBacktrace](https://github.com/swift-server/swift-backtrace) library to provide stack traces after a fatal error or assertion on Linux. In order for this to work, your app must include debug symbols during compilation.
```sh
swift build -c release -Xswiftc -g
```
### Error Traces
By default, `Abort` will capture the current stack trace when initialized. Your custom error types can achieve this by conforming to `DebuggableError` and storing `StackTrace.capture()`.
```swift
import Vapor
struct MyError: DebuggableError {
var identifier: String
var reason: String
var stackTrace: StackTrace?
init(
identifier: String,
reason: String,
stackTrace: StackTrace? = .capture()
) {
self.identifier = identifier
self.reason = reason
self.stackTrace = stackTrace
}
}
```
When your application's [log level](logging.md#level) is set to `.debug` or lower, error stack traces will be included in log output.
Stack traces will not be captured when the log level is greater than `.debug`. To override this behavior, set `StackTrace.isCaptureEnabled` manually in `configure`.
```swift
// Always capture stack traces, regardless of log level.
StackTrace.isCaptureEnabled = true
```
## Error Middleware ## Error Middleware
`ErrorMiddleware` is one of the only two middlewares added to your application by default. This middleware converts Swift errors that have been thrown or returned by your route handlers into HTTP responses. Without this middleware, errors thrown will result in the connection being closed without a response. `ErrorMiddleware` is one of the only two middlewares added to your application by default. This middleware converts Swift errors that have been thrown or returned by your route handlers into HTTP responses. Without this middleware, errors thrown will result in the connection being closed without a response.

View File

@ -1,6 +1,6 @@
# Validation # Validation
Vapor's Validation API helps you validate incoming request before using the [Content](content.md) API to decode data. Vapor's Validation API helps you validate the body and query parameters of an incoming request before using the [Content](content.md) API to decode data.
## Introduction ## Introduction
@ -219,8 +219,9 @@ Below is a list of the currently supported validators and a brief explanation of
|`.nil`|Value is `null`.| |`.nil`|Value is `null`.|
|`.range(_:)`|Value is within supplied `Range`.| |`.range(_:)`|Value is within supplied `Range`.|
|`.url`|Contains a valid URL.| |`.url`|Contains a valid URL.|
|`.custom(_:, validationClosure: (value) -> Bool)`|Custom, once-off validation.|
Validators can also be combined to build complex validations using operators. Validators can also be combined to build complex validations using operators. More information on `.custom` validator at [[#Custom Validators]].
|Operator|Position|Description| |Operator|Position|Description|
|-|-|-| |-|-|-|
@ -232,7 +233,11 @@ Validators can also be combined to build complex validations using operators.
## Custom Validators ## Custom Validators
Creating a custom validator for zip codes allows you to extend the functionality of the validation framework. In this section, we'll walk you through the steps to create a custom validator for validating zip codes. There are two ways to create custom validators.
### Extending Validation API
Extending the Validation API is best suited for cases where you plan on using the custom validator in more than one `Content` object. In this section, we'll walk you through the steps to create a custom validator for validating zip codes.
First create a new type to represent the `ZipCode` validation results. This struct will be responsible for reporting whether a given string is a valid zip code. First create a new type to represent the `ZipCode` validation results. This struct will be responsible for reporting whether a given string is a valid zip code.
@ -289,4 +294,44 @@ Now that you've defined the custom `zipCode` validator, you can use it to valida
```swift ```swift
validations.add("zipCode", as: String.self, is: .zipCode) validations.add("zipCode", as: String.self, is: .zipCode)
``` ```
### `Custom` Validator
The `Custom` validator is best suited for cases where you want to validate a property in only one `Content` object. This implementation has the following two advantages compared to extending the Validation API:
- Simpler to implement custom validation logic.
- Shorter syntax.
In this section, we'll walk you through the steps to create a custom validator for checking whether an employee is part of our company by looking at the `nameAndSurname` property.
```swift
let allCompanyEmployees: [String] = [
"Everett Erickson",
"Sabrina Manning",
"Seth Gates",
"Melina Hobbs",
"Brendan Wade",
"Evie Richardson",
]
struct Employee: Content {
var nameAndSurname: String
var email: String
var age: Int
var role: String
static func validations(_ validations: inout Validations) {
validations.add(
"nameAndSurname",
as: String.self,
is: .custom("Validates whether employee is part of XYZ company by looking at name and surname.") { nameAndSurname in
for employee in allCompanyEmployees {
if employee == nameAndSurname {
return true
}
}
return false
}
)
}
}
```