mrdave1999 / simpleresults Goto Github PK
View Code? Open in Web Editor NEWA simple library to implement the Result pattern for returning from services
Home Page: https://mrdave1999.github.io/SimpleResults
License: MIT License
A simple library to implement the Result pattern for returning from services
Home Page: https://mrdave1999.github.io/SimpleResults
License: MIT License
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
}
}
Comments should be added to indicate which properties do not represent a null value by default or vice versa.
[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
var data = new List<string>();
Result<List<string>> result = Result.Success(data);
// Implicit conversion.
List<string> value = result;
PR #50 only works if you use in Minimal API but in controller actions it does not work.
The version of the SimpleResults.AspNetCore package must be incremented.
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.
[Route("api/[controller]")]
public class PersonController : ControllerBase
{
[HttpPost]
public ActionResult<Result<CreatedId>> Create([FromBody]Person person)
{
if (ModelState.IsFailed())
return ModelState.BadRequest();
// Some code..
}
}
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
API documentation is published here: https://mrdave1999.github.io/SimpleResults/api/SimpleResults.html
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.
var person = new Person { Name = "Bob" };
Result invalidResult = Result.Invalid();
Result<Person> result = invalidResult.ToResult(person);
PD: These methods have been added.
var value = new List<string>();
// Implicit conversion.
Result<List<string>> result = value;
The result types must be translated into an implementation of Microsoft.AspNetCore.Http.IResult.
It also adds support for Minimal APIs.
Result Failure(IEnumerable<string> errors);
Result Invalid(IEnumerable<string> errors);
Result NotFound(IEnumerable<string> errors);
Result Unauthorized(IEnumerable<string> errors);
Result Conflict(IEnumerable<string> errors);
Result CriticalError(IEnumerable<string> errors);
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);
}
The filter only converts the Result object to an implementation of Microsoft.AspNetCore.Http.IResult.
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
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}
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);
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.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.