Merge pull request #10534 from umbraco/v9/task/cleanup-validation-results

Streamlines response handling in controllers
This commit is contained in:
Bjarke Berg
2021-07-01 23:04:45 +02:00
committed by GitHub
27 changed files with 383 additions and 266 deletions

View File

@@ -1,8 +1,9 @@
using System.Net;
using System.Net;
using Microsoft.AspNetCore.Mvc;
namespace Umbraco.Cms.Web.Common.ActionsResults
{
// TODO: What is the purpose of this? Doesn't seem to add any benefit
public class UmbracoProblemResult : ObjectResult
{
public UmbracoProblemResult(string message, HttpStatusCode httpStatusCode = HttpStatusCode.InternalServerError) : base(new {Message = message})

View File

@@ -1,10 +1,19 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Extensions;
namespace Umbraco.Cms.Web.Common.ActionsResults
{
// TODO: This should probably follow the same conventions as in aspnet core and use ProblemDetails
// and ProblemDetails factory. See https://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.Core/src/ControllerBase.cs#L1977
// ProblemDetails is explicitly checked for in the application model.
// In our base class UmbracoAuthorizedApiController the logic is there to create a ProblemDetails.
// However, to do this will require changing how angular deals with errors since the response will
// probably be different. Would just be better to follow the aspnet patterns.
/// <summary>
/// Custom result to return a validation error message with required headers
/// </summary>
@@ -13,6 +22,11 @@ namespace Umbraco.Cms.Web.Common.ActionsResults
/// </remarks>
public class ValidationErrorResult : ObjectResult
{
/// <summary>
/// Typically this should not be used and just use the ValidationProblem method on the base controller class.
/// </summary>
/// <param name="errorMessage"></param>
/// <returns></returns>
public static ValidationErrorResult CreateNotificationValidationErrorResult(string errorMessage)
{
var notificationModel = new SimpleNotificationModel
@@ -23,6 +37,9 @@ namespace Umbraco.Cms.Web.Common.ActionsResults
return new ValidationErrorResult(notificationModel);
}
public ValidationErrorResult(ModelStateDictionary modelState)
: this(new SimpleValidationModel(modelState.ToErrorDictionary())) { }
public ValidationErrorResult(object value, int statusCode) : base(value)
{
StatusCode = statusCode;
@@ -32,6 +49,7 @@ namespace Umbraco.Cms.Web.Common.ActionsResults
{
}
// TODO: Like here, shouldn't we use ProblemDetails?
public ValidationErrorResult(string errorMessage, int statusCode) : base(new { Message = errorMessage })
{
StatusCode = statusCode;

View File

@@ -0,0 +1,55 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Umbraco.Extensions
{
public static class ModelStateExtensions
{
/// <summary>
/// Checks if there are any model errors on any fields containing the prefix
/// </summary>
/// <param name="state"></param>
/// <param name="prefix"></param>
/// <returns></returns>
public static bool IsValid(this ModelStateDictionary state, string prefix) =>
state.Where(v => v.Key.StartsWith(prefix + ".")).All(v => !v.Value.Errors.Any());
public static IDictionary<string, object> ToErrorDictionary(this ModelStateDictionary modelState)
{
var modelStateError = new Dictionary<string, object>();
foreach (KeyValuePair<string, ModelStateEntry> keyModelStatePair in modelState)
{
var key = keyModelStatePair.Key;
ModelErrorCollection errors = keyModelStatePair.Value.Errors;
if (errors != null && errors.Count > 0)
{
modelStateError.Add(key, errors.Select(error => error.ErrorMessage));
}
}
return modelStateError;
}
/// <summary>
/// Serializes the ModelState to JSON for JavaScript to interrogate the errors
/// </summary>
/// <param name="state"></param>
/// <returns></returns>
public static JsonResult ToJsonErrors(this ModelStateDictionary state) =>
new JsonResult(new
{
success = state.IsValid.ToString().ToLower(),
failureType = "ValidationError",
validationErrors = from e in state
where e.Value.Errors.Count > 0
select new
{
name = e.Key,
errors = e.Value.Errors.Select(x => x.ErrorMessage)
.Concat(
e.Value.Errors.Where(x => x.Exception != null).Select(x => x.Exception.Message))
}
});
}
}