added BadRequest and Created Action Results with related unit and functional tests.

This commit is contained in:
Ajay Bhargav Baaskaran 2014-12-18 15:31:16 -08:00
parent 9ea5350271
commit eb7283fced
19 changed files with 1273 additions and 2 deletions

17
Mvc.sln
View File

@ -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

View File

@ -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
{
/// <summary>
/// A <see cref="HttpStatusCodeResult"/> that when
/// executed will produce a Bad Request (400) response.
/// </summary>
public class BadRequestResult : HttpStatusCodeResult
{
/// <summary>
/// Creates a new <see cref="BadRequestResult"/> instance.
/// </summary>
public BadRequestResult()
: base(400)
{
}
}
}

View File

@ -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
{
/// <summary>
/// An <see cref="ActionResult"/> that returns a Created (201) response with a Location header.
/// </summary>
public class CreatedAtActionResult : ObjectResult
{
/// <summary>
/// Initializes a new instance of the <see cref="CreatedAtActionResult"/> with the values
/// provided.
/// </summary>
/// <param name="actionName">The name of the action to use for generating the URL.</param>
/// <param name="controllerName">The name of the controller to use for generating the URL.</param>
/// <param name="routeValues">The route data to use for generating the URL.</param>
/// <param name="value">The value to format in the entity body.</param>
public CreatedAtActionResult(string actionName,
string controllerName,
object routeValues,
object value)
: base(value)
{
ActionName = actionName;
ControllerName = controllerName;
RouteValues = TypeHelper.ObjectToDictionary(routeValues);
StatusCode = 201;
}
/// <summary>
/// Gets or sets the <see cref="IUrlHelper" /> used to generate URLs.
/// </summary>
public IUrlHelper UrlHelper { get; set; }
/// <summary>
/// Gets the name of the action to use for generating the URL.
/// </summary>
public string ActionName { get; private set; }
/// <summary>
/// Gets the name of the controller to use for generating the URL.
/// </summary>
public string ControllerName { get; private set; }
/// <summary>
/// Gets the route data to use for generating the URL.
/// </summary>
public IDictionary<string, object> RouteValues { get; private set; }
/// <inheritdoc />
protected override void OnFormatting([NotNull] ActionContext context)
{
var urlHelper = UrlHelper ?? context.HttpContext.RequestServices.GetRequiredService<IUrlHelper>();
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 });
}
}
}

View File

@ -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
{
/// <summary>
/// An <see cref="ActionResult"/> that returns a Created (201) response with a Location header.
/// </summary>
public class CreatedAtRouteResult : ObjectResult
{
/// <summary>
/// Initializes a new instance of the <see cref="CreatedAtRouteResult"/> class with the values
/// provided.
/// </summary>
/// <param name="routeValues">The route data to use for generating the URL.</param>
/// <param name="value">The value to format in the entity body.</param>
public CreatedAtRouteResult(object routeValues, object value)
: this(routeName: null, routeValues: routeValues, value: value)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CreatedAtRouteResult"/> class with the values
/// provided.
/// </summary>
/// <param name="routeName">The name of the route to use for generating the URL.</param>
/// <param name="routeValues">The route data to use for generating the URL.</param>
/// <param name="value">The value to format in the entity body.</param>
public CreatedAtRouteResult(string routeName,
object routeValues,
object value)
: base(value)
{
RouteName = routeName;
RouteValues = TypeHelper.ObjectToDictionary(routeValues);
StatusCode = 201;
}
/// <summary>
/// Gets or sets the <see cref="IUrlHelper" /> used to generate URLs.
/// </summary>
public IUrlHelper UrlHelper { get; set; }
/// <summary>
/// Gets the name of the route to use for generating the URL.
/// </summary>
public string RouteName { get; private set; }
/// <summary>
/// Gets the route data to use for generating the URL.
/// </summary>
public IDictionary<string, object> RouteValues { get; private set; }
/// <inheritdoc />
protected override void OnFormatting([NotNull] ActionContext context)
{
var urlHelper = UrlHelper ?? context.HttpContext.RequestServices.GetRequiredService<IUrlHelper>();
var url = urlHelper.RouteUrl(RouteName, RouteValues);
if (string.IsNullOrEmpty(url))
{
throw new InvalidOperationException(Resources.NoRoutesMatched);
}
context.HttpContext.Response.Headers.Add("Location", new string[] { url });
}
}
}

