- In this simple project tried to showcase the Result pattern using different popular nuget packages available as well as custom implementation.
- Also how can we simplify and send consistent response from our API to consume from client.
- InValid
{
"name": "John Doe",
"age": 1,
"email" : ""
}- Valid
{
"name": "John Doe",
"age": 21,
"email": "test@test.com"
}-
When validation fails using
FluentValidationand gets all the errors.{ "value": null, "status": 5, "isSuccess": false, "successMessage": "", "correlationId": "", "location": "", "errors": [], "validationErrors": [ { "identifier": "Email", "errorMessage": "Email is required.", "errorCode": "NotEmptyValidator", "severity": 0 }, { "identifier": "Email", "errorMessage": "A valid email address is required.", "errorCode": "EmailValidator", "severity": 0 }, { "identifier": "Age", "errorMessage": "Age must be between 18 and 60.", "errorCode": "InclusiveBetweenValidator", "severity": 0 } ] } -
When we need to return single error.
{ "value": null, "status": 6, "isSuccess": false, "successMessage": "", "correlationId": "", "location": "", "errors": [ "Resource not found.", "Resource with Id 2 doesn't exist." ], "validationErrors": [] } -
When returns success result with some value.
{ "value": { "name": "John Doe", "email": "test@test.com", "age": 21, "weatherForecasts": [ { "date": "2024-12-21", "temperatureC": 52, "temperatureF": 125, "summary": "Warm" }, { "date": "2024-12-22", "temperatureC": 8, "temperatureF": 46, "summary": "Balmy" } ] }, "status": 0, "isSuccess": true, "successMessage": "", "correlationId": "", "location": "", "errors": [], "validationErrors": [] }
-
When validation fails using
FluentValidationand gets all the errors.[ { "propertyName": "Email", "errorMessage": "Email is required.", "attemptedValue": "", "customState": null, "severity": 0, "errorCode": "NotEmptyValidator", "formattedMessagePlaceholderValues": { "PropertyName": "Email", "PropertyValue": "", "PropertyPath": "Email" } }, { "propertyName": "Email", "errorMessage": "A valid email address is required.", "attemptedValue": "", "customState": null, "severity": 0, "errorCode": "EmailValidator", "formattedMessagePlaceholderValues": { "PropertyName": "Email", "PropertyValue": "", "PropertyPath": "Email" } }, { "propertyName": "Age", "errorMessage": "Age must be between 18 and 60.", "attemptedValue": 1, "customState": null, "severity": 0, "errorCode": "InclusiveBetweenValidator", "formattedMessagePlaceholderValues": { "From": 18, "To": 60, "PropertyName": "Age", "PropertyValue": 1, "PropertyPath": "Age" } } ] -
When returns success result with some value.
{ "name": "John Doe", "email": "test@test.com", "age": 21 }
-
When validation fails using
FluentValidationand gets all the errors.{ "isError": true, "errors": [ { "code": "General.Validation", "description": "A validation error has occurred.", "type": 2, "numericType": 2, "metadata": { "Email": [ { "propertyName": "Email", "errorMessage": "Email is required.", "attemptedValue": "", "customState": null, "severity": 0, "errorCode": "NotEmptyValidator", "formattedMessagePlaceholderValues": { "PropertyName": "Email", "PropertyValue": "", "PropertyPath": "Email" } }, { "propertyName": "Email", "errorMessage": "A valid email address is required.", "attemptedValue": "", "customState": null, "severity": 0, "errorCode": "EmailValidator", "formattedMessagePlaceholderValues": { "PropertyName": "Email", "PropertyValue": "", "PropertyPath": "Email" } } ], "Age": [ { "propertyName": "Age", "errorMessage": "Age must be between 18 and 60.", "attemptedValue": 1, "customState": null, "severity": 0, "errorCode": "InclusiveBetweenValidator", "formattedMessagePlaceholderValues": { "From": 18, "To": 60, "PropertyName": "Age", "PropertyValue": 1, "PropertyPath": "Age" } } ] } } ], "errorsOrEmptyList": [ { "code": "General.Validation", "description": "A validation error has occurred.", "type": 2, "numericType": 2, "metadata": { "Email": [ { "propertyName": "Email", "errorMessage": "Email is required.", "attemptedValue": "", "customState": null, "severity": 0, "errorCode": "NotEmptyValidator", "formattedMessagePlaceholderValues": { "PropertyName": "Email", "PropertyValue": "", "PropertyPath": "Email" } }, { "propertyName": "Email", "errorMessage": "A valid email address is required.", "attemptedValue": "", "customState": null, "severity": 0, "errorCode": "EmailValidator", "formattedMessagePlaceholderValues": { "PropertyName": "Email", "PropertyValue": "", "PropertyPath": "Email" } } ], "Age": [ { "propertyName": "Age", "errorMessage": "Age must be between 18 and 60.", "attemptedValue": 1, "customState": null, "severity": 0, "errorCode": "InclusiveBetweenValidator", "formattedMessagePlaceholderValues": { "From": 18, "To": 60, "PropertyName": "Age", "PropertyValue": 1, "PropertyPath": "Age" } } ] } } ], "value": null, "firstError": { "code": "General.Validation", "description": "A validation error has occurred.", "type": 2, "numericType": 2, "metadata": { "Email": [ { "propertyName": "Email", "errorMessage": "Email is required.", "attemptedValue": "", "customState": null, "severity": 0, "errorCode": "NotEmptyValidator", "formattedMessagePlaceholderValues": { "PropertyName": "Email", "PropertyValue": "", "PropertyPath": "Email" } }, { "propertyName": "Email", "errorMessage": "A valid email address is required.", "attemptedValue": "", "customState": null, "severity": 0, "errorCode": "EmailValidator", "formattedMessagePlaceholderValues": { "PropertyName": "Email", "PropertyValue": "", "PropertyPath": "Email" } } ], "Age": [ { "propertyName": "Age", "errorMessage": "Age must be between 18 and 60.", "attemptedValue": 1, "customState": null, "severity": 0, "errorCode": "InclusiveBetweenValidator", "formattedMessagePlaceholderValues": { "From": 18, "To": 60, "PropertyName": "Age", "PropertyValue": 1, "PropertyPath": "Age" } } ] } } } -
When we need to return single error.
{
"isError": true,
"errors": [
{
"code": "General.Unexpected",
"description": "Something unexpected happened.",
"type": 1,
"numericType": 1,
"metadata": null
}
],
"errorsOrEmptyList": [
{
"code": "General.Unexpected",
"description": "Something unexpected happened.",
"type": 1,
"numericType": 1,
"metadata": null
}
],
"value": null,
"firstError": {
"code": "General.Unexpected",
"description": "Something unexpected happened.",
"type": 1,
"numericType": 1,
"metadata": null
}
}- When returns success result with some value.
{
"isError": false,
"errors": [
{
"code": "ErrorOr.NoErrors",
"description": "Error list cannot be retrieved from a successful ErrorOr.",
"type": 1,
"numericType": 1,
"metadata": null
}
],
"errorsOrEmptyList": [],
"value": {
"name": "John Doe",
"email": "test@test.com",
"age": 21,
"weatherForecasts": [
{
"date": "2024-12-21",
"temperatureC": 25,
"temperatureF": 76,
"summary": "Freezing"
},
{
"date": "2024-12-22",
"temperatureC": -15,
"temperatureF": 6,
"summary": "Bracing"
}
]
},
"firstError": {
"code": "ErrorOr.NoFirstError",
"description": "First error cannot be retrieved from a successful ErrorOr.",
"type": 1,
"numericType": 1,
"metadata": null
}
}- When validation fails using
FluentValidationand gets all the errors.
[
{
"reasons": [],
"message": "Some validation errors.",
"metadata": {
"Email": [
{
"propertyName": "Email",
"errorMessage": "Email is required.",
"attemptedValue": "",
"customState": null,
"severity": 0,
"errorCode": "NotEmptyValidator",
"formattedMessagePlaceholderValues": {
"PropertyName": "Email",
"PropertyValue": "",
"PropertyPath": "Email"
}
},
{
"propertyName": "Email",
"errorMessage": "A valid email address is required.",
"attemptedValue": "",
"customState": null,
"severity": 0,
"errorCode": "EmailValidator",
"formattedMessagePlaceholderValues": {
"PropertyName": "Email",
"PropertyValue": "",
"PropertyPath": "Email"
}
}
],
"Age": [
{
"propertyName": "Age",
"errorMessage": "Age must be between 18 and 60.",
"attemptedValue": 1,
"customState": null,
"severity": 0,
"errorCode": "InclusiveBetweenValidator",
"formattedMessagePlaceholderValues": {
"From": 18,
"To": 60,
"PropertyName": "Age",
"PropertyValue": 1,
"PropertyPath": "Age"
}
}
]
}
}
]- When we need to return single error.
[
{
"reasons": [],
"message": "Some unexpected happened.",
"metadata": {}
}
]- When returns success result with some value.
{
"valueOrDefault": {
"name": "John Doe",
"email": "test@test.com",
"age": 21,
"weatherForecasts": [
{
"date": "2024-12-21",
"temperatureC": 51,
"temperatureF": 123,
"summary": "Cool"
},
{
"date": "2024-12-22",
"temperatureC": 16,
"temperatureF": 60,
"summary": "Chilly"
}
]
},
"value": {
"name": "John Doe",
"email": "test@test.com",
"age": 21,
"weatherForecasts": [
{
"date": "2024-12-21",
"temperatureC": 51,
"temperatureF": 123,
"summary": "Cool"
},
{
"date": "2024-12-22",
"temperatureC": 16,
"temperatureF": 60,
"summary": "Chilly"
}
]
},
"isFailed": false,
"isSuccess": true,
"reasons": [],
"errors": [],
"successes": []
}- When validation fails using
FluentValidationand gets all the errors.
{
"value": null,
"isSuccess": false,
"message": "Request unsuccessful.",
"error": {
"code": "Request.validation",
"description": "Sample request is invalid.",
"errorType": 3,
"metadata": {
"Email": [
{
"propertyName": "Email",
"errorMessage": "Email is required.",
"attemptedValue": "",
"customState": null,
"severity": 0,
"errorCode": "NotEmptyValidator",
"formattedMessagePlaceholderValues": {
"PropertyName": "Email",
"PropertyValue": "",
"PropertyPath": "Email"
}
},
{
"propertyName": "Email",
"errorMessage": "A valid email address is required.",
"attemptedValue": "",
"customState": null,
"severity": 0,
"errorCode": "EmailValidator",
"formattedMessagePlaceholderValues": {
"PropertyName": "Email",
"PropertyValue": "",
"PropertyPath": "Email"
}
}
],
"Age": [
{
"propertyName": "Age",
"errorMessage": "Age must be between 18 and 60.",
"attemptedValue": 1,
"customState": null,
"severity": 0,
"errorCode": "InclusiveBetweenValidator",
"formattedMessagePlaceholderValues": {
"From": 18,
"To": 60,
"PropertyName": "Age",
"PropertyValue": 1,
"PropertyPath": "Age"
}
}
]
}
},
"errors": [],
"hasMultipleErrors": false
}- When we need to return single error.
{
"value": null,
"isSuccess": false,
"message": "Request unsuccessful.",
"error": {
"code": "NotFound",
"description": "A 'Not Found' error has occurred.",
"errorType": 5,
"metadata": null
},
"errors": [],
"hasMultipleErrors": false
}- When we need to return multiple errors.
{
"value": null,
"isSuccess": false,
"message": "Request unsuccessful with multiple errors.",
"error": null,
"errors": [
{
"code": "Request.Validation",
"description": "Sample request is invalid.",
"errorType": 3,
"metadata": null
},
{
"code": "NotFound",
"description": "A 'Not Found' error has occurred.",
"errorType": 5,
"metadata": null
},
{
"code": "Duplicate",
"description": "A conflict error has occurred.",
"errorType": 4,
"metadata": null
}
],
"hasMultipleErrors": true
}- When returns success result with some value.
{
"value": {
"name": "John Doe",
"email": "test@test.com",
"age": 21,
"weatherForecasts": [
{
"date": "2024-12-21",
"temperatureC": 41,
"temperatureF": 105,
"summary": "Mild"
},
{
"date": "2024-12-22",
"temperatureC": 25,
"temperatureF": 76,
"summary": "Balmy"
}
]
},
"isSuccess": true,
"message": "Request successful.",
"error": null,
"errors": [],
"hasMultipleErrors": false
}
- I have tried only some of the available nuget packages along with custom . One of them I missed is
LanguageExt.Core.- Also all the packages has their own way of returning results , values or errors , so I just tried to implement the basic success and failure with
FluentValidationerrors.- The
CustomResulttype is almost similar likeErrorOr, but implemented as one of my need.- There are lot of customizations and improvements can be done according to our requirements.