diff --git a/sample/Ardalis.Result.Sample.Core/Services/WeatherService.cs b/sample/Ardalis.Result.Sample.Core/Services/WeatherService.cs index 3199f29..f4f13c9 100644 --- a/sample/Ardalis.Result.Sample.Core/Services/WeatherService.cs +++ b/sample/Ardalis.Result.Sample.Core/Services/WeatherService.cs @@ -56,6 +56,19 @@ public Result> GetForecast(ForecastRequestDto model ErrorMessage = "PostalCode is required" } }); } + + // Test value to return Created result with a location + if (model.PostalCode == "12345") + { + return Result>.Created( + Enumerable.Range(1, 1).Select(index => + new WeatherForecast + { + Date = DateTime.Now, + TemperatureC = 0, + Summary = Summaries[0] + }), "weatherforecast/12345"); + } // test value if (model.PostalCode == "55555") diff --git a/src/Ardalis.Result.AspNetCore/ActionResultExtensions.cs b/src/Ardalis.Result.AspNetCore/ActionResultExtensions.cs index 24b7121..35acaab 100644 --- a/src/Ardalis.Result.AspNetCore/ActionResultExtensions.cs +++ b/src/Ardalis.Result.AspNetCore/ActionResultExtensions.cs @@ -74,6 +74,17 @@ internal static ActionResult ToActionResult(this ControllerBase controller, IRes return typeof(Result).IsInstanceOfType(result) ? (ActionResult)controller.StatusCode(statusCode) : controller.StatusCode(statusCode, result.GetValue()); + case ResultStatus.Created: + if(string.IsNullOrEmpty(result.Location)) + return controller.Created((string?)null, result.GetValue()); + + var httpRequest = controller.HttpContext.Request; + var locationUri = new UriBuilder(httpRequest.Scheme, + httpRequest.Host.Host, + httpRequest.Host.Port ?? -1, + result.Location).Uri.AbsoluteUri; + + return controller.Created(locationUri, result.GetValue()); default: return resultStatusOptions.ResponseType == null ? (ActionResult)controller.StatusCode(statusCode) diff --git a/src/Ardalis.Result.AspNetCore/ResultConvention.cs b/src/Ardalis.Result.AspNetCore/ResultConvention.cs index 9bd2e75..c6dc81d 100644 --- a/src/Ardalis.Result.AspNetCore/ResultConvention.cs +++ b/src/Ardalis.Result.AspNetCore/ResultConvention.cs @@ -81,7 +81,8 @@ So we should avoid pairing 'void' Result with OK or Created var resultStatuses = attr?.ResultStatuses ?? _map.Keys; - foreach (var status in resultStatuses.Where(s => s != ResultStatus.Ok)) + foreach (var status in resultStatuses.Where(s => + s is not (ResultStatus.Ok or ResultStatus.Created))) { var info = _map[status]; AddProducesResponseTypeAttribute(action.Filters, (int)info.GetStatusCode(method), info.ResponseType); diff --git a/src/Ardalis.Result.AspNetCore/ResultStatusMap.cs b/src/Ardalis.Result.AspNetCore/ResultStatusMap.cs index ab7c7fd..99d8754 100644 --- a/src/Ardalis.Result.AspNetCore/ResultStatusMap.cs +++ b/src/Ardalis.Result.AspNetCore/ResultStatusMap.cs @@ -26,6 +26,7 @@ internal ResultStatusMap() public ResultStatusMap AddDefaultMap() { return For(ResultStatus.Ok, HttpStatusCode.OK) + .For(ResultStatus.Created, HttpStatusCode.Created) .For(ResultStatus.Error, (HttpStatusCode)422, resultStatusOptions => resultStatusOptions .With(UnprocessableEntity)) .For(ResultStatus.Forbidden, HttpStatusCode.Forbidden) diff --git a/src/Ardalis.Result/IResult.cs b/src/Ardalis.Result/IResult.cs index 0f7026f..67a73b1 100644 --- a/src/Ardalis.Result/IResult.cs +++ b/src/Ardalis.Result/IResult.cs @@ -10,5 +10,6 @@ public interface IResult IEnumerable ValidationErrors { get; } Type ValueType { get; } object GetValue(); + string Location { get; } } } diff --git a/tests/Ardalis.Result.UnitTests/ResultConstructor.cs b/tests/Ardalis.Result.UnitTests/ResultConstructor.cs index 44deaf6..c509050 100644 --- a/tests/Ardalis.Result.UnitTests/ResultConstructor.cs +++ b/tests/Ardalis.Result.UnitTests/ResultConstructor.cs @@ -345,4 +345,20 @@ public void InitializesStatusToNoContentForNoContentFactoryCall() Assert.True(result.IsSuccess); } + + [Fact] + public void InitializedIsSuccessTrueForCreatedFactoryCall() + { + var result = Result.Created(new object()); + + Assert.True(result.IsSuccess); + } + + [Fact] + public void InitializedIsSuccessTrueForCreatedWithLocationFactoryCall() + { + var result = Result.Created(new object(), "sample/endpoint"); + + Assert.True(result.IsSuccess); + } }