diff --git a/sample/Ardalis.Result.Sample.Core/Ardalis.Result.Sample.Core.csproj b/sample/Ardalis.Result.Sample.Core/Ardalis.Result.Sample.Core.csproj
index f6040f7..17deb8e 100644
--- a/sample/Ardalis.Result.Sample.Core/Ardalis.Result.Sample.Core.csproj
+++ b/sample/Ardalis.Result.Sample.Core/Ardalis.Result.Sample.Core.csproj
@@ -1,19 +1,18 @@
-
- Ardalis.Result.Sample.Core
- Ardalis.Result.Sample.Core
-
+
+ Ardalis.Result.Sample.Core
+ Ardalis.Result.Sample.Core
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/sample/Ardalis.Result.Sample.Core/DTOs/CreatePersonRequestDto.cs b/sample/Ardalis.Result.Sample.Core/DTOs/CreatePersonRequestDto.cs
index 8804340..5785c16 100644
--- a/sample/Ardalis.Result.Sample.Core/DTOs/CreatePersonRequestDto.cs
+++ b/sample/Ardalis.Result.Sample.Core/DTOs/CreatePersonRequestDto.cs
@@ -1,7 +1,9 @@
-namespace Ardalis.Result.Sample.Core.DTOs;
+using System;
+
+namespace Ardalis.Result.Sample.Core.DTOs;
public class CreatePersonRequestDto
{
- public string FirstName { get; set; }
- public string LastName { get; set; }
+ public string FirstName { get; set; } = String.Empty;
+ public string LastName { get; set; } = String.Empty;
}
\ No newline at end of file
diff --git a/sample/Ardalis.Result.Sample.Core/DTOs/ForecastRequestDto.cs b/sample/Ardalis.Result.Sample.Core/DTOs/ForecastRequestDto.cs
index 36fa2f7..56c8390 100644
--- a/sample/Ardalis.Result.Sample.Core/DTOs/ForecastRequestDto.cs
+++ b/sample/Ardalis.Result.Sample.Core/DTOs/ForecastRequestDto.cs
@@ -1,4 +1,5 @@
+using System;
using System.ComponentModel.DataAnnotations;
namespace Ardalis.Result.Sample.Core.DTOs
@@ -6,6 +7,6 @@ namespace Ardalis.Result.Sample.Core.DTOs
public class ForecastRequestDto
{
[Required]
- public string PostalCode { get; set; }
+ public string PostalCode { get; set; } = String.Empty;
}
}
\ No newline at end of file
diff --git a/sample/Ardalis.Result.Sample.Core/Exceptions/ForecastRequestInvalidException.cs b/sample/Ardalis.Result.Sample.Core/Exceptions/ForecastRequestInvalidException.cs
index 0d0ab1b..a85700e 100644
--- a/sample/Ardalis.Result.Sample.Core/Exceptions/ForecastRequestInvalidException.cs
+++ b/sample/Ardalis.Result.Sample.Core/Exceptions/ForecastRequestInvalidException.cs
@@ -5,7 +5,7 @@ namespace Ardalis.Result.Sample.Core.Exceptions
{
public class ForecastRequestInvalidException : Exception
{
- public Dictionary ValidationErrors { get; set; }
+ public Dictionary ValidationErrors { get; set; } = new();
public ForecastRequestInvalidException(Dictionary validationErrors) : base("Forecast request is invalid.")
{
diff --git a/sample/Ardalis.Result.Sample.Core/Model/Person.cs b/sample/Ardalis.Result.Sample.Core/Model/Person.cs
index ddacba6..95ad164 100644
--- a/sample/Ardalis.Result.Sample.Core/Model/Person.cs
+++ b/sample/Ardalis.Result.Sample.Core/Model/Person.cs
@@ -6,10 +6,10 @@ namespace Ardalis.Result.Sample.Core.Model
public class Person
{
public int Id { get; set; }
- public string Surname { get; set; }
- public string Forename { get; set; }
+ public string Surname { get; set; } = String.Empty;
+ public string Forename { get; set; } = String.Empty;
- public List Children { get; set; }
+ public List Children { get; set; } = new();
public DateTime DateOfBirth { get; set; }
}
diff --git a/sample/Ardalis.Result.Sample.Core/Model/WeatherForecast.cs b/sample/Ardalis.Result.Sample.Core/Model/WeatherForecast.cs
index 6f38dd2..53471d2 100644
--- a/sample/Ardalis.Result.Sample.Core/Model/WeatherForecast.cs
+++ b/sample/Ardalis.Result.Sample.Core/Model/WeatherForecast.cs
@@ -10,6 +10,6 @@ public class WeatherForecast
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
- public string Summary { get; set; }
+ public string Summary { get; set; } = String.Empty;
}
}
diff --git a/sample/Ardalis.Result.SampleMinimalApi.FunctionalTests/NewWeatherForecast.cs b/sample/Ardalis.Result.SampleMinimalApi.FunctionalTests/NewWeatherForecast.cs
index 2d2485f..1bc2bb1 100644
--- a/sample/Ardalis.Result.SampleMinimalApi.FunctionalTests/NewWeatherForecast.cs
+++ b/sample/Ardalis.Result.SampleMinimalApi.FunctionalTests/NewWeatherForecast.cs
@@ -33,7 +33,7 @@ public async Task ReturnsOkWithValueGivenValidPostalCode()
var stringResponse = await response.Content.ReadAsStringAsync();
var forecasts = JsonConvert.DeserializeObject>(stringResponse);
- Assert.Equal("Freezing", forecasts.First().Summary);
+ Assert.Equal("Freezing", forecasts?.First()?.Summary);
}
[Fact]
diff --git a/sample/Ardalis.Result.SampleWeb.FunctionalTests/WeatherForecastControllerPost.cs b/sample/Ardalis.Result.SampleWeb.FunctionalTests/WeatherForecastControllerPost.cs
index 8a2fddc..f7afe45 100644
--- a/sample/Ardalis.Result.SampleWeb.FunctionalTests/WeatherForecastControllerPost.cs
+++ b/sample/Ardalis.Result.SampleWeb.FunctionalTests/WeatherForecastControllerPost.cs
@@ -13,6 +13,30 @@
namespace Ardalis.Result.SampleWeb.FunctionalTests;
+public class WeatherForecastControllerThrows : IClassFixture>
+{
+ private const string CONTROLLER_THROWS_ROUTE = "/weatherforecast/throws";
+ private readonly HttpClient _client;
+
+ public WeatherForecastControllerThrows(WebApplicationFactory factory)
+ {
+ _client = factory.CreateClient();
+ }
+
+ [Fact]
+ public async Task Returns400BadRequestNot500()
+ {
+ var response = await _client.GetAsync(CONTROLLER_THROWS_ROUTE);
+
+ Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
+ var stringResponse = await response.Content.ReadAsStringAsync();
+ var problemDetails = JsonConvert.DeserializeObject(stringResponse);
+
+ Assert.Equal("One or more validation errors occurred.", problemDetails?.Title);
+ Assert.Equal(400, problemDetails.Status);
+ }
+}
+
public class WeatherForecastControllerPost : IClassFixture>
{
private const string CONTROLLER_POST_ROUTE = "/weatherforecast/create";
@@ -36,7 +60,7 @@ public async Task ReturnsOkWithValueGivenValidPostalCode(string route)
var stringResponse = await response.Content.ReadAsStringAsync();
var forecasts = JsonConvert.DeserializeObject>(stringResponse);
- Assert.Equal("Freezing", forecasts.First().Summary);
+ Assert.Equal("Freezing", forecasts?.First()?.Summary);
}
[Theory]
diff --git a/sample/Ardalis.Result.SampleWeb/MediatrApi/WeatherForecastController.cs b/sample/Ardalis.Result.SampleWeb/MediatrApi/WeatherForecastController.cs
index 557bdac..6add08d 100644
--- a/sample/Ardalis.Result.SampleWeb/MediatrApi/WeatherForecastController.cs
+++ b/sample/Ardalis.Result.SampleWeb/MediatrApi/WeatherForecastController.cs
@@ -46,7 +46,7 @@ public Task>> CreateForecast([FromBody] NewF
public class NewForecastCommand : IRequest>>
{
[Required]
- public string PostalCode { get; set; }
+ public string PostalCode { get; set; } = String.Empty;
}
public class NewForecastHandler : IRequestHandler>>
diff --git a/sample/Ardalis.Result.SampleWeb/Pages/Index.cshtml.cs b/sample/Ardalis.Result.SampleWeb/Pages/Index.cshtml.cs
index a1c8267..b70f852 100644
--- a/sample/Ardalis.Result.SampleWeb/Pages/Index.cshtml.cs
+++ b/sample/Ardalis.Result.SampleWeb/Pages/Index.cshtml.cs
@@ -12,7 +12,7 @@ public IndexModel(IStringLocalizer stringLocalizer)
_stringLocalizer = stringLocalizer;
}
- public string Message { get; set; }
+ public string Message { get; set; } = String.Empty;
public void OnGet()
{
Message = _stringLocalizer["message"].Value;
diff --git a/sample/Ardalis.Result.SampleWeb/WeatherForecastFeature/WeatherForecastController.cs b/sample/Ardalis.Result.SampleWeb/WeatherForecastFeature/WeatherForecastController.cs
index 5626366..825dbf0 100644
--- a/sample/Ardalis.Result.SampleWeb/WeatherForecastFeature/WeatherForecastController.cs
+++ b/sample/Ardalis.Result.SampleWeb/WeatherForecastFeature/WeatherForecastController.cs
@@ -50,4 +50,15 @@ public ActionResult CreateSummaryForecast([FromBody]
// .Map(WeatherForecastSummaryDto.MapFrom)
.ToActionResult(this);
}
+
+ ///
+ /// Issue #179
+ ///
+ ///
+ [HttpGet("throws")]
+ [TranslateResultToActionResult]
+ public Result Throws()
+ {
+ return Result.Invalid(new ValidationError("foo"));
+ }
}
diff --git a/sample/Directory.Build.props b/sample/Directory.Build.props
index f02b7f8..b091742 100644
--- a/sample/Directory.Build.props
+++ b/sample/Directory.Build.props
@@ -1,7 +1,7 @@
net6.0;net7.0;net8.0
- net4.7.1;$(NetCoreFrameworks)
+ net48;$(NetCoreFrameworks)
enable
latest
diff --git a/src/Ardalis.Result.AspNetCore/ResultStatusMap.cs b/src/Ardalis.Result.AspNetCore/ResultStatusMap.cs
index 27d389a..ff492bc 100644
--- a/src/Ardalis.Result.AspNetCore/ResultStatusMap.cs
+++ b/src/Ardalis.Result.AspNetCore/ResultStatusMap.cs
@@ -102,7 +102,11 @@ private static ValidationProblemDetails BadRequest(ControllerBase controller, IR
{
foreach (var error in result.ValidationErrors)
{
- controller.ModelState.AddModelError(error.Identifier, error.ErrorMessage);
+ // TODO: mark ValidationError.Identifier as required and limit setting (see #179)
+ string identifier = error.Identifier ?? "(identifier)";
+ // TODO: mark ValidationError.Identifier as required and limit setting (see #179)
+ string errorMessage = error.ErrorMessage ?? "(error message)";
+ controller.ModelState.AddModelError(identifier, errorMessage);
}
return new ValidationProblemDetails(controller.ModelState);
diff --git a/src/Ardalis.Result/ValidationError.cs b/src/Ardalis.Result/ValidationError.cs
index 0a63e90..63485b7 100644
--- a/src/Ardalis.Result/ValidationError.cs
+++ b/src/Ardalis.Result/ValidationError.cs
@@ -20,6 +20,7 @@ public ValidationError(string identifier, string errorMessage, string errorCode,
}
public string Identifier { get; set; }
+ // TODO: Mark required and limit setting (see #179)
public string ErrorMessage { get; set; }
public string ErrorCode { get; set; }
public ValidationSeverity Severity { get; set; } = ValidationSeverity.Error;
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 948d110..9617ffa 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -1,7 +1,7 @@
net6.0;net7.0;net8.0
- netstandard2.0
+ netstandard2.0;$(NetCoreFrameworks)
latest
true
Steve Smith (@ardalis); Shady Nagy (@ShadyNagy)
diff --git a/tests/Ardalis.Result.AspNetCore.UnitTests/BaseResultConventionTest.cs b/tests/Ardalis.Result.AspNetCore.UnitTests/BaseResultConventionTest.cs
index 5a57b9a..4a04008 100644
--- a/tests/Ardalis.Result.AspNetCore.UnitTests/BaseResultConventionTest.cs
+++ b/tests/Ardalis.Result.AspNetCore.UnitTests/BaseResultConventionTest.cs
@@ -4,7 +4,6 @@
using System.Reflection;
namespace Ardalis.Result.AspNetCore.UnitTests;
-
public class BaseResultConventionTest
{
protected bool ProducesResponseTypeAttribute(IFilterMetadata filterMetadata, int statusCode, Type type)
diff --git a/tests/Ardalis.Result.AspNetCore.UnitTests/ResultConventionDefaultResultStatusMap.cs b/tests/Ardalis.Result.AspNetCore.UnitTests/ResultConventionDefaultResultStatusMap.cs
index dfc847d..02bc746 100644
--- a/tests/Ardalis.Result.AspNetCore.UnitTests/ResultConventionDefaultResultStatusMap.cs
+++ b/tests/Ardalis.Result.AspNetCore.UnitTests/ResultConventionDefaultResultStatusMap.cs
@@ -110,9 +110,10 @@ public void ResultWithValue(string actionName, Type expectedType)
convention.Apply(actionModel);
- Assert.Equal(9, actionModel.Filters.Where(f => f is ProducesResponseTypeAttribute).Count());
+ Assert.Equal(10, actionModel.Filters.Where(f => f is ProducesResponseTypeAttribute).Count());
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 200, expectedType));
+ Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 204, typeof(void)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 404, typeof(ProblemDetails)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 400, typeof(ValidationProblemDetails)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 401, typeof(void)));
diff --git a/tests/Ardalis.Result.AspNetCore.UnitTests/ResultConventionDefaultResultStatusMapModified.cs b/tests/Ardalis.Result.AspNetCore.UnitTests/ResultConventionDefaultResultStatusMapModified.cs
index c81376e..9e27d22 100644
--- a/tests/Ardalis.Result.AspNetCore.UnitTests/ResultConventionDefaultResultStatusMapModified.cs
+++ b/tests/Ardalis.Result.AspNetCore.UnitTests/ResultConventionDefaultResultStatusMapModified.cs
@@ -62,12 +62,8 @@ public void ChangeResultStatus()
[InlineData("Index", typeof(void), typeof(HttpPostAttribute), 204)]
[InlineData("Index", typeof(void), typeof(HttpDeleteAttribute), 204)]
[InlineData("Index", typeof(void), typeof(HttpGetAttribute), 204)]
- [InlineData(nameof(TestController.ResultString), typeof(string), typeof(HttpPostAttribute), 201)]
[InlineData(nameof(TestController.ResultString), typeof(string), typeof(HttpDeleteAttribute), 204)]
- [InlineData(nameof(TestController.ResultString), typeof(string), typeof(HttpGetAttribute), 200)]
- [InlineData(nameof(TestController.ResultEnumerableString), typeof(IEnumerable), typeof(HttpPostAttribute), 201)]
[InlineData(nameof(TestController.ResultEnumerableString), typeof(IEnumerable), typeof(HttpDeleteAttribute), 204)]
- [InlineData(nameof(TestController.ResultEnumerableString), typeof(IEnumerable), typeof(HttpGetAttribute), 200)]
public void ChangeResultStatus_ForSpecificMethods(string actionName, Type expectedType, Type attributeType, int expectedStatusCode)
{
var convention = new ResultConvention(new ResultStatusMap()
@@ -96,4 +92,39 @@ public void ChangeResultStatus_ForSpecificMethods(string actionName, Type expect
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 500, typeof(ProblemDetails)));
Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 503, typeof(ProblemDetails)));
}
+
+ [Theory]
+ [InlineData(nameof(TestController.ResultString), typeof(string), typeof(HttpPostAttribute), 201)]
+ [InlineData(nameof(TestController.ResultString), typeof(string), typeof(HttpGetAttribute), 200)]
+ [InlineData(nameof(TestController.ResultEnumerableString), typeof(IEnumerable), typeof(HttpPostAttribute), 201)]
+ [InlineData(nameof(TestController.ResultEnumerableString), typeof(IEnumerable), typeof(HttpGetAttribute), 200)]
+ public void ChangeResultStatus_ForDeleteMethods(string actionName, Type expectedType, Type attributeType, int expectedStatusCode)
+ {
+ var convention = new ResultConvention(new ResultStatusMap()
+ .AddDefaultMap()
+ .For(ResultStatus.Ok, HttpStatusCode.OK, opts => opts
+ .For("POST", HttpStatusCode.Created)
+ .For("DELETE", HttpStatusCode.NoContent)));
+
+ var actionModelBuilder = new ActionModelBuilder()
+ .AddActionFilter(new TranslateResultToActionResultAttribute())
+ .AddActionAttribute((Attribute)Activator.CreateInstance(attributeType)!);
+
+ var actionModel = actionModelBuilder.GetActionModel(actionName);
+
+ convention.Apply(actionModel);
+
+ Assert.Equal(10, actionModel.Filters.Where(f => f is ProducesResponseTypeAttribute).Count());
+
+ Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, expectedStatusCode, expectedType));
+ Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 204, typeof(void)));
+ Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 404, typeof(ProblemDetails)));
+ Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 400, typeof(ValidationProblemDetails)));
+ Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 401, typeof(void)));
+ Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 403, typeof(void)));
+ Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 409, typeof(ProblemDetails)));
+ Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 422, typeof(ProblemDetails)));
+ Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 500, typeof(ProblemDetails)));
+ Assert.Contains(actionModel.Filters, f => ProducesResponseTypeAttribute(f, 503, typeof(ProblemDetails)));
+ }
}
diff --git a/tests/Ardalis.Result.AspNetCore.UnitTests/ResultStatusMapAddDefaultMap.cs b/tests/Ardalis.Result.AspNetCore.UnitTests/ResultStatusMapAddDefaultMap.cs
new file mode 100644
index 0000000..4449906
--- /dev/null
+++ b/tests/Ardalis.Result.AspNetCore.UnitTests/ResultStatusMapAddDefaultMap.cs
@@ -0,0 +1,37 @@
+using Microsoft.AspNetCore.Mvc;
+using Xunit;
+
+namespace Ardalis.Result.AspNetCore.UnitTests;
+
+public class ResultStatusMapAddDefaultMap
+{
+ [Fact]
+ public void ReturnsBadRequestGivenInvalid()
+ {
+ var controller = new TestController();
+
+ var result = Result.Invalid(new ValidationError("test"));
+
+ // TODO: require identifier
+ Assert.True(true);
+ }
+
+ public class TestController : ControllerBase
+ {
+ public Result Index()
+ {
+ throw new NotImplementedException();
+ }
+
+ public Result ResultString()
+ {
+ throw new NotImplementedException();
+ }
+
+ public Result> ResultEnumerableString()
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+}
diff --git a/tests/Ardalis.Result.UnitTests/SystemTextJsonSerializer.cs b/tests/Ardalis.Result.UnitTests/SystemTextJsonSerializer.cs
index 2ecb2ae..790b132 100644
--- a/tests/Ardalis.Result.UnitTests/SystemTextJsonSerializer.cs
+++ b/tests/Ardalis.Result.UnitTests/SystemTextJsonSerializer.cs
@@ -9,7 +9,7 @@ public class SystemTextJsonSerializer
public void ShouldSerializeResultOfValueType()
{
var result = Result.Success(5);
- string expected = "{\"Value\":5,\"Status\":0,\"IsSuccess\":true,\"SuccessMessage\":\"\",\"CorrelationId\":\"\",\"Errors\":[],\"ValidationErrors\":[]}";
+ string expected = "{\"Value\":5,\"Status\":0,\"IsSuccess\":true,\"SuccessMessage\":\"\",\"CorrelationId\":\"\",\"Location\":\"\",\"Errors\":[],\"ValidationErrors\":[]}";
var json = JsonSerializer.Serialize(result);
@@ -20,7 +20,7 @@ public void ShouldSerializeResultOfValueType()
public void ShouldSerializeResultOfReferenceType()
{
var result = Result.Success(new Foo { Bar = "Result!" });
- string expected = "{\"Value\":{\"Bar\":\"Result!\"},\"Status\":0,\"IsSuccess\":true,\"SuccessMessage\":\"\",\"CorrelationId\":\"\",\"Errors\":[],\"ValidationErrors\":[]}";
+ string expected = "{\"Value\":{\"Bar\":\"Result!\"},\"Status\":0,\"IsSuccess\":true,\"SuccessMessage\":\"\",\"CorrelationId\":\"\",\"Location\":\"\",\"Errors\":[],\"ValidationErrors\":[]}";
var json = JsonSerializer.Serialize(result);
diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props
index 59bda27..448f246 100644
--- a/tests/Directory.Build.props
+++ b/tests/Directory.Build.props
@@ -1,7 +1,7 @@
net6.0;net7.0;net8.0
- net4.7.1;$(NetCoreFrameworks)
+ net48;$(NetCoreFrameworks)
false
latest