diff --git a/src/Microsoft.AspNet.Mvc.Core/Filters/ProducesAttribute.cs b/src/Microsoft.AspNet.Mvc.Core/Filters/ProducesAttribute.cs index 2e89971978..8d7bddaa75 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Filters/ProducesAttribute.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Filters/ProducesAttribute.cs @@ -17,6 +17,21 @@ namespace Microsoft.AspNet.Mvc [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public class ProducesAttribute : ResultFilterAttribute, IApiResponseMetadataProvider { + /// + /// Initializes an instance of . + /// + /// The of object that is going to be written in the response. + public ProducesAttribute([NotNull] Type type) + { + Type = type; + ContentTypes = new List(); + } + + /// + /// Initializes an instance of with allowed content types. + /// + /// The allowed content type for a response. + /// Additional allowed content types for a response. public ProducesAttribute([NotNull] string contentType, params string[] additionalContentTypes) { ContentTypes = GetContentTypes(contentType, additionalContentTypes); diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Filters/ProducesAttributeTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Filters/ProducesAttributeTests.cs index d056dbd575..5fa9d9c948 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Filters/ProducesAttributeTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Filters/ProducesAttributeTests.cs @@ -128,6 +128,29 @@ namespace Microsoft.AspNet.Mvc.Test ex.Message); } + [Fact] + public void ProducesAttribute_WithTypeOnly_SetsTypeProperty() + { + // Arrange + var personType = typeof(Person); + var producesAttribute = new ProducesAttribute(personType); + + // Act and Assert + Assert.NotNull(producesAttribute.Type); + Assert.Same(personType, producesAttribute.Type); + } + + [Fact] + public void ProducesAttribute_WithTypeOnly_DoesNotSetContentTypes() + { + // Arrange + var producesAttribute = new ProducesAttribute(typeof(Person)); + + // Act and Assert + Assert.NotNull(producesAttribute.ContentTypes); + Assert.Empty(producesAttribute.ContentTypes); + } + private static void ValidateMediaType(MediaTypeHeaderValue expectedMediaType, MediaTypeHeaderValue actualMediaType) { Assert.Equal(expectedMediaType.MediaType, actualMediaType.MediaType); @@ -160,5 +183,12 @@ namespace Microsoft.AspNet.Mvc.Test { return new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor()); } + + private class Person + { + public int Id { get; set; } + + public string Name { get; set; } + } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/ConnegTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/ConnegTests.cs index 2e560c4e18..61a09e159b 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/ConnegTests.cs +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/ConnegTests.cs @@ -2,14 +2,19 @@ // 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.Net; using System.Net.Http; using System.Net.Http.Headers; +using System.Runtime.Serialization; using System.Text; using System.Threading.Tasks; +using System.Xml; +using System.Xml.Serialization; using ConnegWebSite; using Microsoft.AspNet.Builder; using Microsoft.AspNet.TestHost; +using Microsoft.AspNet.Mvc.Xml; using Xunit; namespace Microsoft.AspNet.Mvc.FunctionalTests @@ -90,6 +95,48 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests Assert.Equal(expectedContentType, response.Content.Headers.ContentType); } + [Fact] + public async Task ProducesAttributeWithTypeOnly_RunsRegularContentNegotiation() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + var expectedContentType = MediaTypeHeaderValue.Parse("application/json;charset=utf-8"); + var expectedOutput = "{\"Name\":\"John\",\"Address\":\"One Microsoft Way\"}"; + + // Act + var response = await client.GetAsync("http://localhost/Home/UserInfo_ProducesWithTypeOnly"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(expectedContentType, response.Content.Headers.ContentType); + var actual = await response.Content.ReadAsStringAsync(); + Assert.Equal(expectedOutput, actual); + } + + [Fact] + public async Task ProducesAttribute_WithTypeAndContentType_UsesContentType() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml")); + var expectedContentType = MediaTypeHeaderValue.Parse("application/xml;charset=utf-8"); + var expectedOutput = "" + + "
One Microsoft Way
John
"; + + // Act + var response = await client.GetAsync("http://localhost/Home/UserInfo_ProducesWithTypeAndContentType"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(expectedContentType, response.Content.Headers.ContentType); + var actual = await response.Content.ReadAsStringAsync(); + XmlAssert.Equal(expectedOutput, actual); + } + [Fact] public async Task NoMatchingFormatter_ForTheGivenContentType_Returns406() { @@ -426,7 +473,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests { // Arrange var server = TestServer.Create(_provider, _app); - var client = server.CreateClient(); + var client = server.CreateClient(); // Act var response = await client.GetAsync("http://localhost/FormatFilter/MethodWithFormatFilter"); diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json b/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json index dd286a5276..ead5aa95ad 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json @@ -46,6 +46,7 @@ "WebApiCompatShimWebSite": "1.0.0", "Microsoft.AspNet.TestHost": "1.0.0-*", "Microsoft.AspNet.Mvc": "6.0.0-*", + "Microsoft.AspNet.Mvc.Xml": "6.0.0-*", "Microsoft.AspNet.Mvc.TestConfiguration": "1.0.0", "MvcTagHelpersWebSite": "1.0.0", "Microsoft.Framework.ConfigurationModel.Json": "1.0.0-*", diff --git a/test/WebSites/ApiExplorerWebSite/Controllers/ApiExplorerResponseTypeOverrideOnActionController.cs b/test/WebSites/ApiExplorerWebSite/Controllers/ApiExplorerResponseTypeOverrideOnActionController.cs index e32179b916..b5de7a6bd6 100644 --- a/test/WebSites/ApiExplorerWebSite/Controllers/ApiExplorerResponseTypeOverrideOnActionController.cs +++ b/test/WebSites/ApiExplorerWebSite/Controllers/ApiExplorerResponseTypeOverrideOnActionController.cs @@ -15,7 +15,7 @@ namespace ApiExplorerWebSite } [HttpGet("Action")] - [ProducesType(typeof(Customer))] + [Produces(typeof(Customer))] public object GetAction() { return null; diff --git a/test/WebSites/ApiExplorerWebSite/Controllers/ApiExplorerResponseTypeWithAttributeController.cs b/test/WebSites/ApiExplorerWebSite/Controllers/ApiExplorerResponseTypeWithAttributeController.cs index 6608d4c792..ffee8f0769 100644 --- a/test/WebSites/ApiExplorerWebSite/Controllers/ApiExplorerResponseTypeWithAttributeController.cs +++ b/test/WebSites/ApiExplorerWebSite/Controllers/ApiExplorerResponseTypeWithAttributeController.cs @@ -10,7 +10,7 @@ namespace ApiExplorerWebSite public class ApiExplorerResponseTypeWithAttributeController : Controller { [HttpGet] - [ProducesType(typeof(Customer))] + [Produces(typeof(Customer))] public void GetVoid() { } @@ -37,7 +37,7 @@ namespace ApiExplorerWebSite } [HttpGet] - [ProducesType(typeof(Customer))] // It's possible to lie about what type you return + [Produces(typeof(Customer))] // It's possible to lie about what type you return public Product GetProduct() { return null; diff --git a/test/WebSites/ApiExplorerWebSite/ProducesTypeAttribute.cs b/test/WebSites/ApiExplorerWebSite/ProducesTypeAttribute.cs deleted file mode 100644 index 24bbb8b5ea..0000000000 --- a/test/WebSites/ApiExplorerWebSite/ProducesTypeAttribute.cs +++ /dev/null @@ -1,25 +0,0 @@ -// 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 Microsoft.AspNet.Mvc; -using Microsoft.AspNet.Mvc.Description; -using Microsoft.Net.Http.Headers; - -namespace ApiExplorerWebSite -{ - public class ProducesTypeAttribute : ResultFilterAttribute, IApiResponseMetadataProvider - { - public ProducesTypeAttribute(Type type) - { - Type = type; - } - - public Type Type { get; private set; } - - public void SetContentTypes(IList contentTypes) - { - } - } -} \ No newline at end of file diff --git a/test/WebSites/ConnegWebSite/Controllers/HomeController.cs b/test/WebSites/ConnegWebSite/Controllers/HomeController.cs index e54e8e8ad0..6788a39f1c 100644 --- a/test/WebSites/ConnegWebSite/Controllers/HomeController.cs +++ b/test/WebSites/ConnegWebSite/Controllers/HomeController.cs @@ -13,6 +13,23 @@ namespace ConnegWebSite } public User UserInfo() + { + return CreateUser(); + } + + [Produces(typeof(User))] + public IActionResult UserInfo_ProducesWithTypeOnly() + { + return new ObjectResult(CreateUser()); + } + + [Produces("application/xml", Type = typeof(User))] + public IActionResult UserInfo_ProducesWithTypeAndContentType() + { + return new ObjectResult(CreateUser()); + } + + private User CreateUser() { return new User() { Name = "John", Address = "One Microsoft Way" }; }