mirror of https://github.com/abpframework/abp.git
16 changed files with 826 additions and 2 deletions
@ -0,0 +1,35 @@ |
|||||
|
using System; |
||||
|
using System.Reflection; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc |
||||
|
{ |
||||
|
public static class ActionResultHelper |
||||
|
{ |
||||
|
public static bool IsObjectResult(Type returnType) |
||||
|
{ |
||||
|
//Get the actual return type (unwrap Task)
|
||||
|
if (returnType == typeof(Task)) |
||||
|
{ |
||||
|
returnType = typeof(void); |
||||
|
} |
||||
|
else if (returnType.GetTypeInfo().IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>)) |
||||
|
{ |
||||
|
returnType = returnType.GenericTypeArguments[0]; |
||||
|
} |
||||
|
|
||||
|
if (typeof(IActionResult).GetTypeInfo().IsAssignableFrom(returnType)) |
||||
|
{ |
||||
|
if (typeof(JsonResult).GetTypeInfo().IsAssignableFrom(returnType) || typeof(ObjectResult).GetTypeInfo().IsAssignableFrom(returnType)) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,84 @@ |
|||||
|
using System.Net; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.AspNetCore.Mvc.Filters; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||
|
using Volo.Abp.Authorization; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Domain.Entities; |
||||
|
using Volo.Abp.Http; |
||||
|
using Volo.Abp.Validation; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling |
||||
|
{ |
||||
|
public class AbpExceptionFilter : IExceptionFilter, ITransientDependency |
||||
|
{ |
||||
|
public ILogger<AbpExceptionFilter> Logger { get; set; } |
||||
|
|
||||
|
//TODO: Use EventBus to trigger error handled event like in previous ABP
|
||||
|
|
||||
|
private readonly IExceptionToErrorInfoConverter _errorInfoConverter; |
||||
|
|
||||
|
public AbpExceptionFilter(IExceptionToErrorInfoConverter errorInfoConverter) |
||||
|
{ |
||||
|
_errorInfoConverter = errorInfoConverter; |
||||
|
|
||||
|
Logger = NullLogger<AbpExceptionFilter>.Instance; |
||||
|
} |
||||
|
|
||||
|
public void OnException(ExceptionContext context) |
||||
|
{ |
||||
|
if (!context.ActionDescriptor.IsControllerAction()) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
//TODO: Create DontWrap attribute to control wrapping..?
|
||||
|
|
||||
|
Logger.LogException(context.Exception); |
||||
|
HandleAndWrapException(context); |
||||
|
} |
||||
|
|
||||
|
private void HandleAndWrapException(ExceptionContext context) |
||||
|
{ |
||||
|
if (!ActionResultHelper.IsObjectResult(context.ActionDescriptor.GetMethodInfo().ReturnType)) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
context.HttpContext.Response.StatusCode = GetStatusCode(context); |
||||
|
|
||||
|
context.Result = new ObjectResult( |
||||
|
new RemoteServiceErrorResponse( |
||||
|
_errorInfoConverter.Convert(context.Exception) |
||||
|
) |
||||
|
); |
||||
|
|
||||
|
//EventBus.Trigger(this, new AbpHandledExceptionData(context.Exception));
|
||||
|
|
||||
|
context.Exception = null; //Handled!
|
||||
|
} |
||||
|
|
||||
|
private int GetStatusCode(ExceptionContext context) |
||||
|
{ |
||||
|
if (context.Exception is AbpAuthorizationException) |
||||
|
{ |
||||
|
return context.HttpContext.User.Identity.IsAuthenticated |
||||
|
? (int)HttpStatusCode.Forbidden |
||||
|
: (int)HttpStatusCode.Unauthorized; |
||||
|
} |
||||
|
|
||||
|
if (context.Exception is AbpValidationException) |
||||
|
{ |
||||
|
return (int)HttpStatusCode.BadRequest; |
||||
|
} |
||||
|
|
||||
|
if (context.Exception is EntityNotFoundException) |
||||
|
{ |
||||
|
return (int)HttpStatusCode.NotFound; |
||||
|
} |
||||
|
|
||||
|
return (int)HttpStatusCode.InternalServerError; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,209 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using Volo.Abp.Authorization; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Domain.Entities; |
||||
|
using Volo.Abp.Http; |
||||
|
using Volo.Abp.Ui; |
||||
|
using Volo.Abp.Validation; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling |
||||
|
{ |
||||
|
public class DefaultExceptionToErrorInfoConverter : IExceptionToErrorInfoConverter, ITransientDependency |
||||
|
{ |
||||
|
public bool SendAllExceptionsToClients { get; set; } = false; |
||||
|
|
||||
|
public RemoteServiceErrorInfo Convert(Exception exception) |
||||
|
{ |
||||
|
var errorInfo = CreateErrorInfoWithoutCode(exception); |
||||
|
|
||||
|
if (exception is IHasErrorCode) |
||||
|
{ |
||||
|
errorInfo.Code = (exception as IHasErrorCode).Code; |
||||
|
} |
||||
|
|
||||
|
return errorInfo; |
||||
|
} |
||||
|
|
||||
|
private RemoteServiceErrorInfo CreateErrorInfoWithoutCode(Exception exception) |
||||
|
{ |
||||
|
if (SendAllExceptionsToClients) |
||||
|
{ |
||||
|
return CreateDetailedErrorInfoFromException(exception); |
||||
|
} |
||||
|
|
||||
|
if (exception is AggregateException && exception.InnerException != null) |
||||
|
{ |
||||
|
var aggException = exception as AggregateException; |
||||
|
if (aggException.InnerException is UserFriendlyException || |
||||
|
aggException.InnerException is AbpValidationException) |
||||
|
{ |
||||
|
exception = aggException.InnerException; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (exception is UserFriendlyException) |
||||
|
{ |
||||
|
var userFriendlyException = exception as UserFriendlyException; |
||||
|
return new RemoteServiceErrorInfo(userFriendlyException.Message, userFriendlyException.Details); |
||||
|
} |
||||
|
|
||||
|
if (exception is AbpValidationException) |
||||
|
{ |
||||
|
return new RemoteServiceErrorInfo(L("ValidationError")) |
||||
|
{ |
||||
|
ValidationErrors = GetValidationErrorInfos(exception as AbpValidationException), |
||||
|
Details = GetValidationErrorNarrative(exception as AbpValidationException) |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
if (exception is EntityNotFoundException) |
||||
|
{ |
||||
|
var entityNotFoundException = exception as EntityNotFoundException; |
||||
|
|
||||
|
if (entityNotFoundException.EntityType != null) |
||||
|
{ |
||||
|
return new RemoteServiceErrorInfo( |
||||
|
string.Format( |
||||
|
L("EntityNotFound"), |
||||
|
entityNotFoundException.EntityType.Name, |
||||
|
entityNotFoundException.Id |
||||
|
) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
return new RemoteServiceErrorInfo( |
||||
|
entityNotFoundException.Message |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
if (exception is AbpAuthorizationException) |
||||
|
{ |
||||
|
var authorizationException = exception as AbpAuthorizationException; |
||||
|
return new RemoteServiceErrorInfo(authorizationException.Message); |
||||
|
} |
||||
|
|
||||
|
return new RemoteServiceErrorInfo(L("InternalServerError")); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
private RemoteServiceErrorInfo CreateDetailedErrorInfoFromException(Exception exception) |
||||
|
{ |
||||
|
var detailBuilder = new StringBuilder(); |
||||
|
|
||||
|
AddExceptionToDetails(exception, detailBuilder); |
||||
|
|
||||
|
var errorInfo = new RemoteServiceErrorInfo(exception.Message, detailBuilder.ToString()); |
||||
|
|
||||
|
if (exception is AbpValidationException) |
||||
|
{ |
||||
|
errorInfo.ValidationErrors = GetValidationErrorInfos(exception as AbpValidationException); |
||||
|
} |
||||
|
|
||||
|
return errorInfo; |
||||
|
} |
||||
|
|
||||
|
private void AddExceptionToDetails(Exception exception, StringBuilder detailBuilder) |
||||
|
{ |
||||
|
//Exception Message
|
||||
|
detailBuilder.AppendLine(exception.GetType().Name + ": " + exception.Message); |
||||
|
|
||||
|
//Additional info for UserFriendlyException
|
||||
|
if (exception is UserFriendlyException) |
||||
|
{ |
||||
|
var userFriendlyException = exception as UserFriendlyException; |
||||
|
if (!string.IsNullOrEmpty(userFriendlyException.Details)) |
||||
|
{ |
||||
|
detailBuilder.AppendLine(userFriendlyException.Details); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//Additional info for AbpValidationException
|
||||
|
if (exception is AbpValidationException) |
||||
|
{ |
||||
|
var validationException = exception as AbpValidationException; |
||||
|
if (validationException.ValidationErrors.Count > 0) |
||||
|
{ |
||||
|
detailBuilder.AppendLine(GetValidationErrorNarrative(validationException)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//Exception StackTrace
|
||||
|
if (!string.IsNullOrEmpty(exception.StackTrace)) |
||||
|
{ |
||||
|
detailBuilder.AppendLine("STACK TRACE: " + exception.StackTrace); |
||||
|
} |
||||
|
|
||||
|
//Inner exception
|
||||
|
if (exception.InnerException != null) |
||||
|
{ |
||||
|
AddExceptionToDetails(exception.InnerException, detailBuilder); |
||||
|
} |
||||
|
|
||||
|
//Inner exceptions for AggregateException
|
||||
|
if (exception is AggregateException) |
||||
|
{ |
||||
|
var aggException = exception as AggregateException; |
||||
|
if (aggException.InnerExceptions.IsNullOrEmpty()) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
foreach (var innerException in aggException.InnerExceptions) |
||||
|
{ |
||||
|
AddExceptionToDetails(innerException, detailBuilder); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private RemoteServiceValidationErrorInfo[] GetValidationErrorInfos(AbpValidationException validationException) |
||||
|
{ |
||||
|
var validationErrorInfos = new List<RemoteServiceValidationErrorInfo>(); |
||||
|
|
||||
|
foreach (var validationResult in validationException.ValidationErrors) |
||||
|
{ |
||||
|
var validationError = new RemoteServiceValidationErrorInfo(validationResult.ErrorMessage); |
||||
|
|
||||
|
if (validationResult.MemberNames != null && validationResult.MemberNames.Any()) |
||||
|
{ |
||||
|
validationError.Members = validationResult.MemberNames.Select(m => m.ToCamelCase()).ToArray(); |
||||
|
} |
||||
|
|
||||
|
validationErrorInfos.Add(validationError); |
||||
|
} |
||||
|
|
||||
|
return validationErrorInfos.ToArray(); |
||||
|
} |
||||
|
|
||||
|
private string GetValidationErrorNarrative(AbpValidationException validationException) |
||||
|
{ |
||||
|
var detailBuilder = new StringBuilder(); |
||||
|
detailBuilder.AppendLine(L("ValidationNarrativeTitle")); |
||||
|
|
||||
|
foreach (var validationResult in validationException.ValidationErrors) |
||||
|
{ |
||||
|
detailBuilder.AppendFormat(" - {0}", validationResult.ErrorMessage); |
||||
|
detailBuilder.AppendLine(); |
||||
|
} |
||||
|
|
||||
|
return detailBuilder.ToString(); |
||||
|
} |
||||
|
|
||||
|
private string L(string name) |
||||
|
{ |
||||
|
//TODO: Localization?
|
||||
|
//try
|
||||
|
//{
|
||||
|
// return _localizationManager.GetString(AbpWebConsts.LocalizaionSourceName, name);
|
||||
|
//}
|
||||
|
//catch (Exception)
|
||||
|
//{
|
||||
|
// return name;
|
||||
|
//}
|
||||
|
|
||||
|
return name; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp.Http; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// This interface can be implemented to convert an <see cref="Exception"/> object to an <see cref="RemoteServiceErrorInfo"/> object.
|
||||
|
/// Implements Chain Of Responsibility pattern.
|
||||
|
/// </summary>
|
||||
|
public interface IExceptionToErrorInfoConverter |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Converter method.
|
||||
|
/// </summary>
|
||||
|
/// <param name="exception">The exception</param>
|
||||
|
/// <returns>Error info or null</returns>
|
||||
|
RemoteServiceErrorInfo Convert(Exception exception); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,105 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using Volo.Abp.Logging; |
||||
|
using Volo.Abp.Validation; |
||||
|
|
||||
|
namespace Microsoft.Extensions.Logging |
||||
|
{ |
||||
|
public static class LoggerExtensions |
||||
|
{ |
||||
|
public static void LogWithLevel(this ILogger logger, LogLevel logLevel, string message) |
||||
|
{ |
||||
|
switch (logLevel) |
||||
|
{ |
||||
|
case LogLevel.Critical: |
||||
|
logger.LogCritical(message); |
||||
|
break; |
||||
|
case LogLevel.Error: |
||||
|
logger.LogError(message); |
||||
|
break; |
||||
|
case LogLevel.Warning: |
||||
|
logger.LogWarning(message); |
||||
|
break; |
||||
|
case LogLevel.Information: |
||||
|
logger.LogInformation(message); |
||||
|
break; |
||||
|
case LogLevel.Trace: |
||||
|
logger.LogTrace(message); |
||||
|
break; |
||||
|
default: // LogLevel.Debug || LogLevel.None
|
||||
|
logger.LogDebug(message); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static void LogWithLevel(this ILogger logger, LogLevel logLevel, string message, Exception exception) |
||||
|
{ |
||||
|
switch (logLevel) |
||||
|
{ |
||||
|
case LogLevel.Critical: |
||||
|
logger.LogCritical(exception, message); |
||||
|
break; |
||||
|
case LogLevel.Error: |
||||
|
logger.LogError(exception, message); |
||||
|
break; |
||||
|
case LogLevel.Warning: |
||||
|
logger.LogWarning(exception, message); |
||||
|
break; |
||||
|
case LogLevel.Information: |
||||
|
logger.LogInformation(exception, message); |
||||
|
break; |
||||
|
case LogLevel.Trace: |
||||
|
logger.LogTrace(exception, message); |
||||
|
break; |
||||
|
default: // LogLevel.Debug || LogLevel.None
|
||||
|
logger.LogDebug(message); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static void LogException(this ILogger logger, Exception ex) |
||||
|
{ |
||||
|
var logLevel = (ex as IHasLogLevel)?.LogLevel ?? LogLevel.Error; |
||||
|
|
||||
|
logger.LogWithLevel(logLevel, ex.Message, ex); |
||||
|
LogValidationErrors(logger, ex); |
||||
|
} |
||||
|
|
||||
|
private static void LogValidationErrors(ILogger logger, Exception exception) |
||||
|
{ |
||||
|
//Try to find inner validation exception
|
||||
|
if (exception is AggregateException && exception.InnerException != null) |
||||
|
{ |
||||
|
var aggException = exception as AggregateException; |
||||
|
if (aggException.InnerException is AbpValidationException) |
||||
|
{ |
||||
|
exception = aggException.InnerException; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (!(exception is AbpValidationException)) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var validationException = exception as AbpValidationException; |
||||
|
if (validationException.ValidationErrors.IsNullOrEmpty()) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
logger.LogWithLevel(validationException.LogLevel, "There are " + validationException.ValidationErrors.Count + " validation errors:"); |
||||
|
foreach (var validationResult in validationException.ValidationErrors) |
||||
|
{ |
||||
|
var memberNames = ""; |
||||
|
if (validationResult.MemberNames != null && validationResult.MemberNames.Any()) |
||||
|
{ |
||||
|
memberNames = " (" + string.Join(", ", validationResult.MemberNames) + ")"; |
||||
|
} |
||||
|
|
||||
|
logger.LogWithLevel(validationException.LogLevel, validationResult.ErrorMessage + memberNames); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,58 @@ |
|||||
|
using System; |
||||
|
using System.Runtime.Serialization; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Volo.Abp.Logging; |
||||
|
|
||||
|
namespace Volo.Abp.Authorization |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// This exception is thrown on an unauthorized request.
|
||||
|
/// </summary>
|
||||
|
[Serializable] |
||||
|
public class AbpAuthorizationException : AbpException, IHasLogLevel |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Severity of the exception.
|
||||
|
/// Default: Warn.
|
||||
|
/// </summary>
|
||||
|
public LogLevel LogLevel { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Creates a new <see cref="AbpAuthorizationException"/> object.
|
||||
|
/// </summary>
|
||||
|
public AbpAuthorizationException() |
||||
|
{ |
||||
|
LogLevel = LogLevel.Warning; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Creates a new <see cref="AbpAuthorizationException"/> object.
|
||||
|
/// </summary>
|
||||
|
public AbpAuthorizationException(SerializationInfo serializationInfo, StreamingContext context) |
||||
|
: base(serializationInfo, context) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Creates a new <see cref="AbpAuthorizationException"/> object.
|
||||
|
/// </summary>
|
||||
|
/// <param name="message">Exception message</param>
|
||||
|
public AbpAuthorizationException(string message) |
||||
|
: base(message) |
||||
|
{ |
||||
|
LogLevel = LogLevel.Warning; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Creates a new <see cref="AbpAuthorizationException"/> object.
|
||||
|
/// </summary>
|
||||
|
/// <param name="message">Exception message</param>
|
||||
|
/// <param name="innerException">Inner exception</param>
|
||||
|
public AbpAuthorizationException(string message, Exception innerException) |
||||
|
: base(message, innerException) |
||||
|
{ |
||||
|
LogLevel = LogLevel.Warning; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace Volo.Abp |
||||
|
{ |
||||
|
public interface IHasErrorCode |
||||
|
{ |
||||
|
int Code { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
using Microsoft.Extensions.Logging; |
||||
|
|
||||
|
namespace Volo.Abp.Logging |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Interface to define a <see cref="LogLevel"/> property (see <see cref="LogLevel"/>).
|
||||
|
/// </summary>
|
||||
|
public interface IHasLogLevel |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Log severity.
|
||||
|
/// </summary>
|
||||
|
LogLevel LogLevel { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,125 @@ |
|||||
|
using System; |
||||
|
using System.Runtime.Serialization; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Volo.Abp.Logging; |
||||
|
|
||||
|
namespace Volo.Abp.Ui |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// This exception type is directly shown to the user.
|
||||
|
/// </summary>
|
||||
|
[Serializable] |
||||
|
public class UserFriendlyException : AbpException, IHasLogLevel, IHasErrorCode |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Additional information about the exception.
|
||||
|
/// </summary>
|
||||
|
public string Details { get; private set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// An arbitrary error code.
|
||||
|
/// </summary>
|
||||
|
public int Code { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Severity of the exception.
|
||||
|
/// Default: Warn.
|
||||
|
/// </summary>
|
||||
|
public LogLevel LogLevel { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Constructor.
|
||||
|
/// </summary>
|
||||
|
public UserFriendlyException() |
||||
|
{ |
||||
|
LogLevel = LogLevel.Warning; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Constructor for serializing.
|
||||
|
/// </summary>
|
||||
|
public UserFriendlyException(SerializationInfo serializationInfo, StreamingContext context) |
||||
|
: base(serializationInfo, context) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Constructor.
|
||||
|
/// </summary>
|
||||
|
/// <param name="message">Exception message</param>
|
||||
|
public UserFriendlyException(string message) |
||||
|
: base(message) |
||||
|
{ |
||||
|
LogLevel = LogLevel.Warning; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Constructor.
|
||||
|
/// </summary>
|
||||
|
/// <param name="message">Exception message</param>
|
||||
|
/// <param name="severity">Exception severity</param>
|
||||
|
public UserFriendlyException(string message, LogLevel logLevel) |
||||
|
: base(message) |
||||
|
{ |
||||
|
LogLevel = logLevel; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Constructor.
|
||||
|
/// </summary>
|
||||
|
/// <param name="code">Error code</param>
|
||||
|
/// <param name="message">Exception message</param>
|
||||
|
public UserFriendlyException(int code, string message) |
||||
|
: this(message) |
||||
|
{ |
||||
|
Code = code; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Constructor.
|
||||
|
/// </summary>
|
||||
|
/// <param name="message">Exception message</param>
|
||||
|
/// <param name="details">Additional information about the exception</param>
|
||||
|
public UserFriendlyException(string message, string details) |
||||
|
: this(message) |
||||
|
{ |
||||
|
Details = details; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Constructor.
|
||||
|
/// </summary>
|
||||
|
/// <param name="code">Error code</param>
|
||||
|
/// <param name="message">Exception message</param>
|
||||
|
/// <param name="details">Additional information about the exception</param>
|
||||
|
public UserFriendlyException(int code, string message, string details) |
||||
|
: this(message, details) |
||||
|
{ |
||||
|
Code = code; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Constructor.
|
||||
|
/// </summary>
|
||||
|
/// <param name="message">Exception message</param>
|
||||
|
/// <param name="innerException">Inner exception</param>
|
||||
|
public UserFriendlyException(string message, Exception innerException) |
||||
|
: base(message, innerException) |
||||
|
{ |
||||
|
LogLevel = LogLevel.Warning; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Constructor.
|
||||
|
/// </summary>
|
||||
|
/// <param name="message">Exception message</param>
|
||||
|
/// <param name="details">Additional information about the exception</param>
|
||||
|
/// <param name="innerException">Inner exception</param>
|
||||
|
public UserFriendlyException(string message, string details, Exception innerException) |
||||
|
: this(message, innerException) |
||||
|
{ |
||||
|
Details = details; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,81 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using System.Runtime.Serialization; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Volo.Abp.Logging; |
||||
|
|
||||
|
namespace Volo.Abp.Validation |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// This exception type is used to throws validation exceptions.
|
||||
|
/// </summary>
|
||||
|
[Serializable] |
||||
|
public class AbpValidationException : AbpException, IHasLogLevel |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Detailed list of validation errors for this exception.
|
||||
|
/// </summary>
|
||||
|
public IList<ValidationResult> ValidationErrors { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Exception severity.
|
||||
|
/// Default: Warn.
|
||||
|
/// </summary>
|
||||
|
public LogLevel LogLevel { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Constructor.
|
||||
|
/// </summary>
|
||||
|
public AbpValidationException() |
||||
|
{ |
||||
|
ValidationErrors = new List<ValidationResult>(); |
||||
|
LogLevel = LogLevel.Warning; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Constructor for serializing.
|
||||
|
/// </summary>
|
||||
|
public AbpValidationException(SerializationInfo serializationInfo, StreamingContext context) |
||||
|
: base(serializationInfo, context) |
||||
|
{ |
||||
|
ValidationErrors = new List<ValidationResult>(); |
||||
|
LogLevel = LogLevel.Warning; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Constructor.
|
||||
|
/// </summary>
|
||||
|
/// <param name="message">Exception message</param>
|
||||
|
public AbpValidationException(string message) |
||||
|
: base(message) |
||||
|
{ |
||||
|
ValidationErrors = new List<ValidationResult>(); |
||||
|
LogLevel = LogLevel.Warning; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Constructor.
|
||||
|
/// </summary>
|
||||
|
/// <param name="message">Exception message</param>
|
||||
|
/// <param name="validationErrors">Validation errors</param>
|
||||
|
public AbpValidationException(string message, IList<ValidationResult> validationErrors) |
||||
|
: base(message) |
||||
|
{ |
||||
|
ValidationErrors = validationErrors; |
||||
|
LogLevel = LogLevel.Warning; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Constructor.
|
||||
|
/// </summary>
|
||||
|
/// <param name="message">Exception message</param>
|
||||
|
/// <param name="innerException">Inner exception</param>
|
||||
|
public AbpValidationException(string message, Exception innerException) |
||||
|
: base(message, innerException) |
||||
|
{ |
||||
|
ValidationErrors = new List<ValidationResult>(); |
||||
|
LogLevel = LogLevel.Warning; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||
|
using Volo.Abp.Ui; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.App |
||||
|
{ |
||||
|
[Route("api/exception-test")] |
||||
|
public class ExceptionTestController : AbpController |
||||
|
{ |
||||
|
[HttpGet] |
||||
|
[Route("UserFriendlyException1")] |
||||
|
public void UserFriendlyException1() |
||||
|
{ |
||||
|
throw new UserFriendlyException("This is a sample exception!"); |
||||
|
} |
||||
|
|
||||
|
[HttpGet] |
||||
|
[Route("UserFriendlyException2")] |
||||
|
public ActionResult UserFriendlyException2() |
||||
|
{ |
||||
|
throw new UserFriendlyException("This is a sample exception!"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
using System.Net; |
||||
|
using System.Threading.Tasks; |
||||
|
using Shouldly; |
||||
|
using Volo.Abp.Http; |
||||
|
using Volo.Abp.Ui; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc |
||||
|
{ |
||||
|
public class ExceptionTestController_Tests : AspNetCoreMvcTestBase |
||||
|
{ |
||||
|
[Fact] |
||||
|
public async Task Should_Return_RemoteServiceErrorResponse_For_UserFriendlyException_For_Void_Return_Value() |
||||
|
{ |
||||
|
var result = await GetResponseAsObjectAsync<RemoteServiceErrorResponse>("/api/exception-test/UserFriendlyException1", HttpStatusCode.InternalServerError); |
||||
|
result.Error.ShouldNotBeNull(); |
||||
|
result.Error.Message.ShouldBe("This is a sample exception!"); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_Not_Handle_Exceptions_For_ActionResult_Return_Values() |
||||
|
{ |
||||
|
await Assert.ThrowsAsync<UserFriendlyException>( |
||||
|
async () => await GetResponseAsObjectAsync<RemoteServiceErrorResponse>( |
||||
|
"/api/exception-test/UserFriendlyException2" |
||||
|
) |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue