-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from marcominerva/develop
Add OperationResults for ASP.NET Core
- Loading branch information
Showing
12 changed files
with
365 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
name: Publish OperationResults for ASP.NET Core on NuGet | ||
|
||
on: | ||
push: | ||
branches: [ master ] | ||
paths: [ 'src/OperationResults.AspNetCore/**' ] | ||
workflow_dispatch: | ||
|
||
env: | ||
NET_VERSION: '6.x' | ||
PROJECT_NAME: src/OperationResults.AspNetCore | ||
PROJECT_FILE: OperationResults.AspNetCore.csproj | ||
|
||
jobs: | ||
build: | ||
name: Publish on NuGet | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@v3 | ||
with: | ||
fetch-depth: 0 # avoid shallow clone so nbgv can do its work. | ||
|
||
- name: Setup .NET SDK ${{ env.NET_VERSION }} | ||
uses: actions/setup-dotnet@v1 | ||
with: | ||
dotnet-version: ${{ env.NET_VERSION }} | ||
|
||
- name: Nerdbank.GitVersioning | ||
uses: dotnet/nbgv@v0.4 | ||
id: nbgv | ||
with: | ||
path: ${{ env.PROJECT_NAME }} | ||
|
||
- name: Package | ||
run: dotnet pack -c Release -o . '${{ env.PROJECT_NAME }}/${{ env.PROJECT_FILE }}' | ||
|
||
- name: Publish on NuGet | ||
run: dotnet nuget push *.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json | ||
|
||
- name: Create release | ||
uses: actions/create-release@v1 | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} | ||
with: | ||
tag_name: aspnetcore_v${{ steps.nbgv.outputs.NuGetPackageVersion }} | ||
release_name: ASP.NET Core Release v${{ steps.nbgv.outputs.NuGetPackageVersion }} | ||
draft: false | ||
prerelease: false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
127 changes: 127 additions & 0 deletions
127
src/OperationResults.AspNetCore/ControllerExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
using System.Diagnostics; | ||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.AspNetCore.Routing; | ||
using Microsoft.AspNetCore.WebUtilities; | ||
using Microsoft.Extensions.DependencyInjection; | ||
|
||
namespace OperationResults.AspNetCore; | ||
|
||
public static class ControllerExtensions | ||
{ | ||
public static IActionResult CreateResponse(this HttpContext httpContext, Result operationResult, int? responseStatusCode = null) | ||
{ | ||
if (operationResult.Success) | ||
{ | ||
return new StatusCodeResult(responseStatusCode.GetValueOrDefault(StatusCodes.Status204NoContent)); | ||
} | ||
|
||
return Problem(httpContext, FailureReasonToStatusCode(httpContext, operationResult.FailureReason), null, operationResult.ErrorMessage, operationResult.ErrorDetail, operationResult.ValidationErrors); | ||
} | ||
|
||
public static IActionResult CreateResponse<T>(this HttpContext httpContext, Result<T> operationResult, int? responseStatusCode = null) | ||
=> CreateResponse(httpContext, operationResult, null, null, responseStatusCode); | ||
|
||
public static IActionResult CreateResponse<T>(this HttpContext httpContext, Result<T> operationResult, string? actionName, object? routeValues = null, int? responseStatusCode = null) | ||
{ | ||
if (operationResult.Success) | ||
{ | ||
if (operationResult.Content is not null) | ||
{ | ||
if (!string.IsNullOrWhiteSpace(actionName)) | ||
{ | ||
var routeValueDictionary = new RouteValueDictionary(routeValues); | ||
//var apiVersion = HttpContext.GetRequestedApiVersion(); | ||
//if (!routeValueDictionary.ContainsKey("version") && apiVersion != null) | ||
//{ | ||
// routeValueDictionary.Add("version", apiVersion.ToString()); | ||
//} | ||
|
||
return new CreatedAtRouteResult(actionName, routeValueDictionary, operationResult.Content); | ||
} | ||
else if (operationResult.Content is StreamFileContent streamFileContent) | ||
{ | ||
var fileStreamResult = new FileStreamResult(streamFileContent.Content, streamFileContent.ContentType) | ||
{ | ||
FileDownloadName = streamFileContent.DownloadFileName | ||
}; | ||
|
||
return fileStreamResult; | ||
} | ||
else if (operationResult.Content is ByteArrayFileContent byteArrayFileContent) | ||
{ | ||
var fileContentResult = new FileContentResult(byteArrayFileContent.Content, byteArrayFileContent.ContentType) | ||
{ | ||
FileDownloadName = byteArrayFileContent.DownloadFileName | ||
}; | ||
|
||
return fileContentResult; | ||
} | ||
|
||
var okResult = new ObjectResult(operationResult.Content) | ||
{ | ||
StatusCode = responseStatusCode.GetValueOrDefault(StatusCodes.Status200OK) | ||
}; | ||
|
||
return okResult; | ||
} | ||
|
||
return new StatusCodeResult(responseStatusCode.GetValueOrDefault(StatusCodes.Status204NoContent)); | ||
} | ||
|
||
return Problem(httpContext, FailureReasonToStatusCode(httpContext, operationResult.FailureReason), operationResult.Content, operationResult.ErrorMessage, operationResult.ErrorDetail, operationResult.ValidationErrors); | ||
} | ||
|
||
private static IActionResult Problem(HttpContext httpContext, int statusCode, object? content = null, string? title = null, string? detail = null, IEnumerable<ValidationError>? validationErrors = null) | ||
{ | ||
if (content is not null) | ||
{ | ||
var objectResult = new ObjectResult(content) | ||
{ | ||
StatusCode = statusCode | ||
}; | ||
|
||
return objectResult; | ||
} | ||
|
||
var problemDetails = new ProblemDetails | ||
{ | ||
Status = statusCode, | ||
Type = $"https://httpstatuses.io/{statusCode}", | ||
Title = title ?? ReasonPhrases.GetReasonPhrase(statusCode), | ||
Detail = detail, | ||
Instance = httpContext.Request.Path | ||
}; | ||
|
||
problemDetails.Extensions.Add("traceId", Activity.Current?.Id ?? httpContext.TraceIdentifier); | ||
if (validationErrors?.Any() ?? false) | ||
{ | ||
var options = httpContext.RequestServices.GetRequiredService<OperationResultOptions>(); | ||
|
||
if (options.ErrorResponseFormat == ErrorResponseFormat.Default) | ||
{ | ||
var errors = validationErrors.GroupBy(v => v.Name).ToDictionary(k => k.Key, v => v.Select(e => e.Message)); | ||
problemDetails.Extensions.Add("errors", errors); | ||
} | ||
else | ||
{ | ||
problemDetails.Extensions.Add("errors", validationErrors); | ||
} | ||
} | ||
|
||
var problemDetailsResults = new ObjectResult(problemDetails) | ||
{ | ||
StatusCode = statusCode | ||
}; | ||
|
||
return problemDetailsResults; | ||
} | ||
|
||
private static int FailureReasonToStatusCode(HttpContext httpContext, int failureReason, int? defaultResponseStatusCode = null) | ||
{ | ||
var options = httpContext.RequestServices.GetRequiredService<OperationResultOptions>(); | ||
var statusCode = options.GetStatusCode(failureReason, defaultResponseStatusCode); | ||
|
||
return statusCode; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace OperationResults.AspNetCore; | ||
|
||
public enum ErrorResponseFormat | ||
{ | ||
Default, | ||
List | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
using Microsoft.AspNetCore.Http; | ||
|
||
namespace OperationResults.AspNetCore; | ||
|
||
public class OperationResultOptions | ||
{ | ||
public ErrorResponseFormat ErrorResponseFormat { get; set; } | ||
|
||
public Dictionary<int, int> StatusCodesMapping { get; set; } | ||
|
||
public OperationResultOptions() | ||
{ | ||
StatusCodesMapping = new Dictionary<int, int> | ||
{ | ||
[FailureReasons.None] = StatusCodes.Status200OK, | ||
[FailureReasons.ItemNotFound] = StatusCodes.Status404NotFound, | ||
[FailureReasons.Forbidden] = StatusCodes.Status403Forbidden, | ||
[FailureReasons.DatabaseError] = StatusCodes.Status500InternalServerError, | ||
[FailureReasons.ClientError] = StatusCodes.Status400BadRequest, | ||
[FailureReasons.InvalidFile] = StatusCodes.Status415UnsupportedMediaType, | ||
[FailureReasons.Conflict] = StatusCodes.Status409Conflict, | ||
[FailureReasons.Unauthorized] = StatusCodes.Status401Unauthorized, | ||
[FailureReasons.GenericError] = StatusCodes.Status500InternalServerError | ||
}; | ||
} | ||
|
||
internal int GetStatusCode(int failureReason, int? defaultStatusCode = null) | ||
{ | ||
if (!StatusCodesMapping.TryGetValue(failureReason, out var statusCode)) | ||
{ | ||
statusCode = defaultStatusCode.GetValueOrDefault(StatusCodes.Status501NotImplemented); | ||
} | ||
|
||
return statusCode; | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
src/OperationResults.AspNetCore/OperationResults.AspNetCore.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net6.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
<Authors>Marco Minerva</Authors> | ||
<Company>Marco Minerva</Company> | ||
<Product>OperationResults.AspNetCore</Product> | ||
<Title>OperationResults.AspNetCore</Title> | ||
<Description>A lightweight library to totally decouple operation results and ASP.NET Core responses.</Description> | ||
<PackageId>OperationResultTools.AspNetCore</PackageId> | ||
<PackageLicenseExpression>MIT</PackageLicenseExpression> | ||
<PackageProjectUrl>https://github.com/marcominerva/OperationResults</PackageProjectUrl> | ||
<PackageIcon>Toolbox.png</PackageIcon> | ||
<PackageTags>csharp visual-studio net aspnetcore desktop web mobile utilities helpers</PackageTags> | ||
<RepositoryType>git</RepositoryType> | ||
<RepositoryUrl>https://github.com/marcominerva/OperationResults.git</RepositoryUrl> | ||
<RepositoryBranch>master</RepositoryBranch> | ||
<PackageReadmeFile>README.md</PackageReadmeFile> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<FrameworkReference Include="Microsoft.AspNetCore.App" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Nerdbank.GitVersioning" Version="3.5.109"> | ||
<PrivateAssets>all</PrivateAssets> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
</PackageReference> | ||
<PackageReference Include="OperationResultTools" Version="1.0.2" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<None Include="..\..\Toolbox.png"> | ||
<Pack>True</Pack> | ||
<PackagePath></PackagePath> | ||
</None> | ||
<None Include="..\..\README.md" Pack="true" PackagePath="\" /> | ||
</ItemGroup> | ||
</Project> |
Oops, something went wrong.