View File

@ -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
{
/// <summary>
/// An <see cref="ActionResult"/> that returns a Created (201) response with a Location header.
/// </summary>
public class CreatedResult : ObjectResult
{
/// <summary>
/// Initializes a new instance of the <see cref="CreatedResult"/> class with the values
/// provided.
/// </summary>
/// <param name="location">The location at which the content has been created.</param>
/// <param name="value">The value to format in the entity body.</param>
public CreatedResult([NotNull] string location, object value)
: base(value)
{
Location = location;
StatusCode = 201;
}
/// <summary>
/// Gets the location at which the content has been created.
/// </summary>
public string Location { get; private set; }
/// <inheritdoc />
protected override void OnFormatting([NotNull] ActionContext context)
{
context.HttpContext.Response.Headers.Add("Location", new string[] { Location });
}
}
}

View File

@ -20,6 +20,11 @@ namespace Microsoft.AspNet.Mvc
public Type DeclaredType { get; set; }
/// <summary>
/// Gets or sets the HTTP status code.
/// </summary>
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;
}
/// <summary>
/// This method is called before the formatter writes to the output stream.
/// </summary>
protected virtual void OnFormatting([NotNull] ActionContext context)
{
}
}
}

View File

@ -617,6 +617,128 @@ namespace Microsoft.AspNet.Mvc
return new HttpNotFoundResult();
}
/// <summary>
/// Creates an <see cref="BadRequestResult"/> that produces a Bad Request (400) response.
/// </summary>
/// <returns>The created <see cref="BadRequestResult"/> for the response.</returns>
[NonAction]
public virtual BadRequestResult HttpBadRequest()
{
return new BadRequestResult();
}
/// <summary>
/// Creates a <see cref="CreatedResult"/> object that produces a Created (201) response.
/// </summary>
/// <param name="uri">The URI at which the content has been created.</param>
/// <param name="value">The content value to format in the entity body.</param>
/// <returns>The created <see cref="CreatedResult"/> for the response.</returns>
[NonAction]
public virtual CreatedResult Created([NotNull] string uri, object value)
{
return new CreatedResult(uri, value);
}
/// <summary>
/// Creates a <see cref="CreatedResult"/> object that produces a Created (201) response.
/// </summary>
/// <param name="uri">The URI at which the content has been created.</param>
/// <param name="value">The content value to format in the entity body.</param>
/// <returns>The created <see cref="CreatedResult"/> for the response.</returns>
[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);
}
/// <summary>
/// Creates a <see cref="CreatedAtActionResult"/> object that produces a Created (201) response.
/// </summary>
/// <param name="actionName">The name of the action to use for generating the URL.</param>
/// <param name="value">The content value to format in the entity body.</param>
/// <returns>The created <see cref="CreatedAtRouteResult"/> for the response.</returns>
[NonAction]
public virtual CreatedAtActionResult CreatedAtAction(string actionName, object value)
{
return CreatedAtAction(actionName, routeValues: null, value: value);
}
/// <summary>
/// Creates a <see cref="CreatedAtActionResult"/> object that produces a Created (201) response.
/// </summary>
/// <param name="actionName">The name of the action to use for generating the URL.</param>
/// <param name="routeValues">The route data to use for generating the URL.</param>
/// <param name="value">The content value to format in the entity body.</param>
/// <returns>The created <see cref="CreatedAtRouteResult"/> for the response.</returns>
[NonAction]
public virtual CreatedAtActionResult CreatedAtAction(string actionName, object routeValues, object value)
{
return CreatedAtAction(actionName, controllerName: null, routeValues: routeValues, value: value);
}
/// <summary>
/// Creates a <see cref="CreatedAtActionResult"/> object that produces a Created (201) response.
/// </summary>
/// <param name="actionName">The name of the action to use for generating the URL.</param>
/// <param name="controllerName">The name of the controller to use for generating the URL.</param>
/// <param name="routeValues">The route data to use for generating the URL.</param>
/// <param name="value">The content value to format in the entity body.</param>
/// <returns>The created <see cref="CreatedAtRouteResult"/> for the response.</returns>
[NonAction]
public virtual CreatedAtActionResult CreatedAtAction(string actionName,
string controllerName,
object routeValues,
object value)
{
return new CreatedAtActionResult(actionName, controllerName, routeValues, value);
}
/// <summary>
/// Creates a <see cref="CreatedAtRouteResult"/> object that produces a Created (201) response.
/// </summary>
/// <param name="routeName">The name of the route to use for generating the URL.</param>
/// <param name="value">The content value to format in the entity body.</param>
/// <returns>The created <see cref="CreatedAtRouteResult"/> for the response.</returns>
[NonAction]
public virtual CreatedAtRouteResult CreatedAtRoute(string routeName, object value)
{
return CreatedAtRoute(routeName, routeValues: null, value: value);
}
/// <summary>
/// Creates a <see cref="CreatedAtRouteResult"/> object that produces a Created (201) response.
/// </summary>
/// <param name="routeValues">The route data to use for generating the URL.</param>
/// <param name="value">The content value to format in the entity body.</param>
/// <returns>The created <see cref="CreatedAtRouteResult"/> for the response.</returns>
[NonAction]
public virtual CreatedAtRouteResult CreatedAtRoute(object routeValues, object value)
{
return CreatedAtRoute(routeName: null, routeValues: routeValues, value: value);
}
/// <summary>
/// Creates a <see cref="CreatedAtRouteResult"/> object that produces a Created (201) response.
/// </summary>
/// <param name="routeName">The name of the route to use for generating the URL.</param>
/// <param name="routeValues">The route data to use for generating the URL.</param>
/// <param name="value">The content value to format in the entity body.</param>
/// <returns>The created <see cref="CreatedAtRouteResult"/> for the response.</returns>
[NonAction]
public virtual CreatedAtRouteResult CreatedAtRoute(string routeName, object routeValues, object value)
{
return new CreatedAtRouteResult(routeName, routeValues, value);
}
/// <summary>
/// Called before the action method is invoked.
/// </summary>

