close
Skip to content

anikdakua03/Results-Pattern

Repository files navigation

All Results Pattern

Table of Contents

  1. Introduction
  2. Sample Requests
  3. Example responses
    1. Ardalis
    2. Error-or
    3. Fluent Results
    4. CSharp Functional Extension
    5. Custom Result Implementation
  4. Some Notes
  5. References

Introduction:-

  • 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.

Used Request body:-

  • InValid
{
    "name": "John Doe",
    "age": 1,
    "email" : ""
}
  • Valid
{
    "name": "John Doe",
    "age": 21,
    "email": "test@test.com"
}

Sample responses:-

Using Ardalis.Result

  1. When validation fails using FluentValidation and 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
        }
      ]
    }
  2. 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": []
    }
  3. 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": []
    }

Using CSharpFunctionalExtensions

  1. When validation fails using FluentValidation and 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"
            }
        }
    ]
  2. When returns success result with some value.

    {
      "name": "John Doe",
      "email": "test@test.com",
      "age": 21
    }

Using ErrorOr

  1. When validation fails using FluentValidation and 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"
                        }
                    }
                ]
            }
        }
    }
  2. 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
    }
}
  1. 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
    }
}

Using FluentResults

  1. When validation fails using FluentValidation and 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"
                    }
                }
            ]
        }
    }
]
  1. When we need to return single error.
[
  {
    "reasons": [],
    "message": "Some unexpected happened.",
    "metadata": {}
  }
]
  1. 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": []
}

Using CustomResult

  1. When validation fails using FluentValidation and 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
}
  1. 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
}
  1. 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
}
  1. 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
}

NOTES:-

  • 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 FluentValidation errors.
  • The CustomResult type is almost similar like ErrorOr , but implemented as one of my need.
  • There are lot of customizations and improvements can be done according to our requirements.


References:-


About

This is simple implementation of Results pattern which includes from popular NUGET packages along with my custom.

Topics

Resources

Stars

Watchers

Forks

Contributors

Languages