From 05b0d12618123a80662cd9a01781685366e312dd Mon Sep 17 00:00:00 2001 From: Jonathan Channon Date: Sun, 30 Nov 2025 17:17:05 +0000 Subject: [PATCH 1/2] Remove attribute approach --- src/Carter/Helpers/NegotiationHelper.cs | 50 ------------------- src/Carter/Response/ResponseExtensions.cs | 40 +++++++++++++-- .../ContentNegotiation/NegotiatorModule.cs | 1 + .../TestResponseExtensions.cs | 22 -------- 4 files changed, 37 insertions(+), 76 deletions(-) delete mode 100644 src/Carter/Helpers/NegotiationHelper.cs delete mode 100644 test/Carter.Tests/ContentNegotiation/TestResponseExtensions.cs diff --git a/src/Carter/Helpers/NegotiationHelper.cs b/src/Carter/Helpers/NegotiationHelper.cs deleted file mode 100644 index 2543ca4..0000000 --- a/src/Carter/Helpers/NegotiationHelper.cs +++ /dev/null @@ -1,50 +0,0 @@ -namespace Carter.Helpers; - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using Carter.Attributes; -using Carter.Response; -using Microsoft.AspNetCore.Http; -using Microsoft.Net.Http.Headers; - -public static class NegotiationHelper -{ - /// - /// Selects the most appropriate for content negotiation based on the current 's "Accept" headers, - /// or defaults to if none match. - /// - /// Current - /// List of available instances - /// The selected for the response. - public static IResponseNegotiator SelectNegotiator(HttpContext httpContext, List negotiators) - { - IResponseNegotiator negotiator = null; - - MediaTypeHeaderValue.TryParseList(httpContext.Request.Headers["Accept"], out var accept); - if (accept != null) - { - var ordered = accept.OrderByDescending(x => x.Quality ?? 1); - - foreach (var acceptHeader in ordered) - { - negotiator = negotiators.FirstOrDefault(x => x.CanHandle(acceptHeader)); - if (negotiator != null) - { - break; - } - } - } - - if (negotiator == null) - { - negotiator = negotiators.First(x => x.GetType() == typeof(DefaultJsonResponseNegotiator)); - } - - return negotiator; - } - - public static bool IsTestNegotiator(IResponseNegotiator negotiator) - => negotiator.GetType().IsDefined(typeof(TestNegotiatorAttribute), inherit: true); -} diff --git a/src/Carter/Response/ResponseExtensions.cs b/src/Carter/Response/ResponseExtensions.cs index bff7787..85e3401 100644 --- a/src/Carter/Response/ResponseExtensions.cs +++ b/src/Carter/Response/ResponseExtensions.cs @@ -1,13 +1,13 @@ namespace Carter.Response; using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.Mime; using System.Threading; using System.Threading.Tasks; -using Carter.Helpers; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; using Microsoft.Extensions.DependencyInjection; @@ -26,10 +26,9 @@ public static Task Negotiate(this HttpResponse response, T model, Cancellatio { var negotiators = response.HttpContext.RequestServices .GetServices() - .Where(n => !NegotiationHelper.IsTestNegotiator(n)) .ToList(); - var chosenNegotiator = NegotiationHelper.SelectNegotiator(response.HttpContext, negotiators); + var chosenNegotiator = SelectNegotiator(response.HttpContext, negotiators); return chosenNegotiator.Handle(response.HttpContext.Request, response, model, cancellationToken); } @@ -45,13 +44,46 @@ public static Task AsJson(this HttpResponse response, T model, CancellationTo { var negotiators = response.HttpContext.RequestServices .GetServices() - .Where(n => !NegotiationHelper.IsTestNegotiator(n)) .ToList(); var negotiator = negotiators.First(x => x.CanHandle(new MediaTypeHeaderValue("application/json"))); return negotiator.Handle(response.HttpContext.Request, response, model, cancellationToken); } + + /// + /// Selects the most appropriate for content negotiation based on the current 's "Accept" headers, + /// or defaults to if none match. + /// + /// Current + /// List of available instances + /// The selected for the response. + private static IResponseNegotiator SelectNegotiator(HttpContext httpContext, List negotiators) + { + IResponseNegotiator negotiator = null; + + MediaTypeHeaderValue.TryParseList(httpContext.Request.Headers["Accept"], out var accept); + if (accept != null) + { + var ordered = accept.OrderByDescending(x => x.Quality ?? 1); + + foreach (var acceptHeader in ordered) + { + negotiator = negotiators.FirstOrDefault(x => x.CanHandle(acceptHeader)); + if (negotiator != null) + { + break; + } + } + } + + if (negotiator == null) + { + negotiator = negotiators.First(x => x.GetType() == typeof(DefaultJsonResponseNegotiator)); + } + + return negotiator; + } /// /// Copy a stream into the response body diff --git a/test/Carter.Tests/ContentNegotiation/NegotiatorModule.cs b/test/Carter.Tests/ContentNegotiation/NegotiatorModule.cs index 3f2f91d..9b75848 100644 --- a/test/Carter.Tests/ContentNegotiation/NegotiatorModule.cs +++ b/test/Carter.Tests/ContentNegotiation/NegotiatorModule.cs @@ -1,5 +1,6 @@ namespace Carter.Tests.ContentNegotiation { + using Carter.Response; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; diff --git a/test/Carter.Tests/ContentNegotiation/TestResponseExtensions.cs b/test/Carter.Tests/ContentNegotiation/TestResponseExtensions.cs deleted file mode 100644 index 357d336..0000000 --- a/test/Carter.Tests/ContentNegotiation/TestResponseExtensions.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Carter.Tests.ContentNegotiation; - -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Carter.Helpers; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; - -public static class TestResponseExtensions -{ - public static Task Negotiate(this HttpResponse response, T model, CancellationToken cancellationToken = default) - { - var negotiators = response.HttpContext.RequestServices - .GetServices() - .ToList(); - - var chosenNegotiator = NegotiationHelper.SelectNegotiator(response.HttpContext, negotiators); - - return chosenNegotiator.Handle(response.HttpContext.Request, response, model, cancellationToken); - } -} From 2ed0973d0db7cabcaf9d316b25a4b0c90477d536 Mon Sep 17 00:00:00 2001 From: Jonathan Channon Date: Sun, 30 Nov 2025 17:22:00 +0000 Subject: [PATCH 2/2] hardcode negotiator for AsJson --- src/Carter/Response/ResponseExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Carter/Response/ResponseExtensions.cs b/src/Carter/Response/ResponseExtensions.cs index 85e3401..4a59a5f 100644 --- a/src/Carter/Response/ResponseExtensions.cs +++ b/src/Carter/Response/ResponseExtensions.cs @@ -46,8 +46,8 @@ public static Task AsJson(this HttpResponse response, T model, CancellationTo .GetServices() .ToList(); - var negotiator = negotiators.First(x => x.CanHandle(new MediaTypeHeaderValue("application/json"))); - + var negotiator = negotiators.First(x => x.GetType() == typeof(DefaultJsonResponseNegotiator)); + return negotiator.Handle(response.HttpContext.Request, response, model, cancellationToken); }