{"id":23055917,"url":"https://github.com/aklaus/domainresult","last_synced_at":"2026-01-20T12:06:05.980Z","repository":{"id":39887110,"uuid":"285982585","full_name":"AKlaus/DomainResult","owner":"AKlaus","description":"Tiny package for decoupling domain operation results from IActionResult and IResult types of ASP.NET Web API","archived":false,"fork":false,"pushed_at":"2024-12-27T08:23:47.000Z","size":306,"stargazers_count":59,"open_issues_count":9,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-12-30T21:42:17.898Z","etag":null,"topics":["ddd","design-patterns","domain-driven-design","nuget","result","webapi"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AKlaus.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-08-08T05:51:03.000Z","updated_at":"2025-08-05T14:21:12.000Z","dependencies_parsed_at":"2023-12-31T10:25:15.592Z","dependency_job_id":"cc85ff90-3868-44e5-8ffd-d21ca637f2a5","html_url":"https://github.com/AKlaus/DomainResult","commit_stats":{"total_commits":170,"total_committers":2,"mean_commits":85.0,"dds":0.03529411764705881,"last_synced_commit":"f506818e0025f0733a0e6e801e96f3f6aaa5c2e9"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/AKlaus/DomainResult","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AKlaus%2FDomainResult","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AKlaus%2FDomainResult/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AKlaus%2FDomainResult/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AKlaus%2FDomainResult/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AKlaus","download_url":"https://codeload.github.com/AKlaus/DomainResult/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AKlaus%2FDomainResult/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28603365,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-20T12:01:53.233Z","status":"ssl_error","status_checked_at":"2026-01-20T12:01:46.545Z","response_time":117,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ddd","design-patterns","domain-driven-design","nuget","result","webapi"],"created_at":"2024-12-16T01:13:58.953Z","updated_at":"2026-01-20T12:06:05.972Z","avatar_url":"https://github.com/AKlaus.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DomainResult\n\n**NuGet for decoupling domain operation results from IActionResult and IResult types of ASP.NET Web API (the Result Pattern implementation for ASP.NET).**\n\n![CI](https://github.com/AKlaus/DomainResult/workflows/CI/badge.svg)\n[![Test Coverage](https://coveralls.io/repos/github/AKlaus/DomainResult/badge.svg?branch=master)](https://coveralls.io/github/AKlaus/DomainResult?branch=master)\n[![DomainResult NuGet version](https://img.shields.io/nuget/v/DomainResult.svg?style=flat\u0026label=nuget%3A%20DomainResult)](https://www.nuget.org/packages/DomainResult)\n[![DomainResult.Common NuGet version](https://img.shields.io/nuget/v/DomainResult.Common.svg?style=flat\u0026label=nuget%3A%20DomainResult.Common)](https://www.nuget.org/packages/DomainResult.Common)\n[![Downloads](https://img.shields.io/nuget/dt/DomainResult.Common.svg?style=flat)](https://www.nuget.org/packages/DomainResult.Common)\n\n\nTwo tiny NuGet packages for applying the _Result Pattern_ to [ASP.NET Web API](https://dotnet.microsoft.com/apps/aspnet/apis) apps, helping with separation of the services in the _Domain Layer_ (aka _Business Layer_) from the _Application Layer_:\n\n- eliminating dependency on _Microsoft.AspNetCore.*_ ([IActionResult](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.iactionresult) and [IResult](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.iresult) in particular) in the _Domain Layer_ (usually a separate project);\n- mapping various of responses from the _Domain Layer_ to appropriate [ActionResult](https://docs.microsoft.com/en-us/aspnet/core/web-api/action-return-types) in classic Web API controllers or [IResult](https://devblogs.microsoft.com/dotnet/asp-net-core-updates-in-net-6-preview-7/#added-iresult-implementations-for-producing-common-http-responses) in the [minimal API](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis).\n\n### Content:\n\n- [Basic use-case](#basic-use-case)\n- [Quick start](#quick-start)\n- ['DomainResult.Common' package. Returning result from Domain Layer method](#domainresultcommon-package-returning-result-from-domain-layer-method)\n  - [Examples (Domain layer)](#examples-domain-layer)\n  - [Type conversion](#type-conversion)\n  - [Throwing exceptions on failures](#throwing-exceptions-on-failures)\n- ['DomainResult' package](#domainresult-package)\n  - [Conversion to IActionResult](#conversion-to-iactionresult)\n  - [Conversion to IResult (minimal API)](#conversion-to-iresult-minimal-api)\n- [Custom Problem Details output](#custom-problem-details-output)\n  - [Custom response for 2xx HTTP codes](#custom-response-for-2xx-http-codes)\n    - [Example (custom response for IActionResult)](#example-custom-response-for-iactionresult)\n    - [Example (custom response for IResult)](#example-custom-response-for-iresult)\n  - [Custom error handling](#custom-error-handling)\n- [Alternative solutions](#alternative-solutions)\n  - [Why not FluentResults?](#why-not-fluentresults)\n  - [Why not Hellang's ProblemDetails?](#why-not-hellangs-problemdetails)\n  - [Why not Ardalis.Result?](#why-not-ardalisresult)\n\n## Basic use-case\n\nFor a _Domain Layer_ method like this:\n\n```csharp\npublic async Task\u003c(InvoiceResponseDto, IDomainResult)\u003e GetInvoice(int invoiceId)\n{\n    if (invoiceId \u003c 0)\n        // Returns a validation error\n        return IDomainResult.Failed\u003cInvoiceResponseDto\u003e(\"Try harder\");\n\n    var invoice = await DataContext.Invoices.FindAsync(invoiceId);\n\n    if (invoice == null)\n        // Returns a Not Found response\n        return IDomainResult.NotFound\u003cInvoiceResponseDto\u003e();\n\n    // Returns the invoice\n    return IDomainResult.Success(invoice);\n}\n```\n\nor if you're against [ValueTuple](https://docs.microsoft.com/en-us/dotnet/api/system.valuetuple) or static methods on interfaces ([added in C# 8](https://docs.microsoft.com/en-us/dotnet/csharp/tutorials/default-interface-methods-versions#provide-parameterization)), then a more traditional method signature:\n\n```csharp\npublic async Task\u003cIDomainResult\u003cInvoiceResponseDto\u003e\u003e GetInvoice(int invoiceId)\n{\n    if (invoiceId \u003c 0)\n        // Returns a validation error\n        return DomainResult.Failed\u003cInvoiceResponseDto\u003e(\"Try harder\");\n\n    var invoice = await DataContext.Invoices.FindAsync(invoiceId);\n\n    if (invoice == null)\n        // Returns a Not Found response\n        return DomainResult.NotFound\u003cInvoiceResponseDto\u003e();\n\n    // Returns the invoice\n    return DomainResult.Success(invoice);\n}\n```\n\nThe _Web API_ controller method would look like:\n\n```csharp\n[ProducesResponseType(typeof(InvoiceResponseDto), StatusCodes.Status200OK)]\n[ProducesResponseType(StatusCodes.Status400BadRequest)]\n[ProducesResponseType(StatusCodes.Status404NotFound)]\npublic Task\u003cIActionResult\u003e GetInvoice()\n{\n    return _service.GetInvoice().ToActionResult();\n}\n```\n\nor leverage [ActionResult\u0026lt;T\u0026gt;](https://docs.microsoft.com/en-us/aspnet/core/web-api/action-return-types#actionresultt-type) (see [comparison with IActionResult](https://stackoverflow.com/a/54371053/968003))\n\n```csharp\n[ProducesResponseType(StatusCodes.Status200OK)]\n[ProducesResponseType(StatusCodes.Status400BadRequest)]\n[ProducesResponseType(StatusCodes.Status404NotFound)]\npublic Task\u003cActionResult\u003cInvoiceResponseDto\u003e\u003e GetInvoice()\n{\n    return _service.GetInvoice().ToActionResultOfT();\n}\n```\n\nor for the _Minimal APIs_ convert to [IResult](https://devblogs.microsoft.com/dotnet/asp-net-core-updates-in-net-6-preview-7/#added-iresult-implementations-for-producing-common-http-responses):\n\n```csharp\napp.MapGet(\"Invoice\", () =\u003e _service.GetInvoice().ToResult())\n   .Produces\u003cInvoiceResponseDto\u003e()\n   .ProducesProblem(StatusCodes.Status400BadRequest)\n   .ProducesProblem(StatusCodes.Status404NotFound);\n```\n\nThe above returns:\n\n- HTTP code `200 OK` along with an instance of `InvoiceResponseDto` on successful executions.\n- Non-2xx codes wrapped in [ProblemDetails](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.problemdetails) (as per [RFC 7807](https://tools.ietf.org/html/rfc7807)):\n  - HTTP code `400 Bad Request` with a message \"_Try harder_\" when the invoice ID \u003c 1 (the HTTP code can be configured to `422 Unprocessable Entity`).\n  - HTTP code `404 Not Found` for incorrect invoice IDs.\n\n## Quick start\n\n- Install [DomainResult](https://www.nuget.org/packages/DomainResult) NuGet package for the _Web API_ project.\n- Install [DomainResult.Common](https://www.nuget.org/packages/DomainResult.Common) NuGet package for the _Domain Layer_ (aka _Business Layer_) projects. If the _Domain Layer_ is inside the _Web API_ project, then skip this step.\n- Follow the documentation below, `samples` in the repo and common sense.\n\nThe library targets `.NET 7`, `.NET 8`, `.NET 9` and `.NET 10` (for .NET v3 – v6 support see older versions of the library).\n\n## 'DomainResult.Common' package. Returning result from Domain Layer method\n\nA tiny package with no dependency on `Microsoft.AspNetCore.*` namespaces that provides:\n\n- data types for returning from domain operations (wraps up the returned value and adds operation status with error messages if applicable);\n- extension methods to effortlessly form the desired response.\n\nIt's built around `IDomainResult` interface that has 3 properties:\n\n```csharp\nIReadOnlyCollection\u003cstring\u003e Errors { get; } // Collection of error messages if any\nstring Error { get; }                       // Error messages (if any) joined into a line for simplicity\nbool IsSuccess { get; }                     // Flag, whether the current status is successful or not\nDomainOperationStatus Status { get; }       // Current status of the domain operation: Success, Failed, NotFound, Unauthorized, etc.\n```\n\nAnd `IDomainResult\u003cT\u003e` interface that also adds\n\n```csharp\n// Value returned by the domain operation\nT Value { get; }\n```\n\nIt has **60+ static extension methods** to return a successful or unsuccessful result from the domain method with one of the following types:\n\n| Returned type        | Returned type wrapped in `Task` |\n|----------------------|---------------------------------|\n| `IDomainResult`      | `Task\u003cIDomainResult\u003e`           |\n| `IDomainResult\u003cT\u003e`   | `Task\u003cIDomainResult\u003cT\u003e\u003e`        |\n| `(T, IDomainResult)` | `Task\u003c(T, IDomainResult)\u003e`      |\n\n### Examples (Domain layer):\n\n```csharp\n// Successful result with no value\nIDomainResult res = IDomainResult.Success();        // res.Status is 'Success'\n// Successful result with an int\n(value, state) = IDomainResult.Success(10);         // value = 10; state.Status is 'Success'\n// The same but wrapped in a task\nvar res = IDomainResult.SuccessTask(10);            // res is Task\u003c(int, IDomainResult)\u003e\n// Implicit convertion\nIDomainResult\u003cint\u003e res = 10;                        // res.Value = 10; res.Status is 'Success'\n\n// Error message\nIDomainResult res = IDomainResult.Failed(\"Ahh!\");   // res.Status is 'Failed' and res.Errors = new []{ \"Ahh!\" }\n// Error when expected an int\n(value, state) = IDomainResult.Failed\u003cint\u003e(\"Ahh!\"); // value = 0, state.Status is 'Failed' and state.Errors = new []{ \"Ahh!\" }\n\n// 'Not Found' acts like the errors\n(value, state) = IDomainResult.NotFound\u003cint\u003e();     // value = 0, state.Status is 'NotFound'\nTask\u003c(int val, IDomainResult state)\u003e res = IDomainResult.NotFoundTask\u003cint\u003e();  // value = 0, state.Status is 'NotFound'\n\n// 'Unauthorized' response\n(value, state) = IDomainResult.Unauthorized\u003cint\u003e(); // value = 0, state.Status is 'Unauthorized'\n```\n\n_Notes_:\n\n- The `Task` suffix on the extension methods indicates that the returned type is wrapped in a `Task` (e.g. `SuccessTask()`, `FailedTask()`, `NotFoundTask()`, `UnauthorizedTask()`).\n- The `Failed()` and `NotFound()` methods take as input parameters: `string`, `string[]`. `Failed()` can also take [ValidationResult](https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations.validationresult).\n\n### Type conversion\nType conversion comes in handy for propagating errors from nested method calls, e.g. from `IDomainResult` to `IDomainResult\u003cT\u003e`, or the other way around, etc.\n\n```csharp\nIDomainResult failedResult = IDomainResult.Failed(\"Ahh!\");\n\nIDomainResult\u003cint\u003e  resOfInt  = failedResult.To\u003cint\u003e();             // from IDomainResult to IDomainResult\u003cT\u003e\nIDomainResult\u003clong\u003e resOfLong = resOfInt.To\u003clong\u003e();                // from IDomainResult\u003cT\u003e to IDomainResult\u003cV\u003e\n\nDomainResult\u003cint\u003e resFromTuple = (default, failedResult);           // from IDomainResult to DomainResult\u003cT\u003e\n\nTask\u003cIDomainResult\u003e failedResultTask = IDomainResult.FailedTask(\"Ahh!\");\nTask\u003cIDomainResult\u003cint\u003e\u003e    resOfInt = failedResultTask.To\u003cint\u003e();  // from Task\u003cIDomainResult\u003e to Task\u003cIDomainResult\u003cT\u003e\u003e\n```\nNote that returning [Tuple](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/value-tuples) types drastically simplifies type conversions. \n\n### Throwing exceptions on failures\nIn some cases, throwing an exception on failed statuses is the desired behaviour. Use `ThrowIfNoSuccess()` extension method to interrupt the flow with `DomainResultException` exception if the `IDomainResult.IsSuccess == false`:\n\n```csharp\nvar failedResult = IDomainResult.Failed(\"Ahh!\");\nfailedResult.ThrowIfNoSuccess();                    // DomainResultException is thrown here \nfailedResult.ThrowIfNoSuccess\u003cCustomException\u003e();   // CustomException is thrown here \n```\n\n## 'DomainResult' package\n\n**Converts a `IDomainResult`-based object to various `IActionResult` and `IResult`-based types providing 40+ static extension methods.**\n\nThe mapping rules are built around `IDomainResult.Status`:\n\n| `IDomainResult.Status`    | Returned `IActionResult`/`IResult` type with default HTTP code                                                                                                                                                                                                 |\n|---------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `Success`                 | If no value is returned then `204 NoContent` ([docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/204)), otherwise - `200 OK` ([docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/200))\u003cbr\u003eSupports custom codes (e.g. `201 Created`) |\n| `NotFound`                | HTTP code `404 NotFound` ([docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404))                                                                                                                                                                |\n| `Failed`                  | HTTP code `400` ([docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400)) or can be configured to `422` ([docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422)) or any other code                                                  |\n| `Unauthorized`            | HTTP code `403 Forbidden` ([docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/403))                                                                                                                                                               |\n| `Conflict`                | HTTP code `409 Conflict` ([docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/409))                                                                                                                                                                |\n| `ContentTooLarge`         | HTTP code `413 Content Too Large` ([docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/413))                                                                                                                                                       |\n| `CriticalDependencyError` | HTTP code `503 Service Unavailable` ([docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/503))                                                                                                                                                     |\n\n\u003csub\u003e\u003csup\u003eNote: `DomainResult` package has dependency on `Microsoft.AspNetCore.*` namespace and `DomainResult.Common` package.\u003c/sup\u003e\u003c/sub\u003e\n\n### Conversion to IActionResult\n\nFor classic Web API controllers, call the following extension methods on a `IDomainResult` value:\n\n| Returned type     | Returned type wrapped in `Task` | Extension methods                                    |\n|-------------------|---------------------------------|------------------------------------------------------|\n| `IActionResult`   | `Task\u003cIActionResult\u003e`           | `ToActionResult()`\u003cbr\u003e`ToCustomActionResult()`       |\n| `ActionResult\u003cT\u003e` | `Task\u003cActionResult\u003cT\u003e\u003e`         | `ToActionResultOfT()`\u003cbr\u003e`ToCustomActionResultOfT()` |\n\n#### Examples (IActionResult conversion)\n\n```csharp\n// Returns `IActionResult` with HTTP code `204 NoContent` on success\nIDomainResult.ToActionResult();\n// The same as above, but returns `Task\u003cIActionResult\u003e` with no need in 'await'\nTask\u003cIDomainResult\u003e.ToActionResult();\n\n// Returns `IActionResult` with HTTP code `200 Ok` along with the value\nIDomainResult\u003cT\u003e.ToActionResult();\n(T, IDomainResult).ToActionResult();\n// As above, but returns `Task\u003cIActionResult\u003e` with no need in 'await'\nTask\u003cIDomainResult\u003cT\u003e\u003e.ToActionResult();\nTask\u003c(T, IDomainResult)\u003e.ToActionResult();\n\n// Returns `ActionResult\u003cT\u003e` with HTTP code `200 Ok` along with the value\nIDomainResult\u003cT\u003e.ToActionResultOfT();\n(T, IDomainResult).ToActionResultOfT();\n// As above, but returns `Task\u003cActionResult\u003cT\u003e\u003e` with no need in 'await'\nTask\u003cIDomainResult\u003cT\u003e\u003e.ToActionResultOfT();\nTask\u003c(T, IDomainResult)\u003e.ToActionResultOfT();\n```\n\n### Conversion to IResult (minimal API)\n\nFor the modern [minimal API](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis), call `ToResult()` extension method on a `IDomainResult` value to return the corresponding `IResult` instance.\n\n#### Examples (IResult conversion)\n\n```csharp\n// Returns `IResult` with HTTP code `204 NoContent` on success\nIDomainResult.ToResult();\n// The same as above, but returns `Task\u003cIResult\u003e` with no need in 'await'\nTask\u003cIDomainResult\u003e.ToResult();\n\n// Returns `IResult` with HTTP code `200 Ok` along with the value\nIDomainResult\u003cT\u003e.ToResult();\n(T, IDomainResult).ToResult();\n// As above, but returns `Task\u003cIResult\u003e` with no need in 'await'\nTask\u003cIDomainResult\u003cT\u003e\u003e.ToResult();\nTask\u003c(T, IDomainResult)\u003e.ToResult();\n```\n\n## Custom Problem Details output\n\nThere is a way to tune the Problem Details output case-by-case.\n\n### Custom response for 2xx HTTP codes\n\nWhen returning a standard `200` or `204` HTTP code is not enough, there are extension methods to knock yourself out:\n- `ToCustomActionResult()` and `ToCustomActionResultOfT()` for returning `IActionResult`\n- `ToCustomResult()` for returning `IResult`\n\nExamples of returning [201 Created](https://httpstatuses.com/201) along with a location header field pointing to the created resource (as per [RFC7231](https://tools.ietf.org/html/rfc7231#section-7.2)):\n\n#### Example (custom response for IActionResult)\n\n```csharp\n[HttpPost]\n[ProducesResponseType(StatusCodes.Status201Created)]\npublic ActionResult\u003cint\u003e CreateItem(CreateItemDto dto)\n{\n  // Service method for creating an item and returning its ID.\n  // Can return any of the IDomainResult types (e.g. (int, IDomainResult, IDomainResult\u003cint\u003e, Task\u003c...\u003e, etc).\n  var result = _service.CreateItem(dto);\n  // Custom conversion of the successful response only. For others, it returns standard 4xx HTTP codes\n  return result.ToCustomActionResultOfT(\n            // On success returns '201 Created' with a link to '/{id}' route in HTTP headers\n            val =\u003e CreatedAtAction(nameof(GetById), new { id = val }, val)\n        );\n}\n\n// Returns an entity by ID\n[HttpGet(\"{id}\")]\npublic IActionResult GetById([FromRoute] int id)\n{\n\t...\n}\n```\n\nIt works with any of extensions in `Microsoft.AspNetCore.Mvc.ControllerBase`. Here are some:\n\n- [AcceptedAtAction](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.controllerbase.acceptedataction) and [AcceptedAtRoute](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.controllerbase.acceptedatroute) for HTTP code [202 Accepted](https://httpstatuses.com/202);\n- [File](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.controllerbase.File) or [PhysicalFile](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.controllerbase.PhysicalFile) for returning `200 OK` with the specified `Content-Type`, and the specified file name;\n- [Redirect](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.controllerbase.Redirect), [RedirectToRoute](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.controllerbase.RedirectToRoute), [RedirectToAction](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.controllerbase.RedirectToAction) for returning [302 Found](https://httpstatuses.com/302) with various details.\n\n#### Example (custom response for IResult)\n\nA similar example for a custom response with the minimal API would look like this\n\n```csharp\napp.MapPost(\"/\",\n            () =\u003e _service.CreateItem(dto)\n                          .ToCustomResult(val =\u003e TypedResults.CreatedAtRoute(val, \"GetById\", new { id = val }))\n           )\n```\n\n### Custom error handling\n\nThe default HTTP codes for the supported statuses (`Failed`, `NotFound`, etc.) are defined in `ActionResultConventions` class. The default values are:\n\n```csharp\n// The HTTP code to return when a request 'failed' (also can be 422)\nint FailedHttpCode { get; set; }                  = 400;\n// The 'title' property of the returned JSON on HTTP code 400\nstring FailedProblemDetailsTitle { get; set; }    = \"Bad Request\";\n\n// The HTTP code to return when a record not found\nint NotFoundHttpCode { get; set; }                = 404;\n// The 'title' property of the returned JSON on HTTP code 404\nstring NotFoundProblemDetailsTitle { get; set; }  = \"Not Found\";\n\n// ...and so on for `Unauthorized` (403), `Conflict` (409), `Content Too Large` (413), `CriticalDependencyError` (503), etc.\n```\n\nFeel free to change them (hmm... remember they're static, with all the pros and cons). The reasons you may want it:\n\n- Localisation of the titles\n- Favour [422](https://httpstatuses.com/422) HTTP code in stead of [400](https://httpstatuses.com/400) (see opinions [here](https://stackoverflow.com/a/52098667/968003) and [here](https://stackoverflow.com/a/20215807/968003)).\n\nThe extension methods also support a custom response for special cases when the `IDomainResult.Status` requires a different handler:\n\nFor the classic controllers:\n```csharp\n[HttpGet(\"[action]\")]\n[ProducesResponseType(StatusCodes.Status422UnprocessableEntity)]\npublic Task\u003cActionResult\u003cint\u003e\u003e GetFailedWithCustomStatusAndMessage()\n{\n  var res = _service.GetFailedWithNoMessage();\n  return res.ToActionResultOfT(\n            (problemDetails, state) =\u003e\n            {\n              if (state.Errors?.Any() == true)\n                return;\n              problemDetails.Status = 422;      // Replace the default 400 code\n              problemDetails.Title = \"D'oh!\";   // Replace the default 'Bad Request' title\n              problemDetails.Detail = \"I wish devs put more efforts into it...\";   // Custom message\n            });\n}\n```\nThe same for the minimal API:\n```csharp\napp.MapGet(\"/\", \n           () =\u003e _service.GetFailedWithNoMessage()\n                         .ToResult((problemDetails, state) =\u003e\n                            {\n                                if (state.Errors.Any())\n                                    return;\n                                problemDetails.Status = 422;\n                                problemDetails.Title = \"D'oh!\";\n                                problemDetails.Detail = \"I wish devs put more efforts into it...\";\n                            }))\n```\n\n## Alternative solutions\n\nThe problem solved here is not unique, so how does _DomainResult_ stand out?\n\n### Why not FluentResults?\n\n[FluentResults](https://github.com/altmann/FluentResults) is a great tool for indicating success or failure in the returned object. But there are different objectives:\n\n- _FluentResults_ provides a generalised container for returning results and potential errors;\n- _DomainResult_ is focused on a more specialised case when the Domain Logic is consumed by Web API.\n\nHence, _DomainResult_ provides out-of-the-box:\n\n- Specialised extension methods (like `IDomainResult.NotFound()` that in _FluentResult_ would be indistinctive from other errors)\n- Supports various ways of conversions to `ActionResult` (returning _Problem Details_ in case of error), functionality that is not available in _FluentResults_ and quite weak in the other NuGets extending _FluentResults_.\n\n### Why not Hellang's ProblemDetails?\n\n[Hellang.Middleware.ProblemDetails](https://github.com/khellang/Middleware) is another good one, where you can map exceptions to problem details.\n\nIn this case, the difference is ideological - \"_throwing exception_\" vs \"_returning a faulty status_\" for the sad path of execution in the business logic.\n\nMain distinctive features of _DomainResult_ are\n\n- Allows simpler nested calls of the domain logic (no exceptions handlers when severity of their \"sad\" path is not exception-worthy).\n- Provides a predefined set of responses for main execution paths (\"_bad request_\", \"_not found_\", etc.). Works out-of-the-box.\n- Has an option to tune each output independently.\n\n### Why not Ardalis.Result?\n\n[Ardalis.Result](https://github.com/ardalis/Result) is a popular, feature-rich tool, which is close ideologically. It has support of adjoined use-cases and implicit conversion of results to the HTTP output. The latter feature might look tempting at first glance but can also pose an obstacle when using Swagger or other tools relying on the traditional method signature. \n\nYou may like the _DomainResult_ more due to\n\n- Simpler library with narrow focus.\n- An option to use `ValueTuple` as return parameters.\n- Cool extensions for conversion to HTTP output (e.g. ad-hoc conversion to a customised _201 Created_ result).\n\nMany men, many minds. Make a conscious choice.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faklaus%2Fdomainresult","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faklaus%2Fdomainresult","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faklaus%2Fdomainresult/lists"}