Giter Site home page Giter Site logo

mrdave1999 / simpleresults Goto Github PK

View Code? Open in Web Editor NEW
101.0 3.0 1.0 858 KB

A simple library to implement the Result pattern for returning from services

Home Page: https://mrdave1999.github.io/SimpleResults

License: MIT License

C# 100.00%
csharp dotnet error-handling result pattern aspnetcore fluent-validation

simpleresults's People

Contributors

mrdave1999 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

simpleresults's Issues

Incorrect status is obtained when deserializing the result object

Problem reproduction

public class Program
{
    public static void Main()
    {
        var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
        var json =
            """
            {
              "status": "Ok",
              "success": true,
              "message": "Operation successfully executed",
              "errors": []
            }
            """;

        Result result = JsonSerializer.Deserialize<Result>(json, options);
        Console.WriteLine(result.Status == ResultStatus.Ok); // Output: False
        Console.WriteLine(result.Status); // Output: Failure
    }
}
  • The expected behavior should be True.
  • The result status should not be Failure. Should be Ok in this case.

Add an action filter to translate the `Result` object to `ActionResult`

Example

[TranslateResultToActionResult]
[ApiController]
[Route("[controller]")]
public class PersonController : ControllerBase
{
    private readonly PersonService _personService;
    public PersonController(PersonService personService) => _personService = personService;

    [HttpPost]
    public Result Create([FromBody]Person person)
        => _personService.Create(person);

    [HttpGet]
    public ListedResult<Person> Get() 
        => _personService.GetAll();
}

TranslateResultToActionResultAttribute inherits from ActionFilterAttribute.
See https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-7.0#action-filters

Add extension methods to the ModelStateDictionary type

These extension methods will be added to the ModelStateDictionary type:

public static class SRModelStateDictionaryExtensions
{
    public static bool IsFailed(this ModelStateDictionary modelState);
    public static Result Invalid(this ModelStateDictionary modelState);
    public static Result Invalid(this ModelStateDictionary modelState, string message);
    public static BadRequestObjectResult BadRequest(this ModelStateDictionary modelState);
    public static BadRequestObjectResult BadRequest(this ModelStateDictionary modelState, string message);
}

SRModelStateDictionaryExtensions contains methods that convert an instance of ModelStateDictionary into an instance of type Result.
The SR "prefix" is so as not to cause name conflicts.

Usage

[Route("api/[controller]")]
public class PersonController : ControllerBase
{
    [HttpPost]
    public ActionResult<Result<CreatedId>> Create([FromBody]Person person)
    {
        if (ModelState.IsFailed())
            return ModelState.BadRequest();

        // Some code..
    }
}

Add support for Fluent Validation

These methods can be distributed in a NuGet package:

public static class SRFluentValidationResultExtensions
{
    public static bool IsFailed(this ValidationResult result) => !result.IsValid;
    public static IEnumerable<string> AsErrors(this ValidationResult result)
        => result.Errors.Select(failure => failure.ErrorMessage);

   public static Result Invalid(this ValidationResult result) 
        => Result.Invalid(result.AsErrors());

   public static Result Invalid(this ValidationResult result, string message) 
        => Result.Invalid(message, result.AsErrors());
}

The SR "prefix" is so as not to cause name conflicts.

PD: The package was published here: https://www.nuget.org/packages/SimpleResults.FluentValidation

Add methods to execute explicit conversions

Methods to be added in the Result class to perform the conversion from Result to:

public Result<T> ToResult<T>(T data);
public ListedResult<T> ToListedResult<T>(IEnumerable<T> data);
public PagedResult<T> ToPagedResult<T>(IEnumerable<T> data, PagedInfo pagedInfo);

These methods can go in a file named Result.Conversions.cs.
Note: Implicit operators can call these methods.

Usage

var person = new Person { Name = "Bob" };
Result invalidResult = Result.Invalid();
Result<Person> result = invalidResult.ToResult(person);

PD: These methods have been added.

