Fix for Content negotiation should fallback to the first formatter that can write the type #1033
- Includes functional and unit tests.
This commit is contained in:
parent
d57c34392f
commit
33173d3031
|
|
@ -86,6 +86,20 @@ namespace Microsoft.AspNet.Mvc
|
||||||
formatterContext,
|
formatterContext,
|
||||||
formatters,
|
formatters,
|
||||||
contentTypes);
|
contentTypes);
|
||||||
|
|
||||||
|
// This would be the case when no formatter could write the type base on the
|
||||||
|
// accept headers and the request content type. Fallback on type based match.
|
||||||
|
if(selectedFormatter == null)
|
||||||
|
{
|
||||||
|
foreach (var formatter in formatters)
|
||||||
|
{
|
||||||
|
if (formatter.CanWriteResult(formatterContext,
|
||||||
|
formatter.SupportedMediaTypes?.FirstOrDefault()))
|
||||||
|
{
|
||||||
|
return formatter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (ContentTypes.Count == 1)
|
else if (ContentTypes.Count == 1)
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,19 @@ namespace Microsoft.AspNet.Mvc
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IOutputFormatter
|
public interface IOutputFormatter
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the mutable collection of character encodings supported by
|
||||||
|
/// this <see cref="IOutputFormatter"/>. The encodings are
|
||||||
|
/// used when writing the data.
|
||||||
|
/// </summary>
|
||||||
|
IList<Encoding> SupportedEncodings { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the mutable collection of <see cref="MediaTypeHeaderValue"/> elements supported by
|
||||||
|
/// this <see cref="IOutputFormatter"/>.
|
||||||
|
/// </summary>
|
||||||
|
IList<MediaTypeHeaderValue> SupportedMediaTypes { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether this <see cref="IOutputFormatter"/> can serialize
|
/// Determines whether this <see cref="IOutputFormatter"/> can serialize
|
||||||
/// an object of the specified type.
|
/// an object of the specified type.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
|
using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
|
||||||
|
|
||||||
|
|
@ -11,6 +14,10 @@ namespace Microsoft.AspNet.Mvc
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class NoContentFormatter : IOutputFormatter
|
public class NoContentFormatter : IOutputFormatter
|
||||||
{
|
{
|
||||||
|
public IList<Encoding> SupportedEncodings { get; private set; }
|
||||||
|
|
||||||
|
public IList<MediaTypeHeaderValue> SupportedMediaTypes { get; private set; }
|
||||||
|
|
||||||
public bool CanWriteResult(OutputFormatterContext context, MediaTypeHeaderValue contentType)
|
public bool CanWriteResult(OutputFormatterContext context, MediaTypeHeaderValue contentType)
|
||||||
{
|
{
|
||||||
// ignore the contentType and just look at the content.
|
// ignore the contentType and just look at the content.
|
||||||
|
|
|
||||||
|
|
@ -17,19 +17,6 @@ namespace Microsoft.AspNet.Mvc
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class OutputFormatter : IOutputFormatter
|
public abstract class OutputFormatter : IOutputFormatter
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Gets the mutable collection of character encodings supported by
|
|
||||||
/// this <see cref="OutputFormatter"/> instance. The encodings are
|
|
||||||
/// used when writing the data.
|
|
||||||
/// </summary>
|
|
||||||
public IList<Encoding> SupportedEncodings { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the mutable collection of <see cref="MediaTypeHeaderValue"/> elements supported by
|
|
||||||
/// this <see cref="OutputFormatter"/> instance.
|
|
||||||
/// </summary>
|
|
||||||
public IList<MediaTypeHeaderValue> SupportedMediaTypes { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="OutputFormatter"/> class.
|
/// Initializes a new instance of the <see cref="OutputFormatter"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -39,6 +26,12 @@ namespace Microsoft.AspNet.Mvc
|
||||||
SupportedMediaTypes = new List<MediaTypeHeaderValue>();
|
SupportedMediaTypes = new List<MediaTypeHeaderValue>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IList<Encoding> SupportedEncodings { get; private set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IList<MediaTypeHeaderValue> SupportedMediaTypes { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines the best <see cref="Encoding"/> amongst the supported encodings
|
/// Determines the best <see cref="Encoding"/> amongst the supported encodings
|
||||||
/// for reading or writing an HTTP entity body based on the provided <paramref name="contentTypeHeader"/>.
|
/// for reading or writing an HTTP entity body based on the provided <paramref name="contentTypeHeader"/>.
|
||||||
|
|
|
||||||
|
|
@ -274,6 +274,52 @@ namespace Microsoft.AspNet.Mvc.Core.Test.ActionResults
|
||||||
httpResponse.VerifySet(r => r.ContentType = expectedContentType);
|
httpResponse.VerifySet(r => r.ContentType = expectedContentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("", 2)]
|
||||||
|
[InlineData(null, 2)]
|
||||||
|
[InlineData("application/xml", 3)]
|
||||||
|
[InlineData("application/custom", 3)]
|
||||||
|
[InlineData("application/xml;q=1, application/custom;q=0.8", 4)]
|
||||||
|
public void SelectFormatter_WithNoMatchingAcceptHeadersAndRequestContentType_PicksFormatterBasedOnObjectType
|
||||||
|
(string acceptHeader, int attemptedCountForCanWrite)
|
||||||
|
{
|
||||||
|
// For no accept headers,
|
||||||
|
//can write is called twice once for the request media type and once for the type match pass.
|
||||||
|
// For each adduaccept header, it is called once.
|
||||||
|
// Arrange
|
||||||
|
var stream = new MemoryStream();
|
||||||
|
var httpResponse = new Mock<HttpResponse>();
|
||||||
|
httpResponse.SetupProperty<string>(o => o.ContentType);
|
||||||
|
httpResponse.SetupGet(r => r.Body).Returns(stream);
|
||||||
|
|
||||||
|
var actionContext = CreateMockActionContext(httpResponse.Object,
|
||||||
|
requestAcceptHeader: acceptHeader,
|
||||||
|
requestContentType: "application/xml");
|
||||||
|
var input = "testInput";
|
||||||
|
var result = new ObjectResult(input);
|
||||||
|
|
||||||
|
// Set more than one formatters. The test output formatter throws on write.
|
||||||
|
result.Formatters = new List<IOutputFormatter>
|
||||||
|
{
|
||||||
|
new CannotWriteFormatter(),
|
||||||
|
new CountingFormatter(),
|
||||||
|
};
|
||||||
|
|
||||||
|
var context = new OutputFormatterContext()
|
||||||
|
{
|
||||||
|
ActionContext = actionContext,
|
||||||
|
Object = input,
|
||||||
|
DeclaredType = typeof(string)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var formatter = result.SelectFormatter(context, result.Formatters);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var countingFormatter = Assert.IsType<CountingFormatter>(formatter);
|
||||||
|
Assert.Equal(attemptedCountForCanWrite, countingFormatter.GetCanWriteCallCount());
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task
|
public async Task
|
||||||
ObjectResult_NoContentTypeSetWithNoAcceptHeadersAndNoRequestContentType_PicksFirstFormatterWhichCanWrite()
|
ObjectResult_NoContentTypeSetWithNoAcceptHeadersAndNoRequestContentType_PicksFirstFormatterWhichCanWrite()
|
||||||
|
|
@ -461,21 +507,57 @@ namespace Microsoft.AspNet.Mvc.Core.Test.ActionResults
|
||||||
return serviceCollection.BuildServiceProvider();
|
return serviceCollection.BuildServiceProvider();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class CountingFormatter : OutputFormatter
|
||||||
|
{
|
||||||
|
private int _canWriteCallsCount = 0;
|
||||||
|
|
||||||
|
public CountingFormatter()
|
||||||
|
{
|
||||||
|
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/plain"));
|
||||||
|
SupportedEncodings.Add(Encoding.GetEncoding("utf-8"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanWriteResult(OutputFormatterContext context, MediaTypeHeaderValue contentType)
|
||||||
|
{
|
||||||
|
_canWriteCallsCount++;
|
||||||
|
if (base.CanWriteResult(context, contentType))
|
||||||
|
{
|
||||||
|
var actionReturnString = context.Object as string;
|
||||||
|
if (actionReturnString != null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetCanWriteCallCount()
|
||||||
|
{
|
||||||
|
return _canWriteCallsCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task WriteResponseBodyAsync(OutputFormatterContext context)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class CannotWriteFormatter : IOutputFormatter
|
public class CannotWriteFormatter : IOutputFormatter
|
||||||
{
|
{
|
||||||
public List<Encoding> SupportedEncodings
|
public IList<Encoding> SupportedEncodings
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<MediaTypeHeaderValue> SupportedMediaTypes
|
public IList<MediaTypeHeaderValue> SupportedMediaTypes
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,6 +120,24 @@ namespace Microsoft.AspNet.Mvc.Test
|
||||||
formatterContext.SelectedContentType.RawValue);
|
formatterContext.SelectedContentType.RawValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CanWriteResult_ForNullContentType_UsesFirstEntryInSupportedContentTypes()
|
||||||
|
{
|
||||||
|
// For no accept headers,
|
||||||
|
//can write is called twice once for the request media type and once for the type match pass.
|
||||||
|
// For each adduaccept header, it is called once.
|
||||||
|
// Arrange
|
||||||
|
var context = new OutputFormatterContext();
|
||||||
|
var formatter = new TestOutputFormatter();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = formatter.CanWriteResult(context, null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result);
|
||||||
|
Assert.Equal(formatter.SupportedMediaTypes[0].RawValue, context.SelectedContentType.RawValue);
|
||||||
|
}
|
||||||
|
|
||||||
private class TestOutputFormatter : OutputFormatter
|
private class TestOutputFormatter : OutputFormatter
|
||||||
{
|
{
|
||||||
public TestOutputFormatter()
|
public TestOutputFormatter()
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,22 @@ namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
public Encoding Encoding { get; set; }
|
public Encoding Encoding { get; set; }
|
||||||
|
|
||||||
|
public IList<Encoding> SupportedEncodings
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IList<MediaTypeHeaderValue> SupportedMediaTypes
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool CanWriteResult(OutputFormatterContext context, MediaTypeHeaderValue contentType)
|
public bool CanWriteResult(OutputFormatterContext context, MediaTypeHeaderValue contentType)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
|
using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
|
||||||
using Microsoft.AspNet.Mvc.OptionDescriptors;
|
using Microsoft.AspNet.Mvc.OptionDescriptors;
|
||||||
|
|
@ -55,6 +57,22 @@ namespace Microsoft.AspNet.Mvc.Core
|
||||||
|
|
||||||
private class TestOutputFormatter : IOutputFormatter
|
private class TestOutputFormatter : IOutputFormatter
|
||||||
{
|
{
|
||||||
|
public IList<Encoding> SupportedEncodings
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IList<MediaTypeHeaderValue> SupportedMediaTypes
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool CanWriteResult(OutputFormatterContext context, MediaTypeHeaderValue contentType)
|
public bool CanWriteResult(OutputFormatterContext context, MediaTypeHeaderValue contentType)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
|
|
|
||||||
|
|
@ -266,5 +266,67 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
var body = await response.Content.ReadAsStringAsync();
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
Assert.Equal(expectedBody, body);
|
Assert.Equal(expectedBody, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("UseTheFallback_WithDefaultFormatters")]
|
||||||
|
[InlineData("UseTheFallback_UsingCustomFormatters")]
|
||||||
|
public async Task NoMatchOn_RequestContentType_FallsBackOnTypeBasedMatch_MatchFound(string actionName)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_provider, _app);
|
||||||
|
var client = server.Handler;
|
||||||
|
var expectedContentType = "application/json;charset=utf-8";
|
||||||
|
var expectedBody = "1234";
|
||||||
|
var targetUri = "http://localhost/FallbackOnTypeBasedMatch/" + actionName + "/?input=1234";
|
||||||
|
// Act
|
||||||
|
|
||||||
|
var result = await client.PostAsync(targetUri,
|
||||||
|
"1234",
|
||||||
|
"application/custom",
|
||||||
|
(request) => request.Accept = "application/custom1");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expectedContentType, result.HttpContext.Response.ContentType);
|
||||||
|
var body = await result.HttpContext.Response.ReadBodyAsStringAsync();
|
||||||
|
Assert.Equal(expectedBody, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("OverrideTheFallback_WithDefaultFormatters")]
|
||||||
|
[InlineData("OverrideTheFallback_UsingCustomFormatters")]
|
||||||
|
public async Task NoMatchOn_RequestContentType_SkipTypeMatchByAddingACustomFormatter(string actionName)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_provider, _app);
|
||||||
|
var client = server.Handler;
|
||||||
|
var targetUri = "http://localhost/FallbackOnTypeBasedMatch/" + actionName + "/?input=1234";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await client.PostAsync(targetUri,
|
||||||
|
"1234",
|
||||||
|
"application/custom",
|
||||||
|
(request) => request.Accept = "application/custom1");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(406, result.HttpContext.Response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task NoMatchOn_RequestContentType_FallsBackOnTypeBasedMatch_NoMatchFound_Returns406()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_provider, _app);
|
||||||
|
var client = server.Handler;
|
||||||
|
var targetUri = "http://localhost/FallbackOnTypeBasedMatch/FallbackGivesNoMatch/?input=1234";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await client.PostAsync(targetUri,
|
||||||
|
"1234",
|
||||||
|
"application/custom",
|
||||||
|
(request) => request.Accept = "application/custom1");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(406, result.HttpContext.Response.StatusCode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
namespace ConnegWebsite
|
||||||
|
{
|
||||||
|
public class FallbackOnTypeBasedMatchController : Controller
|
||||||
|
{
|
||||||
|
public int UseTheFallback_WithDefaultFormatters(int input)
|
||||||
|
{
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult UseTheFallback_UsingCustomFormatters(int input)
|
||||||
|
{
|
||||||
|
var objectResult = new ObjectResult(input);
|
||||||
|
|
||||||
|
// Request content type is application/custom.
|
||||||
|
// PlainTextFormatter cannot write because it does not support the type.
|
||||||
|
// JsonOutputFormatter cannot write in the first attempt because it does not support the
|
||||||
|
// request content type.
|
||||||
|
objectResult.Formatters.Add(new PlainTextFormatter());
|
||||||
|
objectResult.Formatters.Add(new JsonOutputFormatter(JsonOutputFormatter.CreateDefaultSettings(), false));
|
||||||
|
|
||||||
|
return objectResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult FallbackGivesNoMatch(int input)
|
||||||
|
{
|
||||||
|
var objectResult = new ObjectResult(input);
|
||||||
|
|
||||||
|
// Request content type is application/custom.
|
||||||
|
// PlainTextFormatter cannot write because it does not support the type.
|
||||||
|
objectResult.Formatters.Add(new PlainTextFormatter());
|
||||||
|
|
||||||
|
return objectResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult OverrideTheFallback_UsingCustomFormatters(int input)
|
||||||
|
{
|
||||||
|
var objectResult = new ObjectResult(input);
|
||||||
|
objectResult.Formatters.Add(new StopIfNoMatchOutputFormatter());
|
||||||
|
objectResult.Formatters.Add(new PlainTextFormatter());
|
||||||
|
objectResult.Formatters.Add(new JsonOutputFormatter(JsonOutputFormatter.CreateDefaultSettings(), false));
|
||||||
|
return objectResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult OverrideTheFallback_WithDefaultFormatters(int input)
|
||||||
|
{
|
||||||
|
var objectResult = new ObjectResult(input);
|
||||||
|
var formattersProvider = ActionContext.HttpContext.RequestServices.GetService<IOutputFormattersProvider>();
|
||||||
|
objectResult.Formatters.Add(new StopIfNoMatchOutputFormatter());
|
||||||
|
foreach (var formatter in formattersProvider.OutputFormatters)
|
||||||
|
{
|
||||||
|
objectResult.Formatters.Add(formatter);
|
||||||
|
}
|
||||||
|
|
||||||
|
return objectResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class StopIfNoMatchOutputFormatter : IOutputFormatter
|
||||||
|
{
|
||||||
|
public IList<Encoding> SupportedEncodings
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IList<MediaTypeHeaderValue> SupportedMediaTypes
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select if no Registered content type.
|
||||||
|
public bool CanWriteResult(OutputFormatterContext context, MediaTypeHeaderValue contentType)
|
||||||
|
{
|
||||||
|
return contentType == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task WriteAsync(OutputFormatterContext context)
|
||||||
|
{
|
||||||
|
var response = context.ActionContext.HttpContext.Response;
|
||||||
|
response.StatusCode = 406;
|
||||||
|
return Task.FromResult<bool>(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue