public async void Should_Rename_TraceId_To_CorrelationId() { // given const string traceId = "123"; var problem = new MvcProblemDetails { Extensions = { { "traceId", traceId } } }; New.ProblemDetails.BadRequest.CopyStandardFieldsTo(problem); var response = await New.HttpResponseMessage.Of(problem); // when Func <Task> act = async() => await response.EnsureNotProblemDetailAsync(); // then var expected = new MvcProblemDetails { Extensions = { { "correlationId", traceId } } }; problem.CopyStandardFieldsTo(expected); (await act.Should().ThrowAsync <ProblemDetailsException>()) .Which.Details.Should().BeEquivalentTo(expected); }
/// <summary> /// Copy the fields defined in the RFC-7807 spec from <paramref name="source" /> to <paramref name="target" /> /// </summary> public static void CopyStandardFieldsTo(this MvcProblemDetails source, MvcProblemDetails target) { target.Type = source.Type; target.Title = source.Title; target.Instance = source.Instance; target.Status = source.Status; target.Detail = source.Detail; }
public static MvcProblemDetails Create(int statusCode) { var details = new MvcProblemDetails(); SetDetails(details, statusCode); return(details); }
internal void CallBeforeWriteHook(HttpContext context, MvcProblemDetails details) { AddTraceId(context, details); OnBeforeWriteDetails?.Invoke(context, details); #if NETCOREAPP3_1 // Workaround for https://github.com/dotnet/aspnetcore/pull/17565. context.Response.StatusCode = details.Status ?? context.Response.StatusCode; #endif }
public static MvcProblemDetails WithExceptionDetails(this MvcProblemDetails problem, Exception error, IEnumerable <ExceptionDetails> details) { problem.Title ??= TypeNameHelper.GetTypeDisplayName(error.GetType()); problem.Status ??= StatusCodes.Status500InternalServerError; problem.Extensions["errors"] = GetErrors(details).ToList(); problem.Instance ??= GetHelpLink(error); problem.Detail ??= error.Message; return(problem); }
private static object DoRemoveExtensionValue(MvcProblemDetails problem, string key) { if (!problem.Extensions.ContainsKey(key)) { return(null); } var value = problem.Extensions[key]; problem.Extensions.Remove(key); return(value); }
internal bool TryMapProblemDetails(Exception exception, out MvcProblemDetails problem) { foreach (var mapper in Mappers) { if (mapper.TryMap(exception, out problem)) { return(true); } } problem = default; return(false); }
public void ShouldHandleEmptyProblemDetails() { var details = new Microsoft.AspNetCore.Mvc.ProblemDetails(); var serialized = MessagePackSerializer.Serialize(details, _options); var deserialized = MessagePackSerializer.Deserialize <Microsoft.AspNetCore.Mvc.ProblemDetails>(serialized, _options); Assert.Null(deserialized.Detail); Assert.Null(deserialized.Instance); Assert.Null(deserialized.Status); Assert.Null(deserialized.Title); Assert.Null(deserialized.Type); Assert.Empty(deserialized.Extensions); }
private void AddTraceId(HttpContext context, MvcProblemDetails details) { var key = TraceIdPropertyName; if (details.Extensions.ContainsKey(key)) { return; } var traceId = GetTraceId.Invoke(context); if (!string.IsNullOrEmpty(traceId)) { details.Extensions[key] = traceId; } }
public async Task Explicit_Client_Exception_Is_Not_Logged_As_Unhandled_Error() { var details = new MvcProblemDetails { Title = "Too Many Requests", Status = StatusCodes.Status429TooManyRequests, }; var ex = new ProblemDetailsException(details); using var client = CreateClient(handler: ResponseThrows(ex)); var response = await client.GetAsync(string.Empty); Assert.Equal((HttpStatusCode)StatusCodes.Status429TooManyRequests, response.StatusCode); AssertUnhandledExceptionNotLogged(Logger); }
private static void FlattenNestedExtensions(MvcProblemDetails problem) { // a *sub-class* of a ProblemDetails or ValidationProblemDetails aren't associated // with special-case converters and so end up causing the Extensions property to be // serialized "as is" and ending up being added as an nested entry in // the Extensions property itself! // this is a workaround to flatten the extensions values into the Extensions property // for details of issue see: https://github.com/khellang/Middleware/issues/74 if (!(problem.RemoveExtensionValue("extensions") is JObject unhandledExtensionValue)) { return; } foreach (var(key, value) in unhandledExtensionValue) { problem.Extensions[key] = value.ToObject(typeof(object)); } }
public bool TryMap(Exception exception, out MvcProblemDetails problem) { if (CanMap(exception.GetType())) { try { problem = Mapping(exception); return(true); } catch { problem = default; return(false); } } problem = default; return(false); }
public async Task ProblemDetailsException_IsHandled() { var expected = HttpStatusCode.TooManyRequests; var details = new MvcProblemDetails { Title = "Too Many Requests", Status = (int)expected, }; var ex = new ProblemDetailsException(details); using var client = CreateClient(handler: ResponseThrows(ex)); var response = await client.GetAsync("/"); Assert.Equal(expected, response.StatusCode); await AssertIsProblemDetailsResponse(response); }
internal bool TryMapProblemDetails(Exception exception, out MvcProblemDetails problem) { var type = exception.GetType(); if (Mappings.TryGetValue(type, out var mapping)) { try { problem = mapping(exception); return(true); } catch { problem = default; return(false); } } problem = default; return(false); }
public async Task ProblemDetailsException_IsHandled() { var problemStatus = HttpStatusCode.TooManyRequests; var details = new MvcProblemDetails { Title = "Too Many Requests", Status = (int)problemStatus, }; var ex = new ProblemDetailsException(details); using (var server = CreateServer(handler: ResponseThrows(ex))) using (var client = server.CreateClient()) { var response = await client.GetAsync("/"); Assert.Equal(problemStatus, response.StatusCode); await AssertIsProblemDetailsResponse(response); } }
public async Task ProblemDetailsException_IsHandled() { const int expected = StatusCodes.Status429TooManyRequests; var details = new MvcProblemDetails { Title = ReasonPhrases.GetReasonPhrase(expected), Type = $"https://httpstatuses.com/{expected}", Status = expected, }; var ex = new ProblemDetailsException(details); using var client = CreateClient(handler: ResponseThrows(ex)); var response = await client.GetAsync("/"); Assert.Equal(expected, (int)response.StatusCode); await AssertIsProblemDetailsResponse(response, expectExceptionDetails : false); }
public async void CorrelationId_Should_Use_Configured_Casing() { // IMPORTANT: This is causing flaky tests as we're modifying static fields and yet xunit will run // tests is parallel and so the change to this static field will apply to those tests that are running // in parallel with this one! // todo: resolve test flake by making this test run NOT in parallel with other tests var originalSettings = JsonProblemDetailsConverter.SerializerOptions; try { // given JsonProblemDetailsConverter.SerializerOptions = new JsonSerializerOptions { PropertyNamingPolicy = null // Pascal casing }; const string traceId = "123"; var problem = new MvcProblemDetails { Extensions = { { "traceId", traceId } } }; New.ProblemDetails.BadRequest.CopyStandardFieldsTo(problem); var response = await New.HttpResponseMessage.Of(problem); // when Func <Task> act = async() => await response.EnsureNotProblemDetailAsync(); // then (await act.Should().ThrowAsync <ProblemDetailsException>()) .Which.Details.Extensions.Should().ContainKey("CorrelationId"); } finally { JsonProblemDetailsConverter.SerializerOptions = originalSettings; } }
public void ShouldHandleProblemDetails() { var details = new Microsoft.AspNetCore.Mvc.ProblemDetails { Type = "https://tools.ietf.org/html/rfc7231#section-6.6.1", Title = "Internal Server Error", Status = 500, Detail = "Failed to serialize CSharpFunctionalExtensions.Result`2[[System.Decimal, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[Microsoft.AspNetCore.Mvc.ProblemDetails, Microsoft.AspNetCore.Mvc.Core, Version=3.1.4.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]] value." }; details.Extensions.Add("spanId", "0000000000000000"); details.Extensions.Add("requestId", "0HLVTHCNOGD28"); var serialized = MessagePackSerializer.Serialize(details, _options); var deserialized = MessagePackSerializer.Deserialize <Microsoft.AspNetCore.Mvc.ProblemDetails>(serialized, _options); Assert.Equal(details.Detail, deserialized.Detail); Assert.Null(deserialized.Instance); Assert.Equal(details.Status, deserialized.Status); Assert.Equal(details.Title, deserialized.Title); Assert.Equal(details.Type, deserialized.Type); Assert.Equal(details.Extensions, deserialized.Extensions); }
/// <summary> /// Attempts to remove the entry in <see cref="MvcProblemDetails.Extensions" /> matching the /// <paramref name="key" />, returning the value of the entry removed, if any. /// </summary> public static object RemoveExtensionValue(this MvcProblemDetails problem, string key) { return(DoRemoveExtensionValue(problem, key) ?? DoRemoveExtensionValue(problem, StringUtils.PascalCase(key))); }
public ProblemDetailsException(MvcProblemDetails details) : this(details, null) { Details = details; }
public ProblemDetailsException(Microsoft.AspNetCore.Mvc.ProblemDetails details) { Details = details; }
public ProblemDetailsWrapper(Microsoft.AspNetCore.Mvc.ProblemDetails problemDetails) { }
public ProblemDetailsException(MvcProblemDetails details, Exception?innerException) : base($"{details.Type} : {details.Title}", innerException) { Details = details; }
public ProblemDetailsException(MvcProblemDetails details) : base($"{details.Type} : {details.Title}") { Details = details; }
public ProblemDetailsException(Microsoft.AspNetCore.Mvc.ProblemDetails details) : base($"{details.Type} : {details.Title}") { Details = details; }
private static void SetDetails(MvcProblemDetails details, int statusCode) { details.Status = statusCode; details.Type = GetDefaultType(statusCode); details.Title = ReasonPhrases.GetReasonPhrase(statusCode); }
private static void SetDetails(MvcProblemDetails details, int statusCode) { details.Status = statusCode; details.Type = $"https://httpstatuses.com/{statusCode}"; details.Title = ReasonPhrases.GetReasonPhrase(statusCode); }