Browse Source

Exception handler.

pull/492/head
Sebastian 6 years ago
parent
commit
7ff5517d97
  1. 9
      backend/src/Squidex.Web/ApiExceptionFilterAttribute.cs
  2. 30
      backend/src/Squidex.Web/Pipeline/DefaultExceptionHandler.cs
  3. 16
      backend/src/Squidex.Web/Pipeline/IExceptionHandler.cs
  4. 42
      backend/src/Squidex.Web/Pipeline/RequestExceptionMiddleware.cs
  5. 7
      backend/src/Squidex.Web/Pipeline/RequestLogPerformanceMiddleware.cs
  6. 3
      backend/src/Squidex/Config/Web/WebServices.cs
  7. 23
      backend/tests/Squidex.Web.Tests/ApiExceptionFilterAttributeTests.cs

9
backend/src/Squidex.Web/ApiExceptionFilterAttribute.cs

@ -9,7 +9,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Squidex.Infrastructure.Log;
using Squidex.Web.Pipeline;
namespace Squidex.Web
{
@ -33,12 +33,9 @@ namespace Squidex.Web
if (!wellKnown)
{
var log = context.HttpContext.RequestServices.GetService<ISemanticLog>();
var exceptionHandler = context.HttpContext.RequestServices.GetService<IExceptionHandler>();
if (log != null)
{
log.LogError(context.Exception, w => w.WriteProperty("status", "UnhandledException"));
}
exceptionHandler?.Handle(context.Exception);
}
context.Result = GetResult(error);

30
backend/src/Squidex.Web/Pipeline/DefaultExceptionHandler.cs

@ -0,0 +1,30 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Log;
namespace Squidex.Web.Pipeline
{
public class DefaultExceptionHandler : IExceptionHandler
{
private readonly ISemanticLog log;
public DefaultExceptionHandler(ISemanticLog log)
{
Guard.NotNull(log);
this.log = log;
}
public void Handle(Exception ex)
{
log.LogError(ex, w => w.WriteProperty("status", "UnhandledException"));
}
}
}

16
backend/src/Squidex.Web/Pipeline/IExceptionHandler.cs

@ -0,0 +1,16 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
namespace Squidex.Web.Pipeline
{
public interface IExceptionHandler
{
void Handle(Exception exception);
}
}

42
backend/src/Squidex.Web/Pipeline/RequestExceptionMiddleware.cs

@ -0,0 +1,42 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.Security;
namespace Squidex.Web.Pipeline
{
public sealed class RequestExceptionMiddleware : IMiddleware
{
private readonly IExceptionHandler exceptionHandler;
public RequestExceptionMiddleware(IExceptionHandler exceptionHandler)
{
Guard.NotNull(exceptionHandler);
this.exceptionHandler = exceptionHandler;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
try
{
await next(context);
}
catch (Exception ex)
{
exceptionHandler.Handle(ex);
context.Response.StatusCode = 500;
}
}
}
}

7
backend/src/Squidex.Web/Pipeline/RequestLogPerformanceMiddleware.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
@ -40,12 +39,6 @@ namespace Squidex.Web.Pipeline
{
await next(context);
}
catch (Exception ex)
{
log.LogError(ex, w => w.WriteProperty("status", "UnhandledException"));
context.Response.StatusCode = 500;
}
finally
{
var elapsedMs = watch.Stop();

3
backend/src/Squidex/Config/Web/WebServices.cs

@ -61,6 +61,9 @@ namespace Squidex.Config.Web
services.AddSingletonAs<ActionContextAccessor>()
.As<IActionContextAccessor>();
services.AddSingletonAs<DefaultExceptionHandler>()
.AsOptional<IExceptionHandler>();
services.Configure<ApiBehaviorOptions>(options =>
{
options.SuppressModelStateInvalidFilter = true;

23
backend/tests/Squidex.Web.Tests/ApiExceptionFilterAttributeTests.cs

@ -19,13 +19,14 @@ using Microsoft.AspNetCore.Routing;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.Validation;
using Squidex.Web.Pipeline;
using Xunit;
namespace Squidex.Web
{
public class ApiExceptionFilterAttributeTests
{
private readonly ISemanticLog log = A.Fake<ISemanticLog>();
private readonly IExceptionHandler exceptionHandler = A.Fake<IExceptionHandler>();
private readonly ApiExceptionFilterAttribute sut = new ApiExceptionFilterAttribute();
[Fact]
@ -49,7 +50,7 @@ namespace Squidex.Web
Assert.Equal(new[] { "Error1", "P: Error2", "P1, P2: Error3" }, ((ErrorDto)result.Value).Details);
A.CallTo(() => log.Log(SemanticLogLevel.Error, None.Value, A<Action<None, IObjectWriter>>._))
A.CallTo(() => exceptionHandler.Handle(A<Exception>._))
.MustNotHaveHappened();
}
@ -62,7 +63,7 @@ namespace Squidex.Web
Assert.IsType<NotFoundResult>(context.Result);
A.CallTo(() => log.Log(SemanticLogLevel.Error, None.Value, A<Action<None, IObjectWriter>>._))
A.CallTo(() => exceptionHandler.Handle(A<Exception>._))
.MustNotHaveHappened();
}
@ -75,7 +76,7 @@ namespace Squidex.Web
Validate(500, context.Result, null);
A.CallTo(() => log.Log(SemanticLogLevel.Error, None.Value, A<Action<None, IObjectWriter>>._))
A.CallTo(() => exceptionHandler.Handle(A<Exception>._))
.MustHaveHappened();
}
@ -88,7 +89,7 @@ namespace Squidex.Web
Validate(400, context.Result, context.Exception);
A.CallTo(() => log.Log(SemanticLogLevel.Error, None.Value, A<Action<None, IObjectWriter>>._))
A.CallTo(() => exceptionHandler.Handle(A<Exception>._))
.MustNotHaveHappened();
}
@ -101,7 +102,7 @@ namespace Squidex.Web
Validate(400, context.Result, context.Exception);
A.CallTo(() => log.Log(SemanticLogLevel.Error, None.Value, A<Action<None, IObjectWriter>>._))
A.CallTo(() => exceptionHandler.Handle(A<Exception>._))
.MustNotHaveHappened();
}
@ -114,7 +115,7 @@ namespace Squidex.Web
Validate(412, context.Result, context.Exception);
A.CallTo(() => log.Log(SemanticLogLevel.Error, None.Value, A<Action<None, IObjectWriter>>._))
A.CallTo(() => exceptionHandler.Handle(A<Exception>._))
.MustNotHaveHappened();
}
@ -127,7 +128,7 @@ namespace Squidex.Web
Validate(403, context.Result, context.Exception);
A.CallTo(() => log.Log(SemanticLogLevel.Error, None.Value, A<Action<None, IObjectWriter>>._))
A.CallTo(() => exceptionHandler.Handle(A<Exception>._))
.MustNotHaveHappened();
}
@ -140,7 +141,7 @@ namespace Squidex.Web
Validate(403, context.Result, null);
A.CallTo(() => log.Log(SemanticLogLevel.Error, None.Value, A<Action<None, IObjectWriter>>._))
A.CallTo(() => exceptionHandler.Handle(A<Exception>._))
.MustHaveHappened();
}
@ -153,7 +154,7 @@ namespace Squidex.Web
Validate(403, context.Result, null);
A.CallTo(() => log.Log(SemanticLogLevel.Error, None.Value, A<Action<None, IObjectWriter>>._))
A.CallTo(() => exceptionHandler.Handle(A<Exception>._))
.MustNotHaveHappened();
}
@ -190,7 +191,7 @@ namespace Squidex.Web
var services = A.Fake<IServiceProvider>();
A.CallTo(() => services.GetService(typeof(ISemanticLog)))
.Returns(log);
.Returns(exceptionHandler);
var httpContext = new DefaultHttpContext
{

Loading…
Cancel
Save