Overload to allow Exceptions

More of a question, than a request.
What would be your opinion on adding an overload on the ErrorResult to accept to Exception (and maybe even a String,Exception)

Something like this:

try
{
    var state = await domainEventHandler.Handle(domainEvent, domainEventMetaData,cancellationToken);
    return new Result.Success(state);
}
catch(Exception e)
{
    return new ErrorResult(e);
}

Add an endpoint filter to translate the Result object to IResult

The filter only converts the Result object to an implementation of Microsoft.AspNetCore.Http.IResult.

Example

public static class PersonEndpoint
{
    public static void AddRoutes(this WebApplication app)
    {
        var personGroup = app
            .MapGroup("/Person")
            .WithTags("Person")
            .AddEndpointFilter<TranslateResultToHttpResultFilter>();

        personGroup
            .MapPost("/", ([FromBody]Person person, PersonService service) => service.Create(person))
            .Produces<Result>();

        personGroup
            .MapGet("/", (PersonService service) => service.GetAll())
            .Produces<ListedResult<Person>>();
    }
}

TranslateResultToHttpResultFilter must implement the IEndpointFilter interface.

The detail to take into consideration is that IEndpointFilter is only available as of ASP.NET Core 7.0. This means that the custom filter should be compiled for specific frameworks such as .NET 7, 8 or higher.

As the SimpleResults.AspNetCore project has multiple target frameworks, the preprocessor symbols could be used as NET7_0_OR_GREATER. See https://learn.microsoft.com/en-us/dotnet/core/tutorials/libraries#preprocessor-symbols

Change error message format when validation fails from Fluent Validation

Currently the SRFluentValidationResultExtensions.AsErrors method will return an error message like this:

'Name' must not be empty.
'First Name' must not be empty.

It would be good to change it to a more specific format like this one:

'Name' property failed validation. Error was: 'Name' must not be empty.

The template would look like this in English:

'{0}' property failed validation. Error was: {1}

And in Spanish:

Propiedad '{0}' falló en la validación. El error fue: {1}

Add a result operation to represent the file contents

New types to be added:

public class StreamFileContent
{
    public StreamFileContent(Stream content) => Content = content;
    public Stream Content { get; }
    public string ContentType { get; init; } = string.Empty;
    public string FileName { get; init; } = string.Empty;
}

public class ByteArrayFileContent
{
    public ByteArrayFileContent(byte[] content) => Content = content;
    public byte[] Content { get; }
    public string ContentType { get; init; } = string.Empty;
    public string FileName { get; init; } = string.Empty;
}

These types represent the content of a file.

Two methods are added to the Result type:

public static Result<StreamFileContent> File(StreamFileContent fileContent);
public static Result<ByteArrayFileContent> File(ByteArrayFileContent fileContent);

Usage

public class DownloadProformaInvoiceUseCase(
    IHtmlTemplateLoader htmlTemplateLoader,
    IHtmlConverter htmlConverter,
    DownloadProformaInvoiceValidator validator)
{
    public async Task<Result<ByteArrayFileContent>> DownloadAsPdfAsync(DownloadProformaInvoiceRequest request)
    {
        ValidationResult result = validator.Validate(request);
        if (result.IsFailed())
            return Result.Invalid(result.AsErrors());

        var html = await htmlTemplateLoader
            .LoadAsync("./Templates/ProformaInvoice.html", request.MapToObject());

        byte[] fileContents = htmlConverter.ConvertToPdf(html, new MemoryStream());
        var byteArrayFileContent = new ByteArrayFileContent(fileContents)
        {
            ContentType = "application/pdf",
            FileName = "Report.pdf"
        };
        return Result.File(byteArrayFileContent);
    }
}

If the result is successful, Result<ByteArrayFileContent> should be translated to an object like FileContentResult or FileStreamResult (these are types used in MVC controllers).

Support should also be added for Minimal APIs. See, FileContentHttpResult and FileStreamHttpResult.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.