From eb7283fced7935cde22781025a1c532f60b13e20 Mon Sep 17 00:00:00 2001 From: Ajay Bhargav Baaskaran Date: Thu, 18 Dec 2014 15:31:16 -0800 Subject: [PATCH] added BadRequest and Created Action Results with related unit and functional tests. --- Mvc.sln | 17 +- .../ActionResults/BadRequestResult.cs | 20 ++ .../ActionResults/CreatedAtActionResult.cs | 71 +++++++ .../ActionResults/CreatedAtRouteResult.cs | 74 +++++++ .../ActionResults/CreatedResult.cs | 37 ++++ .../ActionResults/ObjectResult.cs | 18 ++ src/Microsoft.AspNet.Mvc.Core/Controller.cs | 122 ++++++++++++ .../ActionResults/BadRequestResultTests.cs | 20 ++ .../CreatedAtActionResultTests.cs | 126 ++++++++++++ .../CreatedAtRouteResultTests.cs | 140 +++++++++++++ .../ActionResults/CreatedResultTests.cs | 98 ++++++++++ .../ControllerTests.cs | 179 +++++++++++++++++ .../ActionResultTests.cs | 185 ++++++++++++++++++ .../project.json | 3 +- .../ActionResultsWebSite.kproj | 20 ++ .../ActionResultsVerificationController.cs | 78 ++++++++ .../ActionResultsWebSite/Models/DummyClass.cs | 14 ++ test/WebSites/ActionResultsWebSite/Startup.cs | 34 ++++ .../ActionResultsWebSite/project.json | 19 ++ 19 files changed, 1273 insertions(+), 2 deletions(-) create mode 100644 src/Microsoft.AspNet.Mvc.Core/ActionResults/BadRequestResult.cs create mode 100644 src/Microsoft.AspNet.Mvc.Core/ActionResults/CreatedAtActionResult.cs create mode 100644 src/Microsoft.AspNet.Mvc.Core/ActionResults/CreatedAtRouteResult.cs create mode 100644 src/Microsoft.AspNet.Mvc.Core/ActionResults/CreatedResult.cs create mode 100644 test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/BadRequestResultTests.cs create mode 100644 test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/CreatedAtActionResultTests.cs create mode 100644 test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/CreatedAtRouteResultTests.cs create mode 100644 test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/CreatedResultTests.cs create mode 100644 test/Microsoft.AspNet.Mvc.FunctionalTests/ActionResultTests.cs create mode 100644 test/WebSites/ActionResultsWebSite/ActionResultsWebSite.kproj create mode 100644 test/WebSites/ActionResultsWebSite/Controllers/ActionResultsVerificationController.cs create mode 100644 test/WebSites/ActionResultsWebSite/Models/DummyClass.cs create mode 100644 test/WebSites/ActionResultsWebSite/Startup.cs create mode 100644 test/WebSites/ActionResultsWebSite/project.json diff --git a/Mvc.sln b/Mvc.sln index f1bc715fb6..a47da8f452 100644 --- a/Mvc.sln +++ b/Mvc.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.22410.0 +VisualStudioVersion = 14.0.22416.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{DAAE4C74-D06F-4874-A166-33305D2643CE}" EndProject @@ -112,6 +112,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "CompositeViewEngineWebSite" EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ValueProvidersWebSite", "test\WebSites\ValueProvidersWebSite\ValueProvidersWebSite.kproj", "{14F79E79-AE79-48FA-95DE-D794EF4EABB3}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ActionResultsWebSite", "test\WebSites\ActionResultsWebSite\ActionResultsWebSite.kproj", "{0A6BB4C0-48D3-4E7F-952B-B8917345E075}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -608,6 +610,18 @@ Global {14F79E79-AE79-48FA-95DE-D794EF4EABB3}.Release|Mixed Platforms.Build.0 = Release|Any CPU {14F79E79-AE79-48FA-95DE-D794EF4EABB3}.Release|x86.ActiveCfg = Release|Any CPU {14F79E79-AE79-48FA-95DE-D794EF4EABB3}.Release|x86.Build.0 = Release|Any CPU + {0A6BB4C0-48D3-4E7F-952B-B8917345E075}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0A6BB4C0-48D3-4E7F-952B-B8917345E075}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0A6BB4C0-48D3-4E7F-952B-B8917345E075}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {0A6BB4C0-48D3-4E7F-952B-B8917345E075}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {0A6BB4C0-48D3-4E7F-952B-B8917345E075}.Debug|x86.ActiveCfg = Debug|Any CPU + {0A6BB4C0-48D3-4E7F-952B-B8917345E075}.Debug|x86.Build.0 = Debug|Any CPU + {0A6BB4C0-48D3-4E7F-952B-B8917345E075}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0A6BB4C0-48D3-4E7F-952B-B8917345E075}.Release|Any CPU.Build.0 = Release|Any CPU + {0A6BB4C0-48D3-4E7F-952B-B8917345E075}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {0A6BB4C0-48D3-4E7F-952B-B8917345E075}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {0A6BB4C0-48D3-4E7F-952B-B8917345E075}.Release|x86.ActiveCfg = Release|Any CPU + {0A6BB4C0-48D3-4E7F-952B-B8917345E075}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -662,5 +676,6 @@ Global {B18ADE62-35DE-4A06-8E1D-EDD6245F7F4D} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C} {A853B2BA-4449-4908-A416-5A3C027FC22B} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C} {14F79E79-AE79-48FA-95DE-D794EF4EABB3} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C} + {0A6BB4C0-48D3-4E7F-952B-B8917345E075} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C} EndGlobalSection EndGlobal diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/BadRequestResult.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/BadRequestResult.cs new file mode 100644 index 0000000000..052d026a67 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/BadRequestResult.cs @@ -0,0 +1,20 @@ +// 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 Microsoft.AspNet.Mvc +{ + /// + /// A that when + /// executed will produce a Bad Request (400) response. + /// + public class BadRequestResult : HttpStatusCodeResult + { + /// + /// Creates a new instance. + /// + public BadRequestResult() + : base(400) + { + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/CreatedAtActionResult.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/CreatedAtActionResult.cs new file mode 100644 index 0000000000..764b9bbc62 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/CreatedAtActionResult.cs @@ -0,0 +1,71 @@ +// 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.Core; +using Microsoft.Framework.DependencyInjection; + +namespace Microsoft.AspNet.Mvc +{ + /// + /// An that returns a Created (201) response with a Location header. + /// + public class CreatedAtActionResult : ObjectResult + { + /// + /// Initializes a new instance of the with the values + /// provided. + /// + /// The name of the action to use for generating the URL. + /// The name of the controller to use for generating the URL. + /// The route data to use for generating the URL. + /// The value to format in the entity body. + public CreatedAtActionResult(string actionName, + string controllerName, + object routeValues, + object value) + : base(value) + { + ActionName = actionName; + ControllerName = controllerName; + RouteValues = TypeHelper.ObjectToDictionary(routeValues); + StatusCode = 201; + } + + /// + /// Gets or sets the used to generate URLs. + /// + public IUrlHelper UrlHelper { get; set; } + + /// + /// Gets the name of the action to use for generating the URL. + /// + public string ActionName { get; private set; } + + /// + /// Gets the name of the controller to use for generating the URL. + /// + public string ControllerName { get; private set; } + + /// + /// Gets the route data to use for generating the URL. + /// + public IDictionary RouteValues { get; private set; } + + /// + protected override void OnFormatting([NotNull] ActionContext context) + { + var urlHelper = UrlHelper ?? context.HttpContext.RequestServices.GetRequiredService(); + + var url = urlHelper.Action(ActionName, ControllerName, RouteValues); + + if (string.IsNullOrEmpty(url)) + { + throw new InvalidOperationException(Resources.NoRoutesMatched); + } + + context.HttpContext.Response.Headers.Add("Location", new string[] { url }); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/CreatedAtRouteResult.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/CreatedAtRouteResult.cs new file mode 100644 index 0000000000..d1e99b447d --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/CreatedAtRouteResult.cs @@ -0,0 +1,74 @@ +// 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.Core; +using Microsoft.Framework.DependencyInjection; + +namespace Microsoft.AspNet.Mvc +{ + /// + /// An that returns a Created (201) response with a Location header. + /// + public class CreatedAtRouteResult : ObjectResult + { + /// + /// Initializes a new instance of the class with the values + /// provided. + /// + /// The route data to use for generating the URL. + /// The value to format in the entity body. + public CreatedAtRouteResult(object routeValues, object value) + : this(routeName: null, routeValues: routeValues, value: value) + { + } + + /// + /// Initializes a new instance of the class with the values + /// provided. + /// + /// The name of the route to use for generating the URL. + /// The route data to use for generating the URL. + /// The value to format in the entity body. + public CreatedAtRouteResult(string routeName, + object routeValues, + object value) + : base(value) + { + RouteName = routeName; + RouteValues = TypeHelper.ObjectToDictionary(routeValues); + StatusCode = 201; + } + + /// + /// Gets or sets the used to generate URLs. + /// + public IUrlHelper UrlHelper { get; set; } + + /// + /// Gets the name of the route to use for generating the URL. + /// + public string RouteName { get; private set; } + + /// + /// Gets the route data to use for generating the URL. + /// + public IDictionary RouteValues { get; private set; } + + /// + protected override void OnFormatting([NotNull] ActionContext context) + { + var urlHelper = UrlHelper ?? context.HttpContext.RequestServices.GetRequiredService(); + + var url = urlHelper.RouteUrl(RouteName, RouteValues); + + if (string.IsNullOrEmpty(url)) + { + throw new InvalidOperationException(Resources.NoRoutesMatched); + } + + context.HttpContext.Response.Headers.Add("Location", new string[] { url }); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/CreatedResult.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/CreatedResult.cs new file mode 100644 index 0000000000..42f14a53d5 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/CreatedResult.cs @@ -0,0 +1,37 @@ +// 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; + +namespace Microsoft.AspNet.Mvc +{ + /// + /// An that returns a Created (201) response with a Location header. + /// + public class CreatedResult : ObjectResult + { + /// + /// Initializes a new instance of the class with the values + /// provided. + /// + /// The location at which the content has been created. + /// The value to format in the entity body. + public CreatedResult([NotNull] string location, object value) + : base(value) + { + Location = location; + StatusCode = 201; + } + + /// + /// Gets the location at which the content has been created. + /// + public string Location { get; private set; } + + /// + protected override void OnFormatting([NotNull] ActionContext context) + { + context.HttpContext.Response.Headers.Add("Location", new string[] { Location }); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/ObjectResult.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/ObjectResult.cs index ce3b91e1d5..e8aec35b4a 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ActionResults/ObjectResult.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/ObjectResult.cs @@ -20,6 +20,11 @@ namespace Microsoft.AspNet.Mvc public Type DeclaredType { get; set; } + /// + /// Gets or sets the HTTP status code. + /// + public int? StatusCode { get; set; } + public ObjectResult(object value) { Value = value; @@ -45,6 +50,12 @@ namespace Microsoft.AspNet.Mvc return; } + if (StatusCode != null) + { + context.HttpContext.Response.StatusCode = (int)StatusCode; + } + + OnFormatting(context); await selectedFormatter.WriteAsync(formatterContext); } @@ -216,5 +227,12 @@ namespace Microsoft.AspNet.Mvc return formatters; } + + /// + /// This method is called before the formatter writes to the output stream. + /// + protected virtual void OnFormatting([NotNull] ActionContext context) + { + } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/Controller.cs b/src/Microsoft.AspNet.Mvc.Core/Controller.cs index 2dc1125d70..ea16319d0b 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Controller.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Controller.cs @@ -617,6 +617,128 @@ namespace Microsoft.AspNet.Mvc return new HttpNotFoundResult(); } + /// + /// Creates an that produces a Bad Request (400) response. + /// + /// The created for the response. + [NonAction] + public virtual BadRequestResult HttpBadRequest() + { + return new BadRequestResult(); + } + + /// + /// Creates a object that produces a Created (201) response. + /// + /// The URI at which the content has been created. + /// The content value to format in the entity body. + /// The created for the response. + [NonAction] + public virtual CreatedResult Created([NotNull] string uri, object value) + { + return new CreatedResult(uri, value); + } + + /// + /// Creates a object that produces a Created (201) response. + /// + /// The URI at which the content has been created. + /// The content value to format in the entity body. + /// The created for the response. + [NonAction] + public virtual CreatedResult Created([NotNull] Uri uri, object value) + { + string location; + if (uri.IsAbsoluteUri) + { + location = uri.AbsoluteUri; + } + else + { + location = uri.GetComponents(UriComponents.SerializationInfoString, UriFormat.UriEscaped); + } + return new CreatedResult(location, value); + } + + /// + /// Creates a object that produces a Created (201) response. + /// + /// The name of the action to use for generating the URL. + /// The content value to format in the entity body. + /// The created for the response. + [NonAction] + public virtual CreatedAtActionResult CreatedAtAction(string actionName, object value) + { + return CreatedAtAction(actionName, routeValues: null, value: value); + } + + /// + /// Creates a object that produces a Created (201) response. + /// + /// The name of the action to use for generating the URL. + /// The route data to use for generating the URL. + /// The content value to format in the entity body. + /// The created for the response. + [NonAction] + public virtual CreatedAtActionResult CreatedAtAction(string actionName, object routeValues, object value) + { + return CreatedAtAction(actionName, controllerName: null, routeValues: routeValues, value: value); + } + + /// + /// Creates a object that produces a Created (201) response. + /// + /// The name of the action to use for generating the URL. + /// The name of the controller to use for generating the URL. + /// The route data to use for generating the URL. + /// The content value to format in the entity body. + /// The created for the response. + [NonAction] + public virtual CreatedAtActionResult CreatedAtAction(string actionName, + string controllerName, + object routeValues, + object value) + { + return new CreatedAtActionResult(actionName, controllerName, routeValues, value); + } + + /// + /// Creates a object that produces a Created (201) response. + /// + /// The name of the route to use for generating the URL. + /// The content value to format in the entity body. + /// The created for the response. + [NonAction] + public virtual CreatedAtRouteResult CreatedAtRoute(string routeName, object value) + { + return CreatedAtRoute(routeName, routeValues: null, value: value); + } + + /// + /// Creates a object that produces a Created (201) response. + /// + /// The route data to use for generating the URL. + /// The content value to format in the entity body. + /// The created for the response. + [NonAction] + public virtual CreatedAtRouteResult CreatedAtRoute(object routeValues, object value) + { + return CreatedAtRoute(routeName: null, routeValues: routeValues, value: value); + } + + /// + /// Creates a object that produces a Created (201) response. + /// + /// The name of the route to use for generating the URL. + /// The route data to use for generating the URL. + /// The content value to format in the entity body. + /// The created for the response. + [NonAction] + public virtual CreatedAtRouteResult CreatedAtRoute(string routeName, object routeValues, object value) + { + return new CreatedAtRouteResult(routeName, routeValues, value); + } + /// /// Called before the action method is invoked. /// diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/BadRequestResultTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/BadRequestResultTests.cs new file mode 100644 index 0000000000..fbb5cbb18c --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/BadRequestResultTests.cs @@ -0,0 +1,20 @@ +// 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 Xunit; + +namespace Microsoft.AspNet.Mvc +{ + public class BadRequestResultTests + { + [Fact] + public void BadRequestResult_InitializesStatusCode() + { + // Arrange & act + var badRequest = new BadRequestResult(); + + // Assert + Assert.Equal(400, badRequest.StatusCode); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/CreatedAtActionResultTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/CreatedAtActionResultTests.cs new file mode 100644 index 0000000000..fab57bcedd --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/CreatedAtActionResultTests.cs @@ -0,0 +1,126 @@ +// 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.Threading.Tasks; +using Microsoft.AspNet.Http; +using Microsoft.AspNet.PipelineCore.Collections; +using Microsoft.AspNet.Routing; +using Microsoft.AspNet.Testing; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Mvc +{ + public class CreatedAtActionResultTests + { + [Fact] + public async Task CreatedAtActionResult_ReturnsStatusCode_SetsLocationHeader() + { + // Arrange + var expectedUrl = "testAction"; + var response = GetMockedHttpResponseObject(); + var httpContext = GetHttpContext(response); + var actionContext = GetActionContext(httpContext); + var urlHelper = GetMockUrlHelper(expectedUrl); + + // Act + var result = new CreatedAtActionResult( + actionName: expectedUrl, + controllerName: null, + routeValues: null, + value: null); + + result.UrlHelper = urlHelper; + await result.ExecuteResultAsync(actionContext); + + // Assert + Assert.Equal(201, response.StatusCode); + Assert.Equal(expectedUrl, response.Headers["Location"]); + } + + [Fact] + public async Task CreatedAtActionResult_ThrowsOnNullUrl() + { + // Arrange + var response = GetMockedHttpResponseObject(); + var httpContext = GetHttpContext(response); + var actionContext = GetActionContext(httpContext); + var urlHelper = GetMockUrlHelper(returnValue: null); + + var result = new CreatedAtActionResult( + actionName: null, + controllerName: null, + routeValues: null, + value: null); + + result.UrlHelper = urlHelper; + + // Act & Assert + await ExceptionAssert.ThrowsAsync( + async () => await result.ExecuteResultAsync(actionContext), + "No route matches the supplied values."); + } + + private static HttpResponse GetMockedHttpResponseObject() + { + var stream = new MemoryStream(); + var httpResponse = new Mock(); + httpResponse.SetupProperty(o => o.StatusCode); + httpResponse.Setup(o => o.Headers).Returns( + new HeaderDictionary(new Dictionary())); + httpResponse.SetupGet(o => o.Body).Returns(stream); + return httpResponse.Object; + } + + private static ActionContext GetActionContext(HttpContext httpContext) + { + var routeData = new RouteData(); + routeData.Routers.Add(Mock.Of()); + + return new ActionContext(httpContext, + routeData, + new ActionDescriptor()); + } + + private static HttpContext GetHttpContext(HttpResponse response) + { + var httpContext = new Mock(); + + httpContext.Setup(o => o.Response) + .Returns(response); + httpContext.Setup(o => o.RequestServices.GetService(typeof(IOutputFormattersProvider))) + .Returns(new TestOutputFormatterProvider()); + httpContext.Setup(o => o.Request.PathBase) + .Returns(new PathString("")); + + return httpContext.Object; + } + + private static IUrlHelper GetMockUrlHelper(string returnValue) + { + var urlHelper = new Mock(); + urlHelper.Setup(o => o.Action(It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny())).Returns(returnValue); + + return urlHelper.Object; + } + + private class TestOutputFormatterProvider : IOutputFormattersProvider + { + public IReadOnlyList OutputFormatters + { + get + { + return new List() + { + new TextPlainFormatter(), + new JsonOutputFormatter() + }; + } + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/CreatedAtRouteResultTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/CreatedAtRouteResultTests.cs new file mode 100644 index 0000000000..d7bfe56946 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/CreatedAtRouteResultTests.cs @@ -0,0 +1,140 @@ +// 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.Threading.Tasks; +using Microsoft.AspNet.Http; +using Microsoft.AspNet.PipelineCore.Collections; +using Microsoft.AspNet.Routing; +using Microsoft.AspNet.Testing; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Mvc +{ + public class CreatedAtRouteResultTests + { + public static IEnumerable CreatedAtRouteData + { + get + { + yield return new object[] { null }; + yield return + new object[] { + new Dictionary() { { "hello", "world" } } + }; + yield return + new object[] { + new RouteValueDictionary(new Dictionary() { + { "test", "case" }, + { "sample", "route" } + }) + }; + } + } + + [Theory] + [MemberData(nameof(CreatedAtRouteData))] + public async Task CreatedAtRouteResult_ReturnsStatusCode_SetsLocationHeader(object values) + { + // Arrange + var expectedUrl = "testAction"; + var response = GetMockedHttpResponseObject(); + var httpContext = GetHttpContext(response); + var actionContext = GetActionContext(httpContext); + var urlHelper = GetMockUrlHelper(expectedUrl); + + // Act + var result = new CreatedAtRouteResult(routeName: null, routeValues: values, value: null); + result.UrlHelper = urlHelper; + await result.ExecuteResultAsync(actionContext); + + // Assert + Assert.Equal(201, response.StatusCode); + Assert.Equal(expectedUrl, response.Headers["Location"]); + } + + [Fact] + public async Task CreatedAtRouteResult_ThrowsOnNullUrl() + { + // Arrange + var response = GetMockedHttpResponseObject(); + var httpContext = GetHttpContext(response); + var actionContext = GetActionContext(httpContext); + var urlHelper = GetMockUrlHelper(returnValue: null); + + var result = new CreatedAtRouteResult( + routeName: null, + routeValues: new Dictionary(), + value: null); + + result.UrlHelper = urlHelper; + + // Act & Assert + await ExceptionAssert.ThrowsAsync( + async () => await result.ExecuteResultAsync(actionContext), + "No route matches the supplied values."); + } + + private static HttpResponse GetMockedHttpResponseObject() + { + var stream = new MemoryStream(); + var httpResponse = new Mock(); + httpResponse.SetupProperty(o => o.StatusCode); + httpResponse.Setup(o => o.Headers).Returns( + new HeaderDictionary(new Dictionary())); + httpResponse.SetupGet(o => o.Body).Returns(stream); + return httpResponse.Object; + } + + private static ActionContext GetActionContext(HttpContext httpContext) + { + var routeData = new RouteData(); + routeData.Routers.Add(Mock.Of()); + + return new ActionContext(httpContext, + routeData, + new ActionDescriptor()); + } + + private static HttpContext GetHttpContext(HttpResponse response) + { + var httpContext = new Mock(); + + httpContext.Setup(o => o.Response) + .Returns(response); + httpContext.Setup(o => o.RequestServices.GetService(typeof(IOutputFormattersProvider))) + .Returns(new TestOutputFormatterProvider()); + httpContext.Setup(o => o.Request.PathBase) + .Returns(new PathString("")); + + return httpContext.Object; + } + + private static IUrlHelper GetMockUrlHelper(string returnValue) + { + var urlHelper = new Mock(); + urlHelper.Setup(o => o.RouteUrl(It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny())).Returns(returnValue); + + return urlHelper.Object; + } + + private class TestOutputFormatterProvider : IOutputFormattersProvider + { + public IReadOnlyList OutputFormatters + { + get + { + return new List() + { + new TextPlainFormatter(), + new JsonOutputFormatter() + }; + } + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/CreatedResultTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/CreatedResultTests.cs new file mode 100644 index 0000000000..f1f3fc7a44 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/CreatedResultTests.cs @@ -0,0 +1,98 @@ +// 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.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Microsoft.AspNet.Http; +using Microsoft.AspNet.PipelineCore.Collections; +using Microsoft.AspNet.Routing; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Mvc +{ + public class CreatedResultTests + { + [Fact] + public void CreatedResult_SetsLocation() + { + // Arrange + var location = "http://test/location"; + + // Act + var result = new CreatedResult(location, "testInput"); + + // Assert + Assert.Same(location, result.Location); + } + + [Fact] + public async Task CreatedResult_ReturnsStatusCode_SetsLocationHeader() + { + // Arrange + var location = "/test/"; + var response = GetMockedHttpResponseObject(); + var httpContext = GetHttpContext(response); + var actionContext = GetActionContext(httpContext); + var result = new CreatedResult(location, "testInput"); + + // Act + await result.ExecuteResultAsync(actionContext); + + // Assert + Assert.Equal(201, response.StatusCode); + Assert.Equal(location, response.Headers["Location"]); + } + + private static HttpResponse GetMockedHttpResponseObject() + { + var stream = new MemoryStream(); + var httpResponse = new Mock(); + httpResponse.SetupProperty(o => o.StatusCode); + httpResponse.Setup(o => o.Headers).Returns( + new HeaderDictionary(new Dictionary())); + httpResponse.SetupGet(o => o.Body).Returns(stream); + return httpResponse.Object; + } + + private static ActionContext GetActionContext(HttpContext httpContext) + { + var routeData = new RouteData(); + routeData.Routers.Add(Mock.Of()); + + return new ActionContext(httpContext, + routeData, + new ActionDescriptor()); + } + + private static HttpContext GetHttpContext(HttpResponse response) + { + var httpContext = new Mock(); + + httpContext.Setup(o => o.Response) + .Returns(response); + httpContext.Setup(o => o.RequestServices.GetService(typeof(IOutputFormattersProvider))) + .Returns(new TestOutputFormatterProvider()); + httpContext.Setup(o => o.Request.PathBase) + .Returns(new PathString("")); + + return httpContext.Object; + } + + private class TestOutputFormatterProvider : IOutputFormattersProvider + { + public IReadOnlyList OutputFormatters + { + get + { + return new List() + { + new TextPlainFormatter(), + new JsonOutputFormatter() + }; + } + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerTests.cs index e097c02950..55435cfc16 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerTests.cs @@ -373,6 +373,171 @@ namespace Microsoft.AspNet.Mvc.Test Assert.Equal(expected, resultPermanent.RouteValues); } + [Fact] + public void Created_WithStringParameter_SetsCreatedLocation() + { + // Arrange + var controller = new Controller(); + var uri = "http://test/url"; + + // Act + var result = controller.Created(uri, null); + + // Assert + Assert.IsType(result); + Assert.Equal(201, result.StatusCode); + Assert.Same(uri, result.Location); + } + + [Fact] + public void Created_WithAbsoluteUriParameter_SetsCreatedLocation() + { + // Arrange + var controller = new Controller(); + var uri = new Uri("http://test/url"); + + // Act + var result = controller.Created(uri, null); + + // Assert + Assert.IsType(result); + Assert.Equal(201, result.StatusCode); + Assert.Equal(uri.OriginalString, result.Location); + } + + [Fact] + public void Created_WithRelativeUriParameter_SetsCreatedLocation() + { + // Arrange + var controller = new Controller(); + var uri = new Uri("/test/url", UriKind.Relative); + + // Act + var result = controller.Created(uri, null); + + // Assert + Assert.IsType(result); + Assert.Equal(201, result.StatusCode); + Assert.Equal(uri.OriginalString, result.Location); + } + + [Fact] + public void CreatedAtAction_WithParameterActionName_SetsResultActionName() + { + // Arrange + var controller = new Controller(); + + // Act + var result = controller.CreatedAtAction("SampleAction", null); + + // Assert + Assert.IsType(result); + Assert.Equal(201, result.StatusCode); + Assert.Equal("SampleAction", result.ActionName); + } + + [Theory] + [InlineData("")] + [InlineData(null)] + [InlineData("SampleController")] + public void CreatedAtAction_WithActionControllerAndNullRouteValue_SetsSameValue( + string controllerName) + { + // Arrange + var controller = new Controller(); + + // Act + var result = controller.CreatedAtAction("SampleAction", controllerName, null, null); + + // Assert + Assert.IsType(result); + Assert.Equal(201, result.StatusCode); + Assert.Equal("SampleAction", result.ActionName); + Assert.Equal(controllerName, result.ControllerName); + } + + [Fact] + public void CreatedAtAction_WithActionControllerRouteValues_SetsSameValues() + { + // Arrange + var controller = new Controller(); + var expected = new Dictionary + { + { "test", "case" }, + { "sample", "route" }, + }; + + // Act + var result = controller.CreatedAtAction( + "SampleAction", + "SampleController", + new RouteValueDictionary(expected), null); + + // Assert + Assert.IsType(result); + Assert.Equal(201, result.StatusCode); + Assert.Equal("SampleAction", result.ActionName); + Assert.Equal("SampleController", result.ControllerName); + Assert.Equal(expected, result.RouteValues); + } + + [Fact] + public void CreatedAtRoute_WithParameterRouteName_SetsResultSameRouteName() + { + // Arrange + var controller = new Controller(); + var routeName = "SampleRoute"; + + // Act + var result = controller.CreatedAtRoute(routeName, null); + + // Assert + Assert.IsType(result); + Assert.Same(routeName, result.RouteName); + } + + [Fact] + public void CreatedAtRoute_WithParameterRouteValues_SetsResultSameRouteValues() + { + // Arrange + var controller = new Controller(); + var expected = new Dictionary + { + { "test", "case" }, + { "sample", "route" }, + }; + + // Act + var result = controller.CreatedAtRoute(new RouteValueDictionary(expected), null); + + // Assert + Assert.IsType(result); + Assert.Equal(201, result.StatusCode); + Assert.Equal(expected, result.RouteValues); + } + + [Fact] + public void CreatedAtRoute_WithParameterRouteNameAndValues_SetsResultSameProperties() + { + // Arrange + var controller = new Controller(); + var routeName = "SampleRoute"; + var expected = new Dictionary + { + { "test", "case" }, + { "sample", "route" }, + }; + + // Act + var result = controller.CreatedAtRoute(routeName, new RouteValueDictionary(expected), null); + + // Assert + Assert.IsType(result); + Assert.Equal(201, result.StatusCode); + Assert.Same(routeName, result.RouteName); + Assert.Equal(expected, result.RouteValues); + } + [Fact] public void File_WithContents() { @@ -490,6 +655,20 @@ namespace Microsoft.AspNet.Mvc.Test Assert.Equal(404, result.StatusCode); } + [Fact] + public void BadRequest_SetsStatusCode() + { + // Arrange + var controller = new Controller(); + + // Act + var result = controller.HttpBadRequest(); + + // Assert + Assert.IsType(result); + Assert.Equal(400, result.StatusCode); + } + [Theory] [MemberData(nameof(PublicNormalMethodsFromController))] public void NonActionAttribute_IsOnEveryPublicNormalMethodFromController(MethodInfo method) diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/ActionResultTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/ActionResultTests.cs new file mode 100644 index 0000000000..68e4bc31b1 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/ActionResultTests.cs @@ -0,0 +1,185 @@ +// 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.Net; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using ActionResultsWebSite; +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.TestHost; +using Xunit; + +namespace Microsoft.AspNet.Mvc.FunctionalTests +{ + public class ActionResultTests + { + private readonly IServiceProvider _provider = TestHelper.CreateServices("ActionResultsWebSite"); + private readonly Action _app = new Startup().Configure; + + [Fact] + public async Task BadRequestResult_CanBeReturned() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + var input = "{\"SampleInt\":10}"; + + var request = new HttpRequestMessage( + HttpMethod.Post, + "http://localhost/ActionResultsVerification/GetBadResult"); + + request.Content = new StringContent(input, Encoding.UTF8, "application/json"); + + // Act + var response = await client.SendAsync(request); + + // Assert + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + Assert.Equal("", await response.Content.ReadAsStringAsync()); + } + + [Fact] + public async Task CreatedResult_SetsRelativePathInLocationHeader() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + var request = new HttpRequestMessage( + HttpMethod.Post, + "http://localhost/ActionResultsVerification/GetCreatedRelative"); + + // Act + var response = await client.SendAsync(request); + + // Assert + Assert.Equal(HttpStatusCode.Created, response.StatusCode); + Assert.Equal("1", response.Headers.Location.OriginalString); + Assert.Equal("{\"SampleInt\":10,\"SampleString\":\"Foo\"}", await response.Content.ReadAsStringAsync()); + } + + [Fact] + public async Task CreatedResult_SetsAbsolutePathInLocationHeader() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + var request = new HttpRequestMessage( + HttpMethod.Post, + "http://localhost/ActionResultsVerification/GetCreatedAbsolute"); + + // Act + var response = await client.SendAsync(request); + + // Assert + Assert.Equal(HttpStatusCode.Created, response.StatusCode); + Assert.Equal("/ActionResultsVerification/GetDummy/1", response.Headers.Location.OriginalString); + Assert.Equal("{\"SampleInt\":10,\"SampleString\":\"Foo\"}", await response.Content.ReadAsStringAsync()); + } + + [Fact] + public async Task CreatedResult_SetsQualifiedPathInLocationHeader() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + var request = new HttpRequestMessage( + HttpMethod.Post, + "http://localhost/ActionResultsVerification/GetCreatedQualified"); + + // Act + var response = await client.SendAsync(request); + + // Assert + Assert.Equal(HttpStatusCode.Created, response.StatusCode); + Assert.Equal( + "http://localhost/ActionResultsVerification/GetDummy/1", + response.Headers.Location.OriginalString); + Assert.Equal("{\"SampleInt\":10,\"SampleString\":\"Foo\"}", await response.Content.ReadAsStringAsync()); + } + + [Fact] + public async Task CreatedResult_SetsUriInLocationHeader() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + var request = new HttpRequestMessage( + HttpMethod.Post, + "http://localhost/ActionResultsVerification/GetCreatedUri"); + + // Act + var response = await client.SendAsync(request); + + // Assert + Assert.Equal(HttpStatusCode.Created, response.StatusCode); + Assert.Equal("/ActionResultsVerification/GetDummy/1", response.Headers.Location.OriginalString); + Assert.Equal("{\"SampleInt\":10,\"SampleString\":\"Foo\"}", await response.Content.ReadAsStringAsync()); + } + + [Fact] + public async Task CreatedAtActionResult_GeneratesUri_WithActionAndController() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + var request = new HttpRequestMessage( + HttpMethod.Post, + "http://localhost/ActionResultsVerification/GetCreatedAtAction"); + + // Act + var response = await client.SendAsync(request); + + // Assert + Assert.Equal(HttpStatusCode.Created, response.StatusCode); + Assert.Equal("/ActionResultsVerification/GetDummy/1", response.Headers.Location.OriginalString); + Assert.Equal("{\"SampleInt\":10,\"SampleString\":\"Foo\"}", await response.Content.ReadAsStringAsync()); + } + + [Fact] + public async Task CreatedAtRouteResult_GeneratesUri_WithRouteValues() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + var request = new HttpRequestMessage( + HttpMethod.Post, + "http://localhost/ActionResultsVerification/GetCreatedAtRoute"); + + // Act + var response = await client.SendAsync(request); + + // Assert + Assert.Equal(HttpStatusCode.Created, response.StatusCode); + Assert.Equal("/ActionResultsVerification/GetDummy/1", response.Headers.Location.OriginalString); + Assert.Equal("{\"SampleInt\":10,\"SampleString\":\"Foo\"}", await response.Content.ReadAsStringAsync()); + } + + [Fact] + public async Task CreatedAtRouteResult_GeneratesUri_WithRouteName() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + var request = new HttpRequestMessage( + HttpMethod.Post, + "http://localhost/ActionResultsVerification/GetCreatedAtRouteWithRouteName"); + + // Act + var response = await client.SendAsync(request); + + // Assert + Assert.Equal(HttpStatusCode.Created, response.StatusCode); + Assert.Equal("/foo/ActionResultsVerification/GetDummy/1", response.Headers.Location.OriginalString); + Assert.Equal("{\"SampleInt\":10,\"SampleString\":\"Foo\"}", await response.Content.ReadAsStringAsync()); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json b/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json index 5f50ff8f3b..13f7590d1c 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json @@ -3,6 +3,7 @@ "warningsAsErrors": "true" }, "dependencies": { + "ActionResultsWebSite": "1.0.0", "ActivatorWebSite": "1.0.0", "AddServicesWebSite": "1.0.0", "AntiForgeryWebSite": "1.0.0", @@ -21,7 +22,7 @@ "RoutingWebSite": "1.0.0", "RazorWebSite": "1.0.0", "RazorInstrumentationWebsite": "1.0.0", - "RazorViewEngineOptionsWebsite": "1.0.0", + "RazorViewEngineOptionsWebsite": "1.0.0", "RequestServicesWebSite": "1.0.0", "TagHelperSample.Web": "1.0.0", "TagHelpersWebSite": "1.0.0", diff --git a/test/WebSites/ActionResultsWebSite/ActionResultsWebSite.kproj b/test/WebSites/ActionResultsWebSite/ActionResultsWebSite.kproj new file mode 100644 index 0000000000..38f9273e1c --- /dev/null +++ b/test/WebSites/ActionResultsWebSite/ActionResultsWebSite.kproj @@ -0,0 +1,20 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 0a6bb4c0-48d3-4e7f-952b-b8917345e075 + + + + + + + 2.0 + 41642 + + + \ No newline at end of file diff --git a/test/WebSites/ActionResultsWebSite/Controllers/ActionResultsVerificationController.cs b/test/WebSites/ActionResultsWebSite/Controllers/ActionResultsVerificationController.cs new file mode 100644 index 0000000000..bffd5cd400 --- /dev/null +++ b/test/WebSites/ActionResultsWebSite/Controllers/ActionResultsVerificationController.cs @@ -0,0 +1,78 @@ +// 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 Microsoft.AspNet.Mvc; + +namespace ActionResultsWebSite +{ + public class ActionResultsVerificationController : Controller + { + public IActionResult Index([FromBody]DummyClass test) + { + if (!ModelState.IsValid) + { + return new BadRequestResult(); + } + + return Content("Hello World!"); + } + + public IActionResult GetBadResult() + { + return new BadRequestResult(); + } + + public IActionResult GetCreatedRelative() + { + return Created("1", CreateDummy()); + } + + public IActionResult GetCreatedAbsolute() + { + return Created("/ActionResultsVerification/GetDummy/1", CreateDummy()); + } + + public IActionResult GetCreatedQualified() + { + return Created("http://localhost/ActionResultsVerification/GetDummy/1", CreateDummy()); + } + + public IActionResult GetCreatedUri() + { + return Created(new Uri("/ActionResultsVerification/GetDummy/1", UriKind.Relative), CreateDummy()); + } + + public IActionResult GetCreatedAtAction() + { + var values = new { id = 1 }; + return CreatedAtAction("GetDummy", "ActionResultsVerification", values, CreateDummy()); + } + + public IActionResult GetCreatedAtRoute() + { + var values = new { controller = "ActionResultsVerification", Action = "GetDummy", id = 1 }; + return CreatedAtRoute(null, values, CreateDummy()); + } + + public IActionResult GetCreatedAtRouteWithRouteName() + { + var values = new { controller = "ActionResultsVerification", Action = "GetDummy", id = 1 }; + return CreatedAtRoute("custom-route", values, CreateDummy()); + } + + public DummyClass GetDummy(int id) + { + return CreateDummy(); + } + + private DummyClass CreateDummy() + { + return new DummyClass() + { + SampleInt = 10, + SampleString = "Foo" + }; + } + } +} \ No newline at end of file diff --git a/test/WebSites/ActionResultsWebSite/Models/DummyClass.cs b/test/WebSites/ActionResultsWebSite/Models/DummyClass.cs new file mode 100644 index 0000000000..f9a08ad3dd --- /dev/null +++ b/test/WebSites/ActionResultsWebSite/Models/DummyClass.cs @@ -0,0 +1,14 @@ +// 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.ComponentModel.DataAnnotations; + +namespace ActionResultsWebSite +{ + public class DummyClass + { + public int SampleInt { get; set; } + + public string SampleString { get; set; } + } +} \ No newline at end of file diff --git a/test/WebSites/ActionResultsWebSite/Startup.cs b/test/WebSites/ActionResultsWebSite/Startup.cs new file mode 100644 index 0000000000..3b7e8f4020 --- /dev/null +++ b/test/WebSites/ActionResultsWebSite/Startup.cs @@ -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. + +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.Routing; +using Microsoft.Framework.DependencyInjection; + +namespace ActionResultsWebSite +{ + public class Startup + { + public void Configure(IApplicationBuilder app) + { + var configuration = app.GetTestConfiguration(); + + app.UseServices(services => + { + services.AddMvc(configuration); + }); + + app.UseMvc(routes => + { + routes.MapRoute( + name: "default", + template: "{controller}/{action}/{id?}", + defaults: new { controller = "ActionResultsVerification", action = "Index" }); + + routes.MapRoute( + name: "custom-route", + template: "foo/{controller}/{action}/{id?}"); + }); + } + } +} \ No newline at end of file diff --git a/test/WebSites/ActionResultsWebSite/project.json b/test/WebSites/ActionResultsWebSite/project.json new file mode 100644 index 0000000000..464f3bbc61 --- /dev/null +++ b/test/WebSites/ActionResultsWebSite/project.json @@ -0,0 +1,19 @@ +{ + "commands": { + "web": "Microsoft.AspNet.Hosting server=Microsoft.AspNet.Server.WebListener server.urls=http://localhost:5001", + "kestrel": "Microsoft.AspNet.Hosting --server Kestrel --server.urls http://localhost:5000" + }, + "dependencies": { + "Kestrel": "1.0.0-*", + "Microsoft.AspNet.Mvc": "6.0.0-*", + "Microsoft.AspNet.Mvc.TestConfiguration": "1.0.0", + "Microsoft.AspNet.Server.IIS": "1.0.0-*", + "Microsoft.AspNet.Server.WebListener": "1.0.0-*", + "Microsoft.AspNet.StaticFiles": "1.0.0-*" + }, + "frameworks": { + "aspnet50": { }, + "aspnetcore50": { } + }, + "webroot": "wwwroot" +} \ No newline at end of file