View File

@ -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);
}
}
}

View File

@ -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<InvalidOperationException>(
async () => await result.ExecuteResultAsync(actionContext),
"No route matches the supplied values.");
}
private static HttpResponse GetMockedHttpResponseObject()
{
var stream = new MemoryStream();
var httpResponse = new Mock<HttpResponse>();
httpResponse.SetupProperty(o => o.StatusCode);
httpResponse.Setup(o => o.Headers).Returns(
new HeaderDictionary(new Dictionary<string, string[]>()));
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<IRouter>());
return new ActionContext(httpContext,
routeData,
new ActionDescriptor());
}
private static HttpContext GetHttpContext(HttpResponse response)
{
var httpContext = new Mock<HttpContext>();
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<IUrlHelper>();
urlHelper.Setup(o => o.Action(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<object>(),
It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>())).Returns(returnValue);
return urlHelper.Object;
}
private class TestOutputFormatterProvider : IOutputFormattersProvider
{
public IReadOnlyList<IOutputFormatter> OutputFormatters
{
get
{
return new List<IOutputFormatter>()
{
new TextPlainFormatter(),
new JsonOutputFormatter()
};
}
}
}
}
}

View File

@ -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<object[]> CreatedAtRouteData
{
get
{
yield return new object[] { null };
yield return
new object[] {
new Dictionary<string, string>() { { "hello", "world" } }
};
yield return
new object[] {
new RouteValueDictionary(new Dictionary<string, string>() {
{ "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<string, object>(),
value: null);
result.UrlHelper = urlHelper;
// Act & Assert
await ExceptionAssert.ThrowsAsync<InvalidOperationException>(
async () => await result.ExecuteResultAsync(actionContext),
"No route matches the supplied values.");
}
private static HttpResponse GetMockedHttpResponseObject()
{
var stream = new MemoryStream();
var httpResponse = new Mock<HttpResponse>();
httpResponse.SetupProperty(o => o.StatusCode);
httpResponse.Setup(o => o.Headers).Returns(
new HeaderDictionary(new Dictionary<string, string[]>()));
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<IRouter>());
return new ActionContext(httpContext,
routeData,
new ActionDescriptor());
}
private static HttpContext GetHttpContext(HttpResponse response)
{
var httpContext = new Mock<HttpContext>();
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<IUrlHelper>();
urlHelper.Setup(o => o.RouteUrl(It.IsAny<string>(), It.IsAny<object>(), It.IsAny<string>(),
It.IsAny<string>(), It.IsAny<string>())).Returns(returnValue);
return urlHelper.Object;
}
private class TestOutputFormatterProvider : IOutputFormattersProvider
{
public IReadOnlyList<IOutputFormatter> OutputFormatters
{
get
{
return new List<IOutputFormatter>()
{
new TextPlainFormatter(),
new JsonOutputFormatter()
};
}
}
}
}
}

View File

@ -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>();
httpResponse.SetupProperty(o => o.StatusCode);
httpResponse.Setup(o => o.Headers).Returns(
new HeaderDictionary(new Dictionary<string, string[]>()));
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<IRouter>());
return new ActionContext(httpContext,
routeData,
new ActionDescriptor());
}
private static HttpContext GetHttpContext(HttpResponse response)
{
var httpContext = new Mock<HttpContext>();
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<IOutputFormatter> OutputFormatters
{
get
{
return new List<IOutputFormatter>()
{
new TextPlainFormatter(),
new JsonOutputFormatter()
};
}
}
}
}
}

