From 55eb78a72378394f76c267bfa5af0ec1170d69c3 Mon Sep 17 00:00:00 2001 From: Tim Schneider <43130816+DerStimmler@users.noreply.github.com> Date: Wed, 19 Mar 2025 17:01:41 +0100 Subject: [PATCH 1/6] docs(readme): improve readme --- README.md | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 66a3858..838090e 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,11 @@ HttpResults in your Web-API ## Overview -This library streamlines returning HttpResults from endpoints that leverage [CSharpFunctionalExtensions](https://github.com/vkhorikov/CSharpFunctionalExtensions) by offering convenient extension methods to map you result to an HttpResult. +This library streamlines returning HttpResults from endpoints that leverage [CSharpFunctionalExtensions](https://github.com/vkhorikov/CSharpFunctionalExtensions) by offering convenient extension methods to map your `Result`s to HttpResults. With these, you can maintain a fluent, railway-oriented style by simply invoking the appropriate method at the end of your result chain. -It also supports custom error types and ensures a clear separation between your domain logic and API by using specific mappers to translate domain details into API responses. +It also supports custom error types and ensures a clear separation between your domain logic and Web-API by allowing custom mappers to translate domain details into API responses. + +It's compatible with Minimal APIs and controllers. ## Installation @@ -29,12 +31,12 @@ PM> Install-Package CSharpFunctionalExtensions.HttpResults ``` > [!TIP] -> This library references a fairly old version of CSharpFunctionalExtensions for compatibility reasons. -> It's recommended to also install the latest version of CSharpFunctionalExtensions in your project to get the latest features and fixes. +> This library references an older version of CSharpFunctionalExtensions for wider compatibility. +> It's recommended to additionally install the latest version of CSharpFunctionalExtensions in your project to get the latest features and fixes. ## Usage -This library provides you extension methods to map the following `Result` types to `HttpResults`: +This library provides you extension methods to map the following `Result` types to `HttpResults` at the end of our result chain: - `Result` - `Result` @@ -84,7 +86,7 @@ All methods are available in sync and async variants. ### Default mapping -By default, `Result` and `Result` failures get mapped to a `ProblemHttpResult` based on [RFC9457](https://www.rfc-editor.org/rfc/rfc9457). +By default, `Result` and `Result` failures are mapped to a `ProblemHttpResult` based on [RFC9457](https://www.rfc-editor.org/rfc/rfc9457). - The `status` property contains the status code of the HTTP response. Note: For almost every method you can override the default status codes for Success/Failure case. - The `type` property contains a URI to the corresponding [RFC9110](https://tools.ietf.org/html/rfc9110) entry based on the status code. @@ -95,7 +97,7 @@ This default mapping behaviour is configured inside the [`ProblemDetailsMappingP #### Override default mapping -You can override this behaviour by providing your own dictionary which maps status code to title and type of the resulting `ProblemDetails` object. +You can override this behavior by providing your own dictionary that maps status codes to their corresponding `title` and `type` of the resulting `ProblemDetails` object.
Example for changing the default mapping for german localization @@ -126,7 +128,7 @@ ProblemDetailsMappingProvider.DefaultMappings = new Dictionary -You don't have to provide the whole dictionary but can also override or add only mappings for specific status codes like this: +You don't have to provide the whole dictionary; you can also override or add mappings for specific status codes like this: ```csharp ProblemDetailsMappingProvider.AddOrUpdateMapping(420, "Enhance Your Calm", "https://http-status-code.de/420/"); @@ -136,7 +138,7 @@ It's recommended to override the mappings during startup e.g. in `Program.cs`. #### Override mapping for single use case -If you need to override the mapping for a specific use case in a single location, you can provide an `Action` to fully customize the ProblemDetails. This is particularly useful when you want to add extensions or tailor the `ProblemDetails` specifically for that use case. +If you need to override the mapping for a specific use case in a single location, you can provide an `Action` to fully customize the `ProblemDetails`. This is particularly useful when you want to add extensions or tailor the `ProblemDetails` specifically for that use case. ```csharp ... @@ -155,7 +157,7 @@ When using `Result` or `UnitResult`, this library uses a Source Generato ```csharp public record UserNotFoundError(string UserId); ``` -2. Create a mapper that implements `IResultErrorMapper` which maps this custom error type to an HttpResult / `IResult` that you want to return in your Web-API: +2. Create a mapper that implements `IResultErrorMapper` which maps this custom error type to an HttpResult / `Microsoft.AspNetCore.Http.IResult` that you want to return in your Web-API: ```csharp public class UserNotFoundErrorMapper : IResultErrorMapper { @@ -175,25 +177,25 @@ When using `Result` or `UnitResult`, this library uses a Source Generato ``` 3. Use the auto generated extension method: ```csharp - app.MapGet("/users/{id}", (string id) => { - return userRepository.Find(id) //Result - .ToOkHttpResult(); //Results,ProblemHttpResult> - }); + app.MapGet("/users/{id}", (string id, UserRepository repo) => + repo.Find(id) //Result + .ToOkHttpResult() //Results,ProblemHttpResult> + ); ``` > [!IMPORTANT] -> Make sure that every custom error type has exactly one corresponding `IResultMapper` implementation. - -You can use the `ProblemDetailsMappingProvider.FindMapping()` method to find a suitable title and type for a status code based on [RFC9110](https://tools.ietf.org/html/rfc9110). +> Make sure that each custom error type has exactly one corresponding `IResultMapper` implementation. > [!TIP] +> You can use the `ProblemDetailsMappingProvider.FindMapping()` method to find a suitable title and type for a status code based on [RFC9110](https://tools.ietf.org/html/rfc9110). +> > If extension methods for custom errors are missing, rebuild the project to trigger Source Generation. ## Analyzers This library includes analyzers to help you use it correctly. -For example, they can notify you if you have multiple mappers for the same custom error type. +For example, they will notify you if you have multiple mappers for the same custom error type. You can find a complete list of all analyzers [here](https://github.com/co-IT/CSharpFunctionalExtensions.HttpResults/blob/main/CSharpFunctionalExtensions.HttpResults.Generators/AnalyzerReleases.Shipped.md). @@ -203,7 +205,7 @@ Examples for CRUD, FileStreams, custom errors, etc. in context of a Web-API are ## Development -You're welcome to contribute. Please keep these few rules in mind: +Contributions are welcome! Please keep the following rules in mind: - add documentation in the form of summary comments - add tests for your additions From 37e16706e6ab9664ed86689b12709b60107d8f34 Mon Sep 17 00:00:00 2001 From: Tim Schneider <43130816+DerStimmler@users.noreply.github.com> Date: Wed, 19 Mar 2025 23:45:40 +0100 Subject: [PATCH 2/6] docs: improve documentation --- .../AddBook/AddBookEndpoint.cs | 4 +- .../{Books => CRUD}/AddBook/AddBookRequest.cs | 2 +- .../Features/{Books => CRUD}/Book.cs | 2 +- .../Features/{Books => CRUD}/BookService.cs | 2 +- .../Features/{Books => CRUD}/BooksGroup.cs | 14 ++--- .../DeleteBook/DeleteBookEndpoint.cs | 2 +- .../Features/{Books => CRUD}/FakeBooks.cs | 2 +- .../FindBook/FindBookEndpoint.cs | 2 +- .../FindBookCover/FindBookCoverEndpoint.cs | 2 +- .../GetBooks/GetBooksEndpoint.cs | 2 +- .../UpdateBook/UpdateBookEndpoint.cs | 2 +- .../UpdateBook/UpdateBookRequest.cs | 2 +- .../MultipleErrorChain/MultipleErrorChain.cs | 8 ++- .../Program.cs | 2 +- ...arpFunctionalExtensions.HttpResults.csproj | 2 +- README.md | 55 ++++++++++++++----- 16 files changed, 70 insertions(+), 35 deletions(-) rename CSharpFunctionalExtensions.HttpResults.Examples/Features/{Books => CRUD}/AddBook/AddBookEndpoint.cs (96%) rename CSharpFunctionalExtensions.HttpResults.Examples/Features/{Books => CRUD}/AddBook/AddBookRequest.cs (86%) rename CSharpFunctionalExtensions.HttpResults.Examples/Features/{Books => CRUD}/Book.cs (98%) rename CSharpFunctionalExtensions.HttpResults.Examples/Features/{Books => CRUD}/BookService.cs (98%) rename CSharpFunctionalExtensions.HttpResults.Examples/Features/{Books => CRUD}/BooksGroup.cs (54%) rename CSharpFunctionalExtensions.HttpResults.Examples/Features/{Books => CRUD}/DeleteBook/DeleteBookEndpoint.cs (96%) rename CSharpFunctionalExtensions.HttpResults.Examples/Features/{Books => CRUD}/FakeBooks.cs (98%) rename CSharpFunctionalExtensions.HttpResults.Examples/Features/{Books => CRUD}/FindBook/FindBookEndpoint.cs (97%) rename CSharpFunctionalExtensions.HttpResults.Examples/Features/{Books => CRUD}/FindBookCover/FindBookCoverEndpoint.cs (97%) rename CSharpFunctionalExtensions.HttpResults.Examples/Features/{Books => CRUD}/GetBooks/GetBooksEndpoint.cs (97%) rename CSharpFunctionalExtensions.HttpResults.Examples/Features/{Books => CRUD}/UpdateBook/UpdateBookEndpoint.cs (97%) rename CSharpFunctionalExtensions.HttpResults.Examples/Features/{Books => CRUD}/UpdateBook/UpdateBookRequest.cs (85%) diff --git a/CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/AddBook/AddBookEndpoint.cs b/CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/AddBook/AddBookEndpoint.cs similarity index 96% rename from CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/AddBook/AddBookEndpoint.cs rename to CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/AddBook/AddBookEndpoint.cs index f6db45d..f01ecb5 100644 --- a/CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/AddBook/AddBookEndpoint.cs +++ b/CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/AddBook/AddBookEndpoint.cs @@ -1,9 +1,9 @@ -using CSharpFunctionalExtensions.HttpResults.Examples.Features.Books.FindBook; +using CSharpFunctionalExtensions.HttpResults.Examples.Features.CRUD.FindBook; using CSharpFunctionalExtensions.HttpResults.ResultExtensions; using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; -namespace CSharpFunctionalExtensions.HttpResults.Examples.Features.Books.AddBook; +namespace CSharpFunctionalExtensions.HttpResults.Examples.Features.CRUD.AddBook; public static class AddBookEndpoint { diff --git a/CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/AddBook/AddBookRequest.cs b/CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/AddBook/AddBookRequest.cs similarity index 86% rename from CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/AddBook/AddBookRequest.cs rename to CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/AddBook/AddBookRequest.cs index 19b27b5..bbcf7c2 100644 --- a/CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/AddBook/AddBookRequest.cs +++ b/CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/AddBook/AddBookRequest.cs @@ -1,3 +1,3 @@ -namespace CSharpFunctionalExtensions.HttpResults.Examples.Features.Books.AddBook; +namespace CSharpFunctionalExtensions.HttpResults.Examples.Features.CRUD.AddBook; public record AddBookRequest(string Title, string Author, byte[]? Cover); diff --git a/CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/Book.cs b/CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/Book.cs similarity index 98% rename from CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/Book.cs rename to CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/Book.cs index a0399fe..fda6314 100644 --- a/CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/Book.cs +++ b/CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/Book.cs @@ -1,4 +1,4 @@ -namespace CSharpFunctionalExtensions.HttpResults.Examples.Features.Books; +namespace CSharpFunctionalExtensions.HttpResults.Examples.Features.CRUD; public class Book { diff --git a/CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/BookService.cs b/CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/BookService.cs similarity index 98% rename from CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/BookService.cs rename to CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/BookService.cs index f5537fa..9e3ecb9 100644 --- a/CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/BookService.cs +++ b/CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/BookService.cs @@ -1,4 +1,4 @@ -namespace CSharpFunctionalExtensions.HttpResults.Examples.Features.Books; +namespace CSharpFunctionalExtensions.HttpResults.Examples.Features.CRUD; public class BookService { diff --git a/CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/BooksGroup.cs b/CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/BooksGroup.cs similarity index 54% rename from CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/BooksGroup.cs rename to CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/BooksGroup.cs index a0441a9..8d526de 100644 --- a/CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/BooksGroup.cs +++ b/CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/BooksGroup.cs @@ -1,11 +1,11 @@ -using CSharpFunctionalExtensions.HttpResults.Examples.Features.Books.AddBook; -using CSharpFunctionalExtensions.HttpResults.Examples.Features.Books.DeleteBook; -using CSharpFunctionalExtensions.HttpResults.Examples.Features.Books.FindBook; -using CSharpFunctionalExtensions.HttpResults.Examples.Features.Books.FindBookCover; -using CSharpFunctionalExtensions.HttpResults.Examples.Features.Books.GetBooks; -using CSharpFunctionalExtensions.HttpResults.Examples.Features.Books.UpdateBook; +using CSharpFunctionalExtensions.HttpResults.Examples.Features.CRUD.AddBook; +using CSharpFunctionalExtensions.HttpResults.Examples.Features.CRUD.DeleteBook; +using CSharpFunctionalExtensions.HttpResults.Examples.Features.CRUD.FindBook; +using CSharpFunctionalExtensions.HttpResults.Examples.Features.CRUD.FindBookCover; +using CSharpFunctionalExtensions.HttpResults.Examples.Features.CRUD.GetBooks; +using CSharpFunctionalExtensions.HttpResults.Examples.Features.CRUD.UpdateBook; -namespace CSharpFunctionalExtensions.HttpResults.Examples.Features.Books; +namespace CSharpFunctionalExtensions.HttpResults.Examples.Features.CRUD; public static class BooksGroup { diff --git a/CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/DeleteBook/DeleteBookEndpoint.cs b/CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/DeleteBook/DeleteBookEndpoint.cs similarity index 96% rename from CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/DeleteBook/DeleteBookEndpoint.cs rename to CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/DeleteBook/DeleteBookEndpoint.cs index 978a4db..c1e2620 100644 --- a/CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/DeleteBook/DeleteBookEndpoint.cs +++ b/CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/DeleteBook/DeleteBookEndpoint.cs @@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; -namespace CSharpFunctionalExtensions.HttpResults.Examples.Features.Books.DeleteBook; +namespace CSharpFunctionalExtensions.HttpResults.Examples.Features.CRUD.DeleteBook; public static class DeleteBookEndpoint { diff --git a/CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/FakeBooks.cs b/CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/FakeBooks.cs similarity index 98% rename from CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/FakeBooks.cs rename to CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/FakeBooks.cs index ee8b5cf..0dbb2ad 100644 --- a/CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/FakeBooks.cs +++ b/CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/FakeBooks.cs @@ -2,7 +2,7 @@ using System.Web; using Bogus; -namespace CSharpFunctionalExtensions.HttpResults.Examples.Features.Books; +namespace CSharpFunctionalExtensions.HttpResults.Examples.Features.CRUD; public static class FakeBooks { diff --git a/CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/FindBook/FindBookEndpoint.cs b/CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/FindBook/FindBookEndpoint.cs similarity index 97% rename from CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/FindBook/FindBookEndpoint.cs rename to CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/FindBook/FindBookEndpoint.cs index 4f9e765..90f66b2 100644 --- a/CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/FindBook/FindBookEndpoint.cs +++ b/CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/FindBook/FindBookEndpoint.cs @@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; -namespace CSharpFunctionalExtensions.HttpResults.Examples.Features.Books.FindBook; +namespace CSharpFunctionalExtensions.HttpResults.Examples.Features.CRUD.FindBook; public static class FindBookEndpoint { diff --git a/CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/FindBookCover/FindBookCoverEndpoint.cs b/CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/FindBookCover/FindBookCoverEndpoint.cs similarity index 97% rename from CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/FindBookCover/FindBookCoverEndpoint.cs rename to CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/FindBookCover/FindBookCoverEndpoint.cs index ed8c662..81b3fd5 100644 --- a/CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/FindBookCover/FindBookCoverEndpoint.cs +++ b/CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/FindBookCover/FindBookCoverEndpoint.cs @@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; -namespace CSharpFunctionalExtensions.HttpResults.Examples.Features.Books.FindBookCover; +namespace CSharpFunctionalExtensions.HttpResults.Examples.Features.CRUD.FindBookCover; public static class FindBookCoverEndpoint { diff --git a/CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/GetBooks/GetBooksEndpoint.cs b/CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/GetBooks/GetBooksEndpoint.cs similarity index 97% rename from CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/GetBooks/GetBooksEndpoint.cs rename to CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/GetBooks/GetBooksEndpoint.cs index 1a33483..ba681a1 100644 --- a/CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/GetBooks/GetBooksEndpoint.cs +++ b/CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/GetBooks/GetBooksEndpoint.cs @@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; -namespace CSharpFunctionalExtensions.HttpResults.Examples.Features.Books.GetBooks; +namespace CSharpFunctionalExtensions.HttpResults.Examples.Features.CRUD.GetBooks; public static class GetBooksEndpoint { diff --git a/CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/UpdateBook/UpdateBookEndpoint.cs b/CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/UpdateBook/UpdateBookEndpoint.cs similarity index 97% rename from CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/UpdateBook/UpdateBookEndpoint.cs rename to CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/UpdateBook/UpdateBookEndpoint.cs index f98f076..98ad654 100644 --- a/CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/UpdateBook/UpdateBookEndpoint.cs +++ b/CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/UpdateBook/UpdateBookEndpoint.cs @@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; -namespace CSharpFunctionalExtensions.HttpResults.Examples.Features.Books.UpdateBook; +namespace CSharpFunctionalExtensions.HttpResults.Examples.Features.CRUD.UpdateBook; public static class UpdateBookEndpoint { diff --git a/CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/UpdateBook/UpdateBookRequest.cs b/CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/UpdateBook/UpdateBookRequest.cs similarity index 85% rename from CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/UpdateBook/UpdateBookRequest.cs rename to CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/UpdateBook/UpdateBookRequest.cs index d3a420f..f5652fa 100644 --- a/CSharpFunctionalExtensions.HttpResults.Examples/Features/Books/UpdateBook/UpdateBookRequest.cs +++ b/CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD/UpdateBook/UpdateBookRequest.cs @@ -1,3 +1,3 @@ -namespace CSharpFunctionalExtensions.HttpResults.Examples.Features.Books.UpdateBook; +namespace CSharpFunctionalExtensions.HttpResults.Examples.Features.CRUD.UpdateBook; public record UpdateBookRequest(string Title, string Author, byte[]? Cover); diff --git a/CSharpFunctionalExtensions.HttpResults.Examples/Features/MultipleErrorChain/MultipleErrorChain.cs b/CSharpFunctionalExtensions.HttpResults.Examples/Features/MultipleErrorChain/MultipleErrorChain.cs index 0a5158b..8459553 100644 --- a/CSharpFunctionalExtensions.HttpResults.Examples/Features/MultipleErrorChain/MultipleErrorChain.cs +++ b/CSharpFunctionalExtensions.HttpResults.Examples/Features/MultipleErrorChain/MultipleErrorChain.cs @@ -1,8 +1,14 @@ -using CSharpFunctionalExtensions.HttpResults.Examples.Features.Books; +using CSharpFunctionalExtensions.HttpResults.Examples.Features.CRUD; using Microsoft.AspNetCore.Http.HttpResults; namespace CSharpFunctionalExtensions.HttpResults.Examples.Features.MultipleErrorChain; +/// +/// Because C# does not natively support union types there is no optimal solution to handle different +/// types of errors in a chain of . +/// This is the best workaround I could find for now. +/// + // 1. Create base type for all errors and define concrete errors as child type public record BookError; diff --git a/CSharpFunctionalExtensions.HttpResults.Examples/Program.cs b/CSharpFunctionalExtensions.HttpResults.Examples/Program.cs index e21ea6d..f993264 100644 --- a/CSharpFunctionalExtensions.HttpResults.Examples/Program.cs +++ b/CSharpFunctionalExtensions.HttpResults.Examples/Program.cs @@ -1,5 +1,5 @@ using CSharpFunctionalExtensions.HttpResults; -using CSharpFunctionalExtensions.HttpResults.Examples.Features.Books; +using CSharpFunctionalExtensions.HttpResults.Examples.Features.CRUD; using CSharpFunctionalExtensions.HttpResults.Examples.Features.CustomError; using CSharpFunctionalExtensions.HttpResults.Examples.Features.FileStream; using Scalar.AspNetCore; diff --git a/CSharpFunctionalExtensions.HttpResults/CSharpFunctionalExtensions.HttpResults.csproj b/CSharpFunctionalExtensions.HttpResults/CSharpFunctionalExtensions.HttpResults.csproj index 19b6e45..ebfbd69 100644 --- a/CSharpFunctionalExtensions.HttpResults/CSharpFunctionalExtensions.HttpResults.csproj +++ b/CSharpFunctionalExtensions.HttpResults/CSharpFunctionalExtensions.HttpResults.csproj @@ -15,7 +15,7 @@ CSharpFunctionalExtensions.HttpResults co-IT, Stimmler - Extensions for CSharpFunctionalExtensions to map Results to HttpResults in your Web-API + Seamlessly map Results from CSharpFunctionalExtensions to HttpResults for cleaner, more fluent Web APIs Copyright (c) co-IT.eu GmbH 2025 README.md https://github.com/co-IT/CSharpFunctionalExtensions.HttpResults diff --git a/README.md b/README.md index 838090e..9bb1c88 100644 --- a/README.md +++ b/README.md @@ -5,16 +5,38 @@ [![nuget downloads](https://img.shields.io/nuget/dt/CSharpFunctionalExtensions.HttpResults)](https://www.nuget.org/packages/CSharpFunctionalExtensions.HttpResults/) [![GitHub license](https://img.shields.io/github/license/co-IT/CSharpFunctionalExtensions.HttpResults)](https://github.com/co-IT/CSharpFunctionalExtensions.HttpResults/blob/main/LICENSE.md) -Extensions for [CSharpFunctionalExtensions](https://github.com/vkhorikov/CSharpFunctionalExtensions) to map Results to -HttpResults in your Web-API +Seamlessly map Results from [CSharpFunctionalExtensions](https://github.com/vkhorikov/CSharpFunctionalExtensions) to HttpResults for cleaner, more fluent Web APIs + +
+Table of Contents + +1. [Overview](#overview) +2. [Installation](#installation) +3. [Usage](#usage) + 1. [Default mapping](#default-mapping) + 2. [Custom error mapping](#custom-error-mapping) +4. [Analyzers](#analyzers) +5. [Examples](#examples) +6. [Development](#development) +
## Overview -This library streamlines returning HttpResults from endpoints that leverage [CSharpFunctionalExtensions](https://github.com/vkhorikov/CSharpFunctionalExtensions) by offering convenient extension methods to map your `Result`s to HttpResults. -With these, you can maintain a fluent, railway-oriented style by simply invoking the appropriate method at the end of your result chain. -It also supports custom error types and ensures a clear separation between your domain logic and Web-API by allowing custom mappers to translate domain details into API responses. +This library provides convenient extension methods to seamlessly map Results from [CSharpFunctionalExtensions](https://github.com/vkhorikov/CSharpFunctionalExtensions) to HttpResults. With this, it streamlines your Web API resulting in cleaner, more fluent code. -It's compatible with Minimal APIs and controllers. +### Key Benefits + +- ⚙️ **Zero Configuration:** Get started immediately — the mapping works out of the box without any configuration. +- 🛠️ **Customizable Mappings:** Tailor default mappings or define custom mappings for specific use cases. +- 🌐 **Localization Ready:** Supports custom error messages and status codes, easily adaptable to your localization needs. +- 🔗 **Fluent API:** Maintain a smooth, railway-oriented flow by chaining HttpResult mappings at the end of your Result chain. +- 🧱 **Separation of Domain and HTTP Errors:** Keeps domain errors distinct from HTTP errors, improving maintainability and clarity between business logic and web API concerns. +- ⚡ **Minimal APIs & Controllers Support:** Works with both Minimal APIs and traditional controllers in ASP.NET. +- 📦 **Full Support for ASP.NET Results:** Supports all built-in HTTP response types in ASP.NET, including `Ok`, `Created`, `NoContent`, `Accepted`, `FileStream`, and more. +- 🦺 **Typed Results:** Utilizes typed results for consistent, type-safe API responses. +- 📑 **OpenAPI Ready:** Ensures accurate OpenAPI generation for clear and reliable API documentation. +- 🛡️ **RFC Compliance:** Default mappings adhere to the RFC 9457 standard (`ProblemDetails`), ensuring your API errors are standardized and interoperable. +- 🧑‍💻 **Developer-Friendly:** Includes built-in analyzers and source generators to speed up development and reduce errors. ## Installation @@ -30,7 +52,7 @@ or PM> Install-Package CSharpFunctionalExtensions.HttpResults ``` -> [!TIP] +> [!NOTE] > This library references an older version of CSharpFunctionalExtensions for wider compatibility. > It's recommended to additionally install the latest version of CSharpFunctionalExtensions in your project to get the latest features and fixes. @@ -51,7 +73,7 @@ app.MapGet("/books", (BookService service) => ); ``` -These methods are available: +These methods are available in sync and async variants: | Method | Short Description | |---------------------------------------|------------------------------------------------------------------------------| @@ -82,8 +104,6 @@ These methods are available: | `.ToContentHttpResult()` | Returns `ContentHttpResult` or `ProblemHttpResult` | | `.ToContentHttpResult()` | Returns `ContentHttpResult` or custom error | -All methods are available in sync and async variants. - ### Default mapping By default, `Result` and `Result` failures are mapped to a `ProblemHttpResult` based on [RFC9457](https://www.rfc-editor.org/rfc/rfc9457). @@ -157,7 +177,7 @@ When using `Result` or `UnitResult`, this library uses a Source Generato ```csharp public record UserNotFoundError(string UserId); ``` -2. Create a mapper that implements `IResultErrorMapper` which maps this custom error type to an HttpResult / `Microsoft.AspNetCore.Http.IResult` that you want to return in your Web-API: +2. Create a mapper that implements `IResultErrorMapper` which maps this custom error type to an HttpResult / `Microsoft.AspNetCore.Http.IResult` that you want to return in your Web API: ```csharp public class UserNotFoundErrorMapper : IResultErrorMapper { @@ -188,7 +208,8 @@ When using `Result` or `UnitResult`, this library uses a Source Generato > [!TIP] > You can use the `ProblemDetailsMappingProvider.FindMapping()` method to find a suitable title and type for a status code based on [RFC9110](https://tools.ietf.org/html/rfc9110). -> + +> [!NOTE] > If extension methods for custom errors are missing, rebuild the project to trigger Source Generation. ## Analyzers @@ -201,7 +222,15 @@ You can find a complete list of all analyzers [here](https://github.com/co-IT/CS ## Examples -Examples for CRUD, FileStreams, custom errors, etc. in context of a Web-API are available in the [`CSharpFunctionalExtensions.HttpResults.Examples`](CSharpFunctionalExtensions.HttpResults.Examples) project. +The [`CSharpFunctionalExtensions.HttpResults.Examples`](CSharpFunctionalExtensions.HttpResults.Examples) project contains various examples demonstrating how to use this library in different scenarios, including: + +- **[Basic CRUD operations](CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD)** – Handling `GET`, `POST`, `PUT`, and `DELETE` requests +- **[File handling](CSharpFunctionalExtensions.HttpResults.Examples/Features/FileStream)** – Returning files from your Web API +- **[Custom error mapping](CSharpFunctionalExtensions.HttpResults.Examples/Features/CustomError)** – Defining and mapping custom error types to meaningful HTTP responses +- **[Multiple errors in chain](CSharpFunctionalExtensions.HttpResults.Examples/Features/MultipleErrorChain)** – Using different kind of custom errors in the same result chain +- **[Customizing default mapping](CSharpFunctionalExtensions.HttpResults.Examples/Program.cs)** – Overriding default mappings for localization or specific use cases + +Check out the example project for hands-on implementation details! ## Development From 990a5173cc3ec66d37bbdf688af93589239e2280 Mon Sep 17 00:00:00 2001 From: Tim Schneider <43130816+DerStimmler@users.noreply.github.com> Date: Fri, 21 Mar 2025 21:16:15 +0100 Subject: [PATCH 3/6] ci: add link checking --- .github/workflows/check.yml | 11 ++++++++++- .../README.md | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index e697c35..e82016a 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -11,7 +11,7 @@ jobs: test-versions: strategy: matrix: - csharpFunctionalExtensionsVersion: [ '2.29.0', '2.*', '3.0.0', '3.*' ] + csharpFunctionalExtensionsVersion: ["2.29.0", "2.*", "3.0.0", "3.*"] fail-fast: false uses: ./.github/workflows/test-version.yml with: @@ -46,3 +46,12 @@ jobs: - name: Lint run: dotnet csharpier --check . + + - name: Link Checker + id: lychee + uses: lycheeverse/lychee-action@v2 + with: + fail: true + args: --include-fragments . + jobSummary: true + format: markdown diff --git a/CSharpFunctionalExtensions.HttpResults.Examples/README.md b/CSharpFunctionalExtensions.HttpResults.Examples/README.md index 220ea99..b723307 100644 --- a/CSharpFunctionalExtensions.HttpResults.Examples/README.md +++ b/CSharpFunctionalExtensions.HttpResults.Examples/README.md @@ -6,11 +6,11 @@ This project contains a WebApi to showcase several features of `CSharpFunctional ### CRUD -CRUD operations of an Book store are available under [`Features/Books`](./Features/Books). +CRUD operations of an Book store are available under [`Features/CRUD`](./Features/CRUD). ### Files -An example for a `FileContentResult` is available under [`Features/Books/FindBookCover/FindBookCoverEndpoint.cs`](./Features/Books/FindBookCover/FindBookCoverEndpoint.cs). +An example for a `FileContentResult` is available under [`Features/CRUD/FindBookCover/FindBookCoverEndpoint.cs`](./Features/CRUD/FindBookCover/FindBookCoverEndpoint.cs). ### Streams From 558e8efcde9fb7c19f8a6cddab03f1e55798b91b Mon Sep 17 00:00:00 2001 From: Tim Schneider <43130816+DerStimmler@users.noreply.github.com> Date: Tue, 25 Mar 2025 19:53:41 +0100 Subject: [PATCH 4/6] docs(readme): improve readme --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9bb1c88..3745de3 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,9 @@ Seamlessly map Results from [CSharpFunctionalExtensions](https://github.com/vkho 1. [Overview](#overview) 2. [Installation](#installation) 3. [Usage](#usage) - 1. [Default mapping](#default-mapping) - 2. [Custom error mapping](#custom-error-mapping) + 1. [Available methods](#available-methods) + 2. [Default mapping](#default-mapping) + 3. [Custom error mapping](#custom-error-mapping) 4. [Analyzers](#analyzers) 5. [Examples](#examples) 6. [Development](#development) @@ -28,12 +29,11 @@ This library provides convenient extension methods to seamlessly map Results fro - ⚙️ **Zero Configuration:** Get started immediately — the mapping works out of the box without any configuration. - 🛠️ **Customizable Mappings:** Tailor default mappings or define custom mappings for specific use cases. -- 🌐 **Localization Ready:** Supports custom error messages and status codes, easily adaptable to your localization needs. - 🔗 **Fluent API:** Maintain a smooth, railway-oriented flow by chaining HttpResult mappings at the end of your Result chain. - 🧱 **Separation of Domain and HTTP Errors:** Keeps domain errors distinct from HTTP errors, improving maintainability and clarity between business logic and web API concerns. - ⚡ **Minimal APIs & Controllers Support:** Works with both Minimal APIs and traditional controllers in ASP.NET. - 📦 **Full Support for ASP.NET Results:** Supports all built-in HTTP response types in ASP.NET, including `Ok`, `Created`, `NoContent`, `Accepted`, `FileStream`, and more. -- 🦺 **Typed Results:** Utilizes typed results for consistent, type-safe API responses. +- 🦺 **Typed Results:** Utilizes `TypedResults` for consistent, type-safe API responses. - 📑 **OpenAPI Ready:** Ensures accurate OpenAPI generation for clear and reliable API documentation. - 🛡️ **RFC Compliance:** Default mappings adhere to the RFC 9457 standard (`ProblemDetails`), ensuring your API errors are standardized and interoperable. - 🧑‍💻 **Developer-Friendly:** Includes built-in analyzers and source generators to speed up development and reduce errors. @@ -73,6 +73,8 @@ app.MapGet("/books", (BookService service) => ); ``` +### Available methods + These methods are available in sync and async variants: | Method | Short Description | From 0469e2ed86a717c850f7ce5f8184f59b89ca5210 Mon Sep 17 00:00:00 2001 From: Tim Schneider <43130816+DerStimmler@users.noreply.github.com> Date: Tue, 25 Mar 2025 19:58:42 +0100 Subject: [PATCH 5/6] docs(readme): collapse available methods --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3745de3..37bccaa 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,8 @@ app.MapGet("/books", (BookService service) => ### Available methods -These methods are available in sync and async variants: +
+ Click here to view all available methods. | Method | Short Description | |---------------------------------------|------------------------------------------------------------------------------| @@ -105,6 +106,9 @@ These methods are available in sync and async variants: | `.ToFileStreamHttpResult()` | Returns `FileStreamHttpResult` or custom error | | `.ToContentHttpResult()` | Returns `ContentHttpResult` or `ProblemHttpResult` | | `.ToContentHttpResult()` | Returns `ContentHttpResult` or custom error | +
+ +All methods are available in sync and async variants. ### Default mapping @@ -122,7 +126,7 @@ This default mapping behaviour is configured inside the [`ProblemDetailsMappingP You can override this behavior by providing your own dictionary that maps status codes to their corresponding `title` and `type` of the resulting `ProblemDetails` object.
-Example for changing the default mapping for german localization +Click here to see an example of changing the default mapping for German localization. ```csharp ProblemDetailsMappingProvider.DefaultMappings = new Dictionary From e1442246a51b5bec0f1d781d9adde18674cea297 Mon Sep 17 00:00:00 2001 From: Tim Schneider <43130816+DerStimmler@users.noreply.github.com> Date: Tue, 25 Mar 2025 20:45:25 +0100 Subject: [PATCH 6/6] docs(readme): use absolute github urls --- .github/workflows/check.yml | 2 +- README.md | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index e82016a..1b4bdbe 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -52,6 +52,6 @@ jobs: uses: lycheeverse/lychee-action@v2 with: fail: true - args: --include-fragments . + args: --remap '${{ github.event.repository.default_branch }} ${{ github.head_ref }}' --include-fragments . jobSummary: true format: markdown diff --git a/README.md b/README.md index 37bccaa..1d06895 100644 --- a/README.md +++ b/README.md @@ -228,13 +228,13 @@ You can find a complete list of all analyzers [here](https://github.com/co-IT/CS ## Examples -The [`CSharpFunctionalExtensions.HttpResults.Examples`](CSharpFunctionalExtensions.HttpResults.Examples) project contains various examples demonstrating how to use this library in different scenarios, including: +The [`CSharpFunctionalExtensions.HttpResults.Examples`](https://github.com/co-IT/CSharpFunctionalExtensions.HttpResults/blob/main/CSharpFunctionalExtensions.HttpResults.Examples) project contains various examples demonstrating how to use this library in different scenarios, including: -- **[Basic CRUD operations](CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD)** – Handling `GET`, `POST`, `PUT`, and `DELETE` requests -- **[File handling](CSharpFunctionalExtensions.HttpResults.Examples/Features/FileStream)** – Returning files from your Web API -- **[Custom error mapping](CSharpFunctionalExtensions.HttpResults.Examples/Features/CustomError)** – Defining and mapping custom error types to meaningful HTTP responses -- **[Multiple errors in chain](CSharpFunctionalExtensions.HttpResults.Examples/Features/MultipleErrorChain)** – Using different kind of custom errors in the same result chain -- **[Customizing default mapping](CSharpFunctionalExtensions.HttpResults.Examples/Program.cs)** – Overriding default mappings for localization or specific use cases +- **[Basic CRUD operations](https://github.com/co-IT/CSharpFunctionalExtensions.HttpResults/blob/main/CSharpFunctionalExtensions.HttpResults.Examples/Features/CRUD)** – Handling `GET`, `POST`, `PUT`, and `DELETE` requests +- **[File handling](https://github.com/co-IT/CSharpFunctionalExtensions.HttpResults/blob/main/CSharpFunctionalExtensions.HttpResults.Examples/Features/FileStream)** – Returning files from your Web API +- **[Custom error mapping](https://github.com/co-IT/CSharpFunctionalExtensions.HttpResults/blob/main/CSharpFunctionalExtensions.HttpResults.Examples/Features/CustomError)** – Defining and mapping custom error types to meaningful HTTP responses +- **[Multiple errors in chain](https://github.com/co-IT/CSharpFunctionalExtensions.HttpResults/blob/main/CSharpFunctionalExtensions.HttpResults.Examples/Features/MultipleErrorChain)** – Using different kind of custom errors in the same result chain +- **[Customizing default mapping](https://github.com/co-IT/CSharpFunctionalExtensions.HttpResults/blob/main/CSharpFunctionalExtensions.HttpResults.Examples/Program.cs)** – Overriding default mappings for localization or specific use cases Check out the example project for hands-on implementation details! @@ -257,4 +257,4 @@ To add new methods follow these steps: 2. Add methods for `Result` to `CSharpFunctionalExtensions.HttpResults.Generators.ResultExtensions` and add the class to `ResultExtensionsClassBuilder` 3. Add methods for `UnitResult` to `CSharpFunctionalExtensions.HttpResults.Generators.UnitResultExtensions` and add the class to `UnitResultExtensionsClassBuilder` 4. Add tests for **all** new methods to `CSharpFunctionalExtensions.HttpResults.Tests` -5. Add methods to [README](README.md) +5. Add methods to [README](https://github.com/co-IT/CSharpFunctionalExtensions.HttpResults/blob/main/README.md)