[Fixes #1429] Content negotiation does a case-sensitive matching of media type's parameter values
This commit is contained in:
parent
2d32420f01
commit
17e4dd2bf6
|
|
@ -187,7 +187,7 @@ namespace Microsoft.AspNet.Mvc.HeaderValueAbstractions
|
|||
return false;
|
||||
}
|
||||
|
||||
if (parameterValue2 == null || !parameterValue2.Equals(parameters1[parameterKey]))
|
||||
if (!string.Equals(parameterValue2, parameters1[parameterKey], StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,6 +105,33 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
Assert.Equal(HttpStatusCode.NotAcceptable, response.StatusCode);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("ContactInfoUsingV3Format", "text/vcard; charset=utf-8; version=v3.0", "BEGIN:VCARD#FN:John Williams#END:VCARD#")]
|
||||
[InlineData("ContactInfoUsingV4Format", "text/vcard; charset=utf-8; version=v4.0", "BEGIN:VCARD#FN:John Williams#GENDER:M#END:VCARD#")]
|
||||
public async Task ProducesAttribute_WithMediaTypeHavingParameters_IsCaseInsensitiveMatch(
|
||||
string action,
|
||||
string expectedMediaType,
|
||||
string expectedResponseBody)
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.CreateClient();
|
||||
expectedResponseBody = expectedResponseBody.Replace("#", Environment.NewLine);
|
||||
|
||||
// Act
|
||||
var response = await client.GetAsync("http://localhost/ProducesWithMediaTypeParameters/" + action);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.NotNull(response.Content);
|
||||
var contentType = response.Content.Headers.ContentType;
|
||||
Assert.NotNull(contentType);
|
||||
Assert.Equal(expectedMediaType, contentType.ToString());
|
||||
|
||||
var actualResponseBody = await response.Content.ReadAsStringAsync();
|
||||
Assert.Equal(expectedResponseBody, actualResponseBody);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProducesContentAttribute_OnAction_OverridesTheValueOnClass()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -103,7 +103,12 @@ namespace Microsoft.AspNet.Mvc.HeaderValueAbstractions
|
|||
[InlineData("text/plain;", "*/*;charset=utf-8;", true)]
|
||||
[InlineData("text/plain;", "text/*;charset=utf-8;", true)]
|
||||
[InlineData("text/plain;", "text/plain;charset=utf-8;", true)]
|
||||
[InlineData("text/plain;version=v1", "text/plain;version=", false)]
|
||||
[InlineData("text/plain;version=v1", "Text/plain;Version=v1", true)]
|
||||
[InlineData("text/plain;version=v1", "tExT/plain;version=V1", true)]
|
||||
[InlineData("text/plain;version=v1", "TEXT/PLAIN;VERSION=V1", true)]
|
||||
[InlineData("text/plain;charset=utf-8;foo=bar;q=0.0", "text/plain;charset=utf-8;foo=bar;q=0.0", true)]
|
||||
[InlineData("text/plain;charset=utf-8;foo=bar;q=0.0", "text/plain;foo=bar;q=0.0;charset=utf-8", true)] // different order of parameters
|
||||
[InlineData("text/plain;charset=utf-8;foo=bar;q=0.0", "text/*;charset=utf-8;foo=bar;q=0.0", true)]
|
||||
[InlineData("text/plain;charset=utf-8;foo=bar;q=0.0", "*/*;charset=utf-8;foo=bar;q=0.0", true)]
|
||||
[InlineData("*/*;", "text/plain;charset=utf-8;foo=bar;q=0.0", false)]
|
||||
|
|
@ -111,8 +116,8 @@ namespace Microsoft.AspNet.Mvc.HeaderValueAbstractions
|
|||
[InlineData("text/plain;missingparam=4;", "text/plain;charset=utf-8;foo=bar;q=0.0", false)]
|
||||
[InlineData("text/plain;missingparam=4;", "text/*;charset=utf-8;foo=bar;q=0.0", false)]
|
||||
[InlineData("text/plain;missingparam=4;", "*/*;charset=utf-8;foo=bar;q=0.0", false)]
|
||||
public void MediaTypeHeaderValue_IsSubTypeTests(string mediaType1,
|
||||
string mediaType2,
|
||||
public void MediaTypeHeaderValue_IsSubTypeTests(string mediaType1, // Example: Formatter's supported media type
|
||||
string mediaType2, // Example: Accept header media type
|
||||
bool isMediaType1Subset)
|
||||
{
|
||||
// Arrange
|
||||
|
|
@ -132,6 +137,8 @@ namespace Microsoft.AspNet.Mvc.HeaderValueAbstractions
|
|||
[InlineData("text/plain;charset=utf-16;foo=bar", "text/json;charset=utf-16;foo=bar")]
|
||||
[InlineData("text/plain;charset=utf-16;foo=bar", "application/plain;charset=utf-16;foo=bar")]
|
||||
[InlineData("text/plain;charset=utf-16;foo=bar", "application/json;charset=utf-8;foo=bar1")]
|
||||
[InlineData("text/plain; charset=utf-16; foo=bar", "application/json;charset=utf-8;foo=bar1")]
|
||||
[InlineData("text/plain;charset = utf-16;foo = bar", "application/json;charset=utf-8;foo=bar1")]
|
||||
public void MediaTypeHeaderValue_UpdateValue_RawValueGetsUpdated(string mediaTypeValue,
|
||||
string expectedRawValue)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||
|
|
@ -11,4 +11,4 @@
|
|||
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
// 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 ConnegWebsite.Models;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
|
||||
namespace ConnegWebsite
|
||||
{
|
||||
public class ProducesWithMediaTypeParametersController : Controller
|
||||
{
|
||||
public override void OnActionExecuted(ActionExecutedContext context)
|
||||
{
|
||||
var result = context.Result as ObjectResult;
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
result.Formatters.Add(new VCardFormatter_V3());
|
||||
result.Formatters.Add(new VCardFormatter_V4());
|
||||
}
|
||||
}
|
||||
|
||||
[Produces("text/vcard;VERSION=V3.0")]
|
||||
public Contact ContactInfoUsingV3Format()
|
||||
{
|
||||
return new Contact()
|
||||
{
|
||||
Name = "John Williams",
|
||||
Gender = GenderType.Male
|
||||
};
|
||||
}
|
||||
|
||||
[Produces("text/vcard;VERSION=V4.0")]
|
||||
public Contact ContactInfoUsingV4Format()
|
||||
{
|
||||
return new Contact()
|
||||
{
|
||||
Name = "John Williams",
|
||||
Gender = GenderType.Male
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
// 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.
|
||||
|
||||
namespace ConnegWebsite.Models
|
||||
{
|
||||
public class Contact
|
||||
{
|
||||
public int ContactId { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public GenderType Gender { get; set; }
|
||||
|
||||
public string Address { get; set; }
|
||||
|
||||
public string City { get; set; }
|
||||
|
||||
public string State { get; set; }
|
||||
|
||||
public string Zip { get; set; }
|
||||
|
||||
public string Email { get; set; }
|
||||
|
||||
public string Twitter { get; set; }
|
||||
|
||||
public string Self { get; set; }
|
||||
}
|
||||
|
||||
public enum GenderType
|
||||
{
|
||||
Male,
|
||||
Female
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
// 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.IO;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ConnegWebsite.Models;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
|
||||
|
||||
namespace ConnegWebsite
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides contact information of a person through VCard format.
|
||||
/// </summary>
|
||||
public class VCardFormatter_V3 : OutputFormatter
|
||||
{
|
||||
public VCardFormatter_V3()
|
||||
{
|
||||
SupportedEncodings.Add(Encoding.UTF8);
|
||||
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/vcard;version=v3.0"));
|
||||
}
|
||||
|
||||
protected override bool CanWriteType(Type declaredType, Type runtimeType)
|
||||
{
|
||||
return typeof(Contact).GetTypeInfo().IsAssignableFrom(runtimeType.GetTypeInfo());
|
||||
}
|
||||
|
||||
public override async Task WriteResponseBodyAsync(OutputFormatterContext context)
|
||||
{
|
||||
var contact = (Contact)context.Object;
|
||||
|
||||
var builder = new StringBuilder();
|
||||
builder.AppendLine("BEGIN:VCARD");
|
||||
builder.AppendFormat("FN:{0}", contact.Name);
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("END:VCARD");
|
||||
|
||||
var responseStream = new DelegatingStream(context.ActionContext.HttpContext.Response.Body);
|
||||
using (var writer = new StreamWriter(responseStream, context.SelectedEncoding, bufferSize: 1024))
|
||||
{
|
||||
await writer.WriteAsync(builder.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
// 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.IO;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ConnegWebsite.Models;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
|
||||
|
||||
namespace ConnegWebsite
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides contact information of a person through VCard format.
|
||||
/// In version 4.0 of VCard format, Gender is a supported property.
|
||||
/// </summary>
|
||||
public class VCardFormatter_V4 : OutputFormatter
|
||||
{
|
||||
public VCardFormatter_V4()
|
||||
{
|
||||
SupportedEncodings.Add(Encoding.UTF8);
|
||||
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/vcard;version=v4.0"));
|
||||
}
|
||||
|
||||
protected override bool CanWriteType(Type declaredType, Type runtimeType)
|
||||
{
|
||||
return typeof(Contact).GetTypeInfo().IsAssignableFrom(runtimeType.GetTypeInfo());
|
||||
}
|
||||
|
||||
public override async Task WriteResponseBodyAsync(OutputFormatterContext context)
|
||||
{
|
||||
var contact = (Contact)context.Object;
|
||||
|
||||
var builder = new StringBuilder();
|
||||
builder.AppendLine("BEGIN:VCARD");
|
||||
builder.AppendFormat("FN:{0}", contact.Name);
|
||||
builder.AppendLine();
|
||||
builder.AppendFormat("GENDER:{0}", (contact.Gender == GenderType.Male) ? "M" : "F");
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("END:VCARD");
|
||||
|
||||
var responseStream = new DelegatingStream(context.ActionContext.HttpContext.Response.Body);
|
||||
using (var writer = new StreamWriter(responseStream, context.SelectedEncoding, bufferSize: 1024))
|
||||
{
|
||||
await writer.WriteAsync(builder.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue