Skip to content

Commit 43d2115

Browse files
kishanAnempranavkm
andauthored
Media type application/problem+json lost in combination with ProducesAttribute #19510 (#30367)
* fix and test case added * fix * Apply suggestions from code review Co-authored-by: Pranav K <[email protected]>
1 parent cc79d3e commit 43d2115

File tree

2 files changed

+87
-17
lines changed

2 files changed

+87
-17
lines changed

src/Mvc/Mvc.Core/src/Infrastructure/ObjectResultExecutor.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -151,19 +151,16 @@ private Task ExecuteAsyncCore(ActionContext context, ObjectResult result, Type?
151151
private static void InferContentTypes(ActionContext context, ObjectResult result)
152152
{
153153
Debug.Assert(result.ContentTypes != null);
154-
if (result.ContentTypes.Count != 0)
155-
{
156-
return;
157-
}
158154

159155
// If the user sets the content type both on the ObjectResult (example: by Produces) and Response object,
160156
// then the one set on ObjectResult takes precedence over the Response object
161157
var responseContentType = context.HttpContext.Response.ContentType;
162-
if (!string.IsNullOrEmpty(responseContentType))
158+
if (result.ContentTypes.Count == 0 && !string.IsNullOrEmpty(responseContentType))
163159
{
164160
result.ContentTypes.Add(responseContentType);
165161
}
166-
else if (result.Value is ProblemDetails)
162+
163+
if (result.Value is ProblemDetails)
167164
{
168165
result.ContentTypes.Add("application/problem+json");
169166
result.ContentTypes.Add("application/problem+xml");

src/Mvc/Mvc.Core/test/Infrastructure/ObjectResultExecutorTest.cs

Lines changed: 84 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -114,50 +114,123 @@ public async Task ExecuteAsync_WithOneProvidedContentType_FromResponseContentTyp
114114
}
115115

116116
[Fact]
117-
public async Task ExecuteAsync_ForProblemDetailsValue_UsesSpecifiedContentType()
117+
public async Task ExecuteAsync_WithResponseAndObjectResultContentType_ForProblemDetailsValue_UsesXMLContentType()
118118
{
119119
// Arrange
120120
var executor = CreateExecutor();
121121

122122
var httpContext = new DefaultHttpContext();
123123
var actionContext = new ActionContext() { HttpContext = httpContext };
124-
httpContext.Response.ContentType = "application/json";
124+
httpContext.Response.ContentType = "application/xml"; // This will not be used
125125

126126
var result = new ObjectResult(new ProblemDetails())
127127
{
128-
ContentTypes = { "text/plain" },
128+
ContentTypes = { "text/plain" }, // This will not be used
129129
};
130-
result.Formatters.Add(new TestXmlOutputFormatter());
130+
result.Formatters.Add(new TestXmlOutputFormatter()); // This will be chosen based on the problem details content type
131131
result.Formatters.Add(new TestJsonOutputFormatter());
132-
result.Formatters.Add(new TestStringOutputFormatter()); // This will be chosen based on the content type
132+
result.Formatters.Add(new TestStringOutputFormatter());
133133

134134
// Act
135135
await executor.ExecuteAsync(actionContext, result);
136136

137137
// Assert
138-
MediaTypeAssert.Equal("text/plain; charset=utf-8", httpContext.Response.ContentType);
138+
MediaTypeAssert.Equal("application/problem+xml; charset=utf-8", httpContext.Response.ContentType);
139139
}
140140

141141
[Fact]
142-
public async Task ExecuteAsync_ForProblemDetailsValue_UsesResponseContentType()
142+
public async Task ExecuteAsync_WithResponseContentType_ForProblemDetailsValue_UsesProblemDetailXMLContentType()
143143
{
144144
// Arrange
145145
var executor = CreateExecutor();
146146

147147
var httpContext = new DefaultHttpContext();
148148
var actionContext = new ActionContext() { HttpContext = httpContext };
149-
httpContext.Response.ContentType = "application/json";
149+
httpContext.Response.ContentType = "application/json"; // This will not be used
150150

151151
var result = new ObjectResult(new ProblemDetails());
152+
result.Formatters.Add(new TestXmlOutputFormatter()); // This will be chosen based on the problem details content type
153+
result.Formatters.Add(new TestJsonOutputFormatter());
154+
result.Formatters.Add(new TestStringOutputFormatter());
155+
156+
// Act
157+
await executor.ExecuteAsync(actionContext, result);
158+
159+
// Assert
160+
MediaTypeAssert.Equal("application/problem+xml; charset=utf-8", httpContext.Response.ContentType);
161+
}
162+
163+
[Fact]
164+
public async Task ExecuteAsync_ForProblemDetailsValue_UsesProblemDetailsContentType()
165+
{
166+
// Arrange
167+
var executor = CreateExecutor();
168+
169+
var httpContext = new DefaultHttpContext();
170+
var actionContext = new ActionContext() { HttpContext = httpContext };
171+
httpContext.Response.ContentType = "application/json"; // This will not be used
172+
173+
var result = new ObjectResult(new ProblemDetails());
174+
result.Formatters.Add(new TestXmlOutputFormatter()); // This will be chosen based on the problem details content type
175+
result.Formatters.Add(new TestJsonOutputFormatter());
176+
result.Formatters.Add(new TestStringOutputFormatter());
177+
178+
// Act
179+
await executor.ExecuteAsync(actionContext, result);
180+
181+
// Assert
182+
MediaTypeAssert.Equal("application/problem+xml; charset=utf-8", httpContext.Response.ContentType);
183+
}
184+
185+
[Fact]
186+
public async Task ExecuteAsync_ForProblemDetailsValue_UsesProblemDetailsJsonContentType_BasedOnAcceptHeader()
187+
{
188+
// Arrange
189+
var executor = CreateExecutor();
190+
191+
var httpContext = new DefaultHttpContext();
192+
var actionContext = new ActionContext() { HttpContext = httpContext };
193+
httpContext.Request.Headers[HeaderNames.Accept] = "application/json"; // This will not be used
194+
httpContext.Response.ContentType = "application/xml"; // This will not be used
195+
196+
var result = new ObjectResult(new ProblemDetails())
197+
{
198+
ContentTypes = { "text/plain" }, // This will not be used
199+
};
200+
result.Formatters.Add(new TestJsonOutputFormatter()); // This will be chosen based on the Accept Headers "application/json"
152201
result.Formatters.Add(new TestXmlOutputFormatter());
153-
result.Formatters.Add(new TestJsonOutputFormatter()); // This will be chosen based on the response content type
154202
result.Formatters.Add(new TestStringOutputFormatter());
155203

156204
// Act
157205
await executor.ExecuteAsync(actionContext, result);
158206

159207
// Assert
160-
MediaTypeAssert.Equal("application/json; charset=utf-8", httpContext.Response.ContentType);
208+
MediaTypeAssert.Equal("application/problem+json; charset=utf-8", httpContext.Response.ContentType);
209+
}
210+
211+
[Fact]
212+
public async Task ExecuteAsync_ForProblemDetailsValue_UsesProblemDetailsXMLContentType_BasedOnAcceptHeader()
213+
{
214+
// Arrange
215+
var executor = CreateExecutor();
216+
217+
var httpContext = new DefaultHttpContext();
218+
var actionContext = new ActionContext() { HttpContext = httpContext };
219+
httpContext.Request.Headers[HeaderNames.Accept] = "application/xml"; // This will not be used
220+
221+
var result = new ObjectResult(new ProblemDetails())
222+
{
223+
ContentTypes = { "text/plain" }, // This will not be used
224+
};
225+
result.Formatters.Add(new TestJsonOutputFormatter());
226+
result.Formatters.Add(new TestXmlOutputFormatter()); // This will be chosen based on the Accept Headers "application/xml"
227+
result.Formatters.Add(new TestStringOutputFormatter());
228+
229+
// Act
230+
await executor.ExecuteAsync(actionContext, result);
231+
232+
// Assert
233+
MediaTypeAssert.Equal("application/problem+xml; charset=utf-8", httpContext.Response.ContentType);
161234
}
162235

163236
[Fact]
@@ -170,7 +243,7 @@ public async Task ExecuteAsync_NoContentTypeProvidedForProblemDetails_UsesDefaul
170243
var actionContext = new ActionContext() { HttpContext = httpContext };
171244

172245
var result = new ObjectResult(new ProblemDetails());
173-
result.Formatters.Add(new TestXmlOutputFormatter()); // This will be chosen based on the implicitly added content type
246+
result.Formatters.Add(new TestXmlOutputFormatter()); // This will be chosen based on the problem details added content type
174247
result.Formatters.Add(new TestJsonOutputFormatter());
175248
result.Formatters.Add(new TestStringOutputFormatter());
176249

0 commit comments

Comments
 (0)