View File

@ -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<CreatedResult>(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<CreatedResult>(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<CreatedResult>(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<CreatedAtActionResult>(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<CreatedAtActionResult>(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<string, object>
{
{ "test", "case" },
{ "sample", "route" },
};
// Act
var result = controller.CreatedAtAction(
"SampleAction",
"SampleController",
new RouteValueDictionary(expected), null);
// Assert
Assert.IsType<CreatedAtActionResult>(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<CreatedAtRouteResult>(result);
Assert.Same(routeName, result.RouteName);
}
[Fact]
public void CreatedAtRoute_WithParameterRouteValues_SetsResultSameRouteValues()
{
// Arrange
var controller = new Controller();
var expected = new Dictionary<string, object>
{
{ "test", "case" },
{ "sample", "route" },
};
// Act
var result = controller.CreatedAtRoute(new RouteValueDictionary(expected), null);
// Assert
Assert.IsType<CreatedAtRouteResult>(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<string, object>
{
{ "test", "case" },
{ "sample", "route" },
};
// Act
var result = controller.CreatedAtRoute(routeName, new RouteValueDictionary(expected), null);
// Assert
Assert.IsType<CreatedAtRouteResult>(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<BadRequestResult>(result);
Assert.Equal(400, result.StatusCode);
}
[Theory]
[MemberData(nameof(PublicNormalMethodsFromController))]
public void NonActionAttribute_IsOnEveryPublicNormalMethodFromController(MethodInfo method)

View File

@ -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<IApplicationBuilder> _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());
}
}
}

View File

@ -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",

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="__ToolsVersion__" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>0a6bb4c0-48d3-4e7f-952b-b8917345e075</ProjectGuid>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'" Label="Configuration">
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" Label="Configuration">
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<DevelopmentServerPort>41642</DevelopmentServerPort>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -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"
};
}
}
}

View File

@ -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; }
}
}

View File

@ -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?}");
});
}
}
}

View File

@ -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"
}