diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/HttpStatusCodeResult.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/HttpStatusCodeResult.cs index eb14b754b6..84ba620326 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ActionResults/HttpStatusCodeResult.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/HttpStatusCodeResult.cs @@ -1,9 +1,6 @@ // 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.Threading.Tasks; -using Microsoft.AspNet.Http; - namespace Microsoft.AspNet.Mvc { /// diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/BadRequestErrorMessageResult.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/BadRequestErrorMessageResult.cs new file mode 100644 index 0000000000..ff338c2280 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/BadRequestErrorMessageResult.cs @@ -0,0 +1,36 @@ +// 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.Net; +using System.Threading.Tasks; +using Microsoft.AspNet.Mvc; + +namespace System.Web.Http +{ + /// + /// An action result that returns a response and performs + /// content negotiation on an with a . + /// + public class BadRequestErrorMessageResult : ObjectResult + { + /// Initializes a new instance of the class. + /// The user-visible error message. + public BadRequestErrorMessageResult([NotNull] string message) + : base(new HttpError(message)) + { + Message = message; + } + + /// + /// Gets the error message. + /// + public string Message { get; private set; } + + /// + public override async Task ExecuteResultAsync(ActionContext context) + { + context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; + await base.ExecuteResultAsync(context); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/BadRequestResult.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/BadRequestResult.cs new file mode 100644 index 0000000000..447aa8e471 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/BadRequestResult.cs @@ -0,0 +1,22 @@ +// 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.Net; +using Microsoft.AspNet.Mvc; + +namespace System.Web.Http +{ + /// + /// An action result that returns an empty response. + /// + public class BadRequestResult : HttpStatusCodeResult + { + /// + /// Initializes a new instance of the class. + /// + public BadRequestResult() + : base((int)HttpStatusCode.BadRequest) + { + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/ConflictResult.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/ConflictResult.cs new file mode 100644 index 0000000000..a2d5267247 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/ConflictResult.cs @@ -0,0 +1,22 @@ +// 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.Net; +using Microsoft.AspNet.Mvc; + +namespace System.Web.Http +{ + /// + /// An action result that returns an empty response. + /// + public class ConflictResult : HttpStatusCodeResult + { + /// + /// Initializes a new instance of the class. + /// + public ConflictResult() + : base((int)HttpStatusCode.Conflict) + { + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/CreatedAtRouteNegotiatedContentResult.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/CreatedAtRouteNegotiatedContentResult.cs new file mode 100644 index 0000000000..405eae0ce4 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/CreatedAtRouteNegotiatedContentResult.cs @@ -0,0 +1,65 @@ +// 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.Net; +using System.Threading.Tasks; +using Microsoft.AspNet.Mvc; +using Microsoft.Framework.DependencyInjection; +using ShimResources = Microsoft.AspNet.Mvc.WebApiCompatShim.Resources; + +namespace System.Web.Http +{ + /// + /// Represents an action result that performs route generation and content negotiation and returns a + /// response when content negotiation succeeds. + /// + /// The type of content in the entity body. + public class CreatedAtRouteNegotiatedContentResult : NegotiatedContentResult + { + /// + /// 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 content value to negotiate and format in the entity body. + /// The formatters to use to negotiate and format the content. + public CreatedAtRouteNegotiatedContentResult( + [NotNull] string routeName, + [NotNull] IDictionary routeValues, + [NotNull] T content) + : base(HttpStatusCode.Created, content) + { + RouteName = routeName; + RouteValues = routeValues; + } + + /// + /// 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; } + + /// + public override Task ExecuteResultAsync(ActionContext context) + { + var request = context.HttpContext.Request; + var urlHelper = context.HttpContext.RequestServices.GetService(); + + var url = urlHelper.RouteUrl(RouteName, RouteValues, request.Scheme, request.Host.ToUriComponent()); + if (url == null) + { + throw new InvalidOperationException(ShimResources.FormatCreatedAtRoute_RouteFailed(RouteName)); + } + + context.HttpContext.Response.Headers.Add("Location", new string[] { url }); + + return base.ExecuteResultAsync(context); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/CreatedNegotiatedContentResult.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/CreatedNegotiatedContentResult.cs new file mode 100644 index 0000000000..ccd96f5380 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/CreatedNegotiatedContentResult.cs @@ -0,0 +1,52 @@ +// 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.Net; +using System.Threading.Tasks; +using Microsoft.AspNet.Mvc; + +namespace System.Web.Http +{ + /// + /// Represents an action result that performs route generation and content negotiation and returns a + /// response when content negotiation succeeds. + /// + /// The type of content in the entity body. + public class CreatedNegotiatedContentResult : NegotiatedContentResult + { + /// + /// Initializes a new instance of the class with the values + /// provided. + /// + /// The location at which the content has been created. + /// The content value to negotiate and format in the entity body. + public CreatedNegotiatedContentResult(Uri location, T content) + : base(HttpStatusCode.Created, content) + { + Location = location; + } + + /// + /// Gets the location at which the content has been created. + /// + public Uri Location { get; private set; } + + /// + public override Task ExecuteResultAsync(ActionContext context) + { + string location; + if (Location.IsAbsoluteUri) + { + location = Location.AbsoluteUri; + } + else + { + location = Location.GetComponents(UriComponents.SerializationInfoString, UriFormat.UriEscaped); + } + + context.HttpContext.Response.Headers.Add("Location", new string[] { location }); + + return base.ExecuteResultAsync(context); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/ExceptionResult.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/ExceptionResult.cs new file mode 100644 index 0000000000..b33d33bb6a --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/ExceptionResult.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Net; +using System.Threading.Tasks; +using Microsoft.AspNet.Mvc; + +namespace System.Web.Http +{ + /// + /// An action result that returns a response and + /// performs content negotiation on an based on an . + /// + public class ExceptionResult : ObjectResult + { + /// Initializes a new instance of the class. + /// The exception to include in the error. + /// + /// if the error should include exception messages; otherwise, . + /// + /// The content negotiator to handle content negotiation. + /// The request message which led to this result. + /// The formatters to use to negotiate and format the content. + public ExceptionResult(Exception exception, bool includeErrorDetail) + : base(new HttpError(exception, includeErrorDetail)) + { + Exception = exception; + IncludeErrorDetail = includeErrorDetail; + } + + /// + /// Gets the exception to include in the error. + /// + public Exception Exception { get; private set; } + + /// + /// Gets a value indicating whether the error should include exception messages. + /// + public bool IncludeErrorDetail { get; private set; } + + /// + public override Task ExecuteResultAsync(ActionContext context) + { + context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; + return base.ExecuteResultAsync(context); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/IHttpActionResult.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/IHttpActionResult.cs new file mode 100644 index 0000000000..f6ae37d91d --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/IHttpActionResult.cs @@ -0,0 +1,8 @@ +// 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 System.Web.Http +{ + [Obsolete("This interface is obsolete. Use Microsoft.AspNet.Mvc.IActionResult as a replacement.", error: true)] + public interface IHttpActionResult { } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/InternalServerErrorResult.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/InternalServerErrorResult.cs new file mode 100644 index 0000000000..7c38db27e2 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/InternalServerErrorResult.cs @@ -0,0 +1,22 @@ +// 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.Net; +using Microsoft.AspNet.Mvc; + +namespace System.Web.Http +{ + /// + /// An action result that returns an empty response. + /// + public class InternalServerErrorResult : HttpStatusCodeResult + { + /// + /// Initializes a new instance of the class. + /// + public InternalServerErrorResult() + : base((int)HttpStatusCode.InternalServerError) + { + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/InvalidModelStateResult.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/InvalidModelStateResult.cs new file mode 100644 index 0000000000..498d6b0614 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/InvalidModelStateResult.cs @@ -0,0 +1,46 @@ +// 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.Net; +using System.Threading.Tasks; +using Microsoft.AspNet.Mvc.ModelBinding; +using Microsoft.AspNet.Mvc; + +namespace System.Web.Http +{ + /// + /// An action result that returns a response and performs + /// content negotiation on an based on a . + /// + public class InvalidModelStateResult : ObjectResult + { + /// Initializes a new instance of the class. + /// The model state to include in the error. + /// + /// if the error should include exception messages; otherwise, . + /// + public InvalidModelStateResult([NotNull] ModelStateDictionary modelState, bool includeErrorDetail) + : base(new HttpError(modelState, includeErrorDetail)) + { + ModelState = modelState; + IncludeErrorDetail = includeErrorDetail; + } + + /// + /// Gets the model state to include in the error. + /// + public ModelStateDictionary ModelState { get; private set; } + + /// + /// Gets a value indicating whether the error should include exception messages. + /// + public bool IncludeErrorDetail { get; private set; } + + /// + public override async Task ExecuteResultAsync(ActionContext context) + { + context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; + await base.ExecuteResultAsync(context); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/JsonResult.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/JsonResult.cs new file mode 100644 index 0000000000..79a025ed2c --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/JsonResult.cs @@ -0,0 +1,8 @@ +// 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 System.Web.Http +{ + [Obsolete("This class is obsolete. Use Microsoft.AspNet.Mvc.ObjectResult as a replacement.", error: true)] + public class JsonResult { } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/NegotiatedContentResult.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/NegotiatedContentResult.cs new file mode 100644 index 0000000000..bed9f4109a --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/NegotiatedContentResult.cs @@ -0,0 +1,45 @@ +// 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.Net; +using System.Threading.Tasks; +using Microsoft.AspNet.Mvc; + +namespace System.Web.Http +{ + /// + /// An action result that performs content negotiation. + /// + /// The type of content in the entity body. + public class NegotiatedContentResult : ObjectResult + { + /// + /// Initializes a new instance of the class with the values provided. + /// + /// The HTTP status code for the response message. + /// The content value to negotiate and format in the entity body. + public NegotiatedContentResult(HttpStatusCode statusCode, T content) + : base(content) + { + StatusCode = statusCode; + Content = content; + } + + /// + /// Gets the HTTP status code for the response message. + /// + public HttpStatusCode StatusCode { get; private set; } + + /// + /// Gets the content value to negotiate and format in the entity body. + /// + public T Content { get; private set; } + + /// + public override Task ExecuteResultAsync(ActionContext context) + { + context.HttpContext.Response.StatusCode = (int)StatusCode; + return base.ExecuteResultAsync(context); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/NotFoundResult.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/NotFoundResult.cs new file mode 100644 index 0000000000..703c5b2213 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/NotFoundResult.cs @@ -0,0 +1,12 @@ +// 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.Mvc; + +namespace System.Web.Http +{ + [Obsolete("This class is obsolete. Use Microsoft.AspNet.Mvc.HttpNotFoundResult as a replacement.", error: true)] + public class NotFoundResult + { + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/OkNegotiatedContentResult.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/OkNegotiatedContentResult.cs new file mode 100644 index 0000000000..2275e8c235 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/OkNegotiatedContentResult.cs @@ -0,0 +1,27 @@ +// 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.Net; +using Microsoft.AspNet.Mvc; + +namespace System.Web.Http +{ + /// + /// Represents an action result that performs content negotiation and returns an + /// response when it succeeds. + /// + /// The type of content in the entity body. + public class OkNegotiatedContentResult : NegotiatedContentResult + { + /// + /// Initializes a new instance of the class with the values + /// provided. + /// + /// The content value to negotiate and format in the entity body. + /// The formatters to use to negotiate and format the content. + public OkNegotiatedContentResult([NotNull] T content) + : base(HttpStatusCode.OK, content) + { + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/OkResult.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/OkResult.cs new file mode 100644 index 0000000000..fa7762a552 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/OkResult.cs @@ -0,0 +1,22 @@ +// 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.Net; +using Microsoft.AspNet.Mvc; + +namespace System.Web.Http +{ + /// + /// An action result that returns an empty response. + /// + public class OkResult : HttpStatusCodeResult + { + /// + /// Initializes a new instance of the class. + /// + public OkResult() + : base((int)HttpStatusCode.OK) + { + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/ResponseMessageResult.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/ResponseMessageResult.cs new file mode 100644 index 0000000000..397d625670 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/ResponseMessageResult.cs @@ -0,0 +1,30 @@ +// 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.Net.Http; +using System.Threading.Tasks; +using Microsoft.AspNet.Mvc; + +namespace System.Web.Http +{ + /// + /// An action result that returns a specified response message. + /// + public class ResponseMessageResult : ObjectResult + { + /// + /// Initializes a new instance of the class. + /// + /// The response message. + public ResponseMessageResult([NotNull] HttpResponseMessage response) + : base(response) + { + Response = response; + } + + /// + /// Gets the response message. + /// + public HttpResponseMessage Response { get; private set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/StatusCodeResult.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/StatusCodeResult.cs new file mode 100644 index 0000000000..a82f73f9f0 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ActionResults/StatusCodeResult.cs @@ -0,0 +1,12 @@ +// 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 System.Web.Http +{ + [Obsolete("This class is obsolete, use HttpStatusCodeResult as a replacement.", error: true)] + public class StatusCodeResult + { + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ApiController.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ApiController.cs index db3b82fc9f..f4648d2cb2 100644 --- a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ApiController.cs +++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ApiController.cs @@ -1,14 +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 System.Collections.Generic; +using System.Net; using System.Net.Http; using System.Security.Principal; +using System.Text; using Microsoft.AspNet.Http; using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.WebApiCompatShim; +using Microsoft.AspNet.Routing; using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.OptionsModel; +using Newtonsoft.Json; +using MvcMediaTypeHeaderValue = Microsoft.AspNet.Mvc.HeaderValueAbstractions.MediaTypeHeaderValue; namespace System.Web.Http { @@ -19,7 +25,9 @@ namespace System.Web.Http { private HttpRequestMessage _request; - /// Gets the action context. + /// + /// Gets the action context. + /// /// The setter is intended for unit testing purposes only. [Activate] public ActionContext ActionContext { get; set; } @@ -46,7 +54,9 @@ namespace System.Web.Http } } - /// Gets or sets the HTTP request message. + /// + /// Gets or sets the HTTP request message. + /// /// The setter is intended for unit testing purposes only. public HttpRequestMessage Request { @@ -65,12 +75,16 @@ namespace System.Web.Http } } - /// Gets a factory used to generate URLs to other APIs. + /// + /// Gets a factory used to generate URLs to other APIs. + /// /// The setter is intended for unit testing purposes only. [Activate] public IUrlHelper Url { get; set; } - /// Gets or sets the current principal associated with this request. + /// + /// Gets or sets the current principal associated with this request. + /// public IPrincipal User { get @@ -79,6 +93,296 @@ namespace System.Web.Http } } + /// + /// Creates a (400 Bad Request). + /// + /// A . + [NonAction] + public virtual BadRequestResult BadRequest() + { + return new BadRequestResult(); + } + + /// + /// Creates a (400 Bad Request) with the specified error message. + /// + /// The user-visible error message. + /// A with the specified error message. + [NonAction] + public virtual BadRequestErrorMessageResult BadRequest([NotNull] string message) + { + return new BadRequestErrorMessageResult(message); + } + + /// + /// Creates an (400 Bad Request) with the specified model state. + /// + /// The model state to include in the error. + /// An with the specified model state. + [NonAction] + public virtual InvalidModelStateResult BadRequest([NotNull] ModelStateDictionary modelState) + { + return new InvalidModelStateResult(modelState, includeErrorDetail: false); + } + + /// Creates a (409 Conflict). + /// A . + [NonAction] + public virtual ConflictResult Conflict() + { + return new ConflictResult(); + } + + /// + /// Creates a with the specified values. + /// + /// The type of content in the entity body. + /// The HTTP status code for the response message. + /// The content value to negotiate and format in the entity body. + /// A with the specified values. + [NonAction] + public virtual NegotiatedContentResult Content(HttpStatusCode statusCode, [NotNull] T value) + { + return new NegotiatedContentResult(statusCode, value); + } + + /// + /// Creates a (201 Created) with the specified values. + /// + /// The type of content in the entity body. + /// + /// The location at which the content has been created. Must be a relative or absolute URL. + /// + /// The content value to negotiate and format in the entity body. + /// A with the specified values. + [NonAction] + public virtual CreatedNegotiatedContentResult Created([NotNull] string location, [NotNull] T content) + { + return Created(new Uri(location, UriKind.RelativeOrAbsolute), content); + } + + /// + /// Creates a (201 Created) with the specified values. + /// + /// The type of content in the entity body. + /// The location at which the content has been created. + /// The content value to negotiate and format in the entity body. + /// A with the specified values. + [NonAction] + public virtual CreatedNegotiatedContentResult Created([NotNull] Uri location, [NotNull] T content) + { + return new CreatedNegotiatedContentResult(location, content); + } + + /// + /// Creates a (201 Created) with the specified values. + /// + /// The type of content in the entity body. + /// The name of the route to use for generating the URL. + /// The route data to use for generating the URL. + /// The content value to negotiate and format in the entity body. + /// A with the specified values. + [NonAction] + public virtual CreatedAtRouteNegotiatedContentResult CreatedAtRoute( + [NotNull] string routeName, + object routeValues, + [NotNull] T content) + { + var values = routeValues as IDictionary; + if (values == null) + { + values = new RouteValueDictionary(routeValues); + } + + return new CreatedAtRouteNegotiatedContentResult(routeName, values, content); + } + + /// Creates an (500 Internal Server Error). + /// + /// A . + [NonAction] + public virtual InternalServerErrorResult InternalServerError() + { + return new InternalServerErrorResult(); + } + + /// + /// Creates an (500 Internal Server Error) with the specified exception. + /// + /// The exception to include in the error. + /// An with the specified exception. + [NonAction] + public virtual ExceptionResult InternalServerError([NotNull] Exception exception) + { + return new ExceptionResult(exception, includeErrorDetail: false); + } + + /// + /// Creates an (200 OK) with the specified value. + /// + /// The type of content in the entity body. + /// The content value to serialize in the entity body. + /// A with the specified value. + [NonAction] + public virtual ObjectResult Json([NotNull] T content) + { + var result = new ObjectResult(content); + result.ContentTypes.Add(MvcMediaTypeHeaderValue.Parse("application/json")); + result.ContentTypes.Add(MvcMediaTypeHeaderValue.Parse("text/json")); + return result; + } + + /// + /// Creates an (200 OK) with the specified values. + /// + /// The type of content in the entity body. + /// The content value to serialize in the entity body. + /// The serializer settings. + /// A with the specified values. + [NonAction] + public virtual ObjectResult Json([NotNull] T content, [NotNull] JsonSerializerSettings serializerSettings) + { + var formatter = new JsonOutputFormatter() + { + SerializerSettings = serializerSettings, + }; + + var result = new ObjectResult(content); + result.Formatters.Add(formatter); + result.ContentTypes.Add(MvcMediaTypeHeaderValue.Parse("application/json")); + result.ContentTypes.Add(MvcMediaTypeHeaderValue.Parse("text/json")); + return result; + } + + /// + /// Creates an (200 OK) with the specified values. + /// + /// The type of content in the entity body. + /// The content value to serialize in the entity body. + /// The serializer settings. + /// The content encoding. + /// A with the specified values. + [NonAction] + public virtual ObjectResult Json( + [NotNull] T content, + [NotNull] JsonSerializerSettings serializerSettings, + [NotNull] Encoding encoding) + { + var formatter = new JsonOutputFormatter() + { + SerializerSettings = serializerSettings, + }; + + formatter.SupportedEncodings.Clear(); + formatter.SupportedEncodings.Add(encoding); + + var result = new ObjectResult(content); + result.Formatters.Add(formatter); + result.ContentTypes.Add(MvcMediaTypeHeaderValue.Parse("application/json")); + result.ContentTypes.Add(MvcMediaTypeHeaderValue.Parse("text/json")); + return result; + } + + /// + /// Creates an (404 Not Found). + /// + /// A . + [NonAction] + public virtual HttpNotFoundResult NotFound() + { + return new HttpNotFoundResult(); + } + + /// + /// Creates an (200 OK). + /// + /// An . + [NonAction] + public virtual OkResult Ok() + { + return new OkResult(); + } + + /// + /// Creates an (200 OK) with the specified values. + /// + /// The type of content in the entity body. + /// The content value to negotiate and format in the entity body. + /// An with the specified values. + [NonAction] + public virtual OkNegotiatedContentResult Ok([NotNull] T content) + { + return new OkNegotiatedContentResult(content); + } + + /// + /// Creates a (302 Found) with the specified value. + /// + /// The location to which to redirect. + /// A with the specified value. + [NonAction] + public virtual RedirectResult Redirect([NotNull] string location) + { + // This is how redirect was implemented in legacy webapi - string URIs are assumed to be absolute. + return Redirect(new Uri(location)); + } + + /// + /// Creates a (302 Found) with the specified value. + /// + /// The location to which to redirect. + /// A with the specified value. + [NonAction] + public virtual RedirectResult Redirect([NotNull] Uri location) + { + string uri; + if (location.IsAbsoluteUri) + { + uri = location.AbsoluteUri; + } + else + { + uri = location.GetComponents(UriComponents.SerializationInfoString, UriFormat.UriEscaped); + } + + return new RedirectResult(uri); + } + + /// + /// Creates a (302 Found) with the specified values. + /// + /// The name of the route to use for generating the URL. + /// The route data to use for generating the URL. + /// A with the specified values. + [NonAction] + public virtual RedirectToRouteResult RedirectToRoute([NotNull] string routeName, [NotNull] object routeValues) + { + return new RedirectToRouteResult(Url, routeName, routeValues); + } + + /// + /// Creates a with the specified response. + /// + /// The HTTP response message. + /// A for the specified response. + [NonAction] + public virtual ResponseMessageResult ResponseMessage([NotNull] HttpResponseMessage response) + { + return new ResponseMessageResult(response); + } + + /// + /// Creates a with the specified status code. + /// + /// The HTTP status code for the response message + /// A with the specified status code. + [NonAction] + public virtual HttpStatusCodeResult StatusCode(HttpStatusCode status) + { + return new HttpStatusCodeResult((int)status); + } + [NonAction] public void Dispose() { diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/Properties/Resources.Designer.cs index c77f24fddc..986908598c 100644 --- a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/Properties/Resources.Designer.cs @@ -154,6 +154,22 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim return GetString("HttpResponseExceptionMessage"); } + /// + /// Failed to generate a URL using route '{0}'. + /// + internal static string CreatedAtRoute_RouteFailed + { + get { return GetString("CreatedAtRoute_RouteFailed"); } + } + + /// + /// Failed to generate a URL using route '{0}'. + /// + internal static string FormatCreatedAtRoute_RouteFailed(object p0) + { + return string.Format(CultureInfo.CurrentCulture, GetString("CreatedAtRoute_RouteFailed"), p0); + } + private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/Resources.resx b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/Resources.resx index 1c433adac4..c7c30770bd 100644 --- a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/Resources.resx +++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/Resources.resx @@ -144,4 +144,7 @@ Processing of the HTTP request resulted in an exception. Please see the HTTP response returned by the 'Response' property of this exception for details. + + Failed to generate a URL using route '{0}'. + \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/WebApiCompatShimActionResultTest.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/WebApiCompatShimActionResultTest.cs new file mode 100644 index 0000000000..bdb9b1d740 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/WebApiCompatShimActionResultTest.cs @@ -0,0 +1,381 @@ +// 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. + +#if ASPNET50 +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Http; +using System.Net.Http.Formatting; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using System.Web.Http; +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.TestHost; +using Newtonsoft.Json; +using Xunit; + +namespace Microsoft.AspNet.Mvc.FunctionalTests +{ + public class WebApiCompatShimActionResultTest + { + private readonly IServiceProvider _provider = TestHelper.CreateServices(nameof(WebApiCompatShimWebSite)); + private readonly Action _app = new WebApiCompatShimWebSite.Startup().Configure; + + [Fact] + public async Task ApiController_BadRequest() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/api/Blog/ActionResult/GetBadRequest"); + + // Assert + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + } + + [Fact] + public async Task ApiController_BadRequestMessage() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/api/Blog/ActionResult/GetBadRequestMessage"); + var content = await response.Content.ReadAsStringAsync(); + + // Assert + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + Assert.Equal("{\"Message\":\"Hello, world!\"}", content); + } + + [Fact] + public async Task ApiController_BadRequestModelState() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + var expected = "{\"Message\":\"The request is invalid.\",\"ModelState\":{\"product.Name\":[\"Name is required.\"]}}"; + + // Act + var response = await client.GetAsync("http://localhost/api/Blog/ActionResult/GetBadRequestModelState"); + var content = await response.Content.ReadAsStringAsync(); + + // Assert + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + Assert.Equal(expected, content); + } + + [Fact] + public async Task ApiController_Conflict() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/api/Blog/ActionResult/GetConflict"); + + // Assert + Assert.Equal(HttpStatusCode.Conflict, response.StatusCode); + } + + [Fact] + public async Task ApiController_Content() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/api/Blog/ActionResult/GetContent"); + var content = await response.Content.ReadAsStringAsync(); + + // Assert + Assert.Equal(HttpStatusCode.Ambiguous, response.StatusCode); + Assert.Equal("{\"Name\":\"Test User\"}", content); + } + + [Fact] + public async Task ApiController_CreatedRelative() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/api/Blog/ActionResult/GetCreatedRelative"); + var content = await response.Content.ReadAsStringAsync(); + + // Assert + Assert.Equal(HttpStatusCode.Created, response.StatusCode); + Assert.Equal("{\"Name\":\"Test User\"}", content); + Assert.Equal("5", response.Headers.Location.OriginalString); + } + + [Fact] + public async Task ApiController_CreatedAbsolute() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/api/Blog/ActionResult/GetCreatedAbsolute"); + var content = await response.Content.ReadAsStringAsync(); + + // Assert + Assert.Equal(HttpStatusCode.Created, response.StatusCode); + Assert.Equal("{\"Name\":\"Test User\"}", content); + Assert.Equal("/api/Blog/ActionResult/GetUser/5", response.Headers.Location.OriginalString); + } + + [Fact] + public async Task ApiController_CreatedQualified() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/api/Blog/ActionResult/GetCreatedQualified"); + var content = await response.Content.ReadAsStringAsync(); + + // Assert + Assert.Equal(HttpStatusCode.Created, response.StatusCode); + Assert.Equal("{\"Name\":\"Test User\"}", content); + Assert.Equal("http://localhost/api/Blog/ActionResult/5", response.Headers.Location.OriginalString); + } + + [Fact] + public async Task ApiController_CreatedUri() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/api/Blog/ActionResult/GetCreatedUri"); + var content = await response.Content.ReadAsStringAsync(); + + // Assert + Assert.Equal(HttpStatusCode.Created, response.StatusCode); + Assert.Equal("{\"Name\":\"Test User\"}", content); + Assert.Equal("/api/Blog/ActionResult/GetUser/5", response.Headers.Location.OriginalString); + } + + [Fact] + public async Task ApiController_CreatedAtRoute() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/api/Blog/ActionResult/GetCreatedAtRoute"); + var content = await response.Content.ReadAsStringAsync(); + + // Assert + Assert.Equal(HttpStatusCode.Created, response.StatusCode); + Assert.Equal("{\"Name\":\"Test User\"}", content); + Assert.Equal("http://localhost/api/Blog/ActionResult/GetUser/5", response.Headers.Location.OriginalString); + } + + [Fact] + public async Task ApiController_InternalServerError() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/api/Blog/ActionResult/GetInternalServerError"); + + // Assert + Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); + } + + [Fact] + public async Task ApiController_InternalServerErrorException() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/api/Blog/ActionResult/GetInternalServerErrorException"); + var content = await response.Content.ReadAsStringAsync(); + + // Assert + Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); + Assert.Equal("{\"Message\":\"An error has occurred.\"}", content); + } + + [Fact] + public async Task ApiController_Json() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/api/Blog/ActionResult/GetJson"); + var content = await response.Content.ReadAsStringAsync(); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal("{\"Name\":\"Test User\"}", content); + } + + [Fact] + public async Task ApiController_JsonSettings() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + var expected = + "{" + Environment.NewLine + + " \"Name\": \"Test User\"" + Environment.NewLine + + "}"; + + // Act + var response = await client.GetAsync("http://localhost/api/Blog/ActionResult/GetJsonSettings"); + var content = await response.Content.ReadAsStringAsync(); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(expected, content); + } + + [Fact] + public async Task ApiController_JsonSettingsEncoding() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + var expected = + "{" + Environment.NewLine + + " \"Name\": \"Test User\"" + Environment.NewLine + + "}"; + + // Act + var response = await client.GetAsync("http://localhost/api/Blog/ActionResult/GetJsonSettingsEncoding"); + var content = await response.Content.ReadAsStringAsync(); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(expected, content); + Assert.Equal("utf-32", response.Content.Headers.ContentType.CharSet); + } + + [Fact] + public async Task ApiController_NotFound() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/api/Blog/ActionResult/GetNotFound"); + + // Assert + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + + [Fact] + public async Task ApiController_Ok() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/api/Blog/ActionResult/GetOk"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } + + [Fact] + public async Task ApiController_OkContent() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/api/Blog/ActionResult/GetOkContent"); + var content = await response.Content.ReadAsStringAsync(); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal("{\"Name\":\"Test User\"}", content); + } + + [Fact] + public async Task ApiController_RedirectString() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/api/Blog/ActionResult/GetRedirectString"); + + // Assert + Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); + Assert.Equal("http://localhost/api/Users", response.Headers.Location.OriginalString); + } + + [Fact] + public async Task ApiController_RedirectUri() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/api/Blog/ActionResult/GetRedirectUri"); + + // Assert + Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); + Assert.Equal("api/Blog", response.Headers.Location.OriginalString); + } + + [Fact] + public async Task ApiController_ResponseMessage() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/api/Blog/ActionResult/GetResponseMessage"); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(new string[] { "Hello" }, response.Headers.GetValues("X-Test")); + } + + [Fact] + public async Task ApiController_StatusCode() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetAsync("http://localhost/api/Blog/ActionResult/GetStatusCode"); + + // Assert + Assert.Equal(HttpStatusCode.PaymentRequired, response.StatusCode); + } + } +} +#endif diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/BadRequestErrorMessageResultTest.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/BadRequestErrorMessageResultTest.cs new file mode 100644 index 0000000000..02204bb325 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/BadRequestErrorMessageResultTest.cs @@ -0,0 +1,80 @@ +// 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. + +#if ASPNET50 +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Microsoft.AspNet.Mvc; +using Microsoft.AspNet.PipelineCore; +using Microsoft.AspNet.Routing; +using Moq; +using Xunit; + +namespace System.Web.Http +{ + public class BadRequestErrorMessageResultTest + { + [Fact] + public async Task BadRequestErrorMessageResult_SetsStatusCode() + { + // Arrange + var httpContext = new DefaultHttpContext(); + httpContext.RequestServices = CreateServices(); + + var stream = new MemoryStream(); + httpContext.Response.Body = stream; + + var context = new ActionContext(new RouteContext(httpContext), new ActionDescriptor()); + var result = new BadRequestErrorMessageResult("Error"); + + // Act + await result.ExecuteResultAsync(context); + + // Assert + Assert.Equal(400, context.HttpContext.Response.StatusCode); + } + + [Fact] + public async Task BadRequestErrorMessageResult_WritesHttpError() + { + // Arrange + var httpContext = new DefaultHttpContext(); + httpContext.RequestServices = CreateServices(); + + var stream = new MemoryStream(); + httpContext.Response.Body = stream; + + var context = new ActionContext(new RouteContext(httpContext), new ActionDescriptor()); + var result = new BadRequestErrorMessageResult("Error"); + + // Act + await result.ExecuteResultAsync(context); + + // Assert + using (var reader = new StreamReader(stream)) + { + stream.Seek(0, SeekOrigin.Begin); + var content = reader.ReadToEnd(); + Assert.Equal("{\"Message\":\"Error\"}", content); + } + } + + private IServiceProvider CreateServices() + { + var services = new Mock(MockBehavior.Strict); + + var formatters = new Mock(MockBehavior.Strict); + formatters + .SetupGet(f => f.OutputFormatters) + .Returns(new List() { new JsonOutputFormatter(), }); + + services + .Setup(s => s.GetService(typeof(IOutputFormattersProvider))) + .Returns(formatters.Object); + + return services.Object; + } + } +} +#endif \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/BadRequestResultTest.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/BadRequestResultTest.cs new file mode 100644 index 0000000000..b79ab1fdcc --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/BadRequestResultTest.cs @@ -0,0 +1,28 @@ +// 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.Threading.Tasks; +using Microsoft.AspNet.Mvc; +using Microsoft.AspNet.PipelineCore; +using Microsoft.AspNet.Routing; +using Xunit; + +namespace System.Web.Http +{ + public class BadRequestResultTest + { + [Fact] + public async Task BadRequestResult_SetsStatusCode() + { + // Arrange + var context = new ActionContext(new RouteContext(new DefaultHttpContext()), new ActionDescriptor()); + var result = new BadRequestResult(); + + // Act + await result.ExecuteResultAsync(context); + + // Assert + Assert.Equal(400, context.HttpContext.Response.StatusCode); + } + } +} diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/ConflictResultTest.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/ConflictResultTest.cs new file mode 100644 index 0000000000..db9100bb4b --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/ConflictResultTest.cs @@ -0,0 +1,28 @@ +// 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.Threading.Tasks; +using Microsoft.AspNet.Mvc; +using Microsoft.AspNet.PipelineCore; +using Microsoft.AspNet.Routing; +using Xunit; + +namespace System.Web.Http +{ + public class ConflictResultTest + { + [Fact] + public async Task ConflictResult_SetsStatusCode() + { + // Arrange + var context = new ActionContext(new RouteContext(new DefaultHttpContext()), new ActionDescriptor()); + var result = new ConflictResult(); + + // Act + await result.ExecuteResultAsync(context); + + // Assert + Assert.Equal(409, context.HttpContext.Response.StatusCode); + } + } +} diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/CreatedAtRouteNegotiatedContentResultTest.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/CreatedAtRouteNegotiatedContentResultTest.cs new file mode 100644 index 0000000000..321fb60aa7 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/CreatedAtRouteNegotiatedContentResultTest.cs @@ -0,0 +1,130 @@ +// 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. + +#if ASPNET50 +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Microsoft.AspNet.Mvc; +using Microsoft.AspNet.PipelineCore; +using Microsoft.AspNet.Routing; +using Moq; +using Xunit; + +namespace System.Web.Http +{ + public class CreatedAtRouteNegotiatedContentResultTest + { + [Fact] + public async Task CreatedAtRouteNegotiatedContentResult_SetsStatusCode() + { + // Arrange + var urlHelper = new Mock(MockBehavior.Strict); + urlHelper + .Setup(u => u.RouteUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns("http://contoso.com/api/Products/5"); + + var httpContext = new DefaultHttpContext(); + httpContext.RequestServices = CreateServices(urlHelper.Object); + + var stream = new MemoryStream(); + httpContext.Response.Body = stream; + + var context = new ActionContext(new RouteContext(httpContext), new ActionDescriptor()); + var result = new CreatedAtRouteNegotiatedContentResult( + "api_route", + new RouteValueDictionary(new { controller = "Products", id = 5 }), + new Product()); + + // Act + await result.ExecuteResultAsync(context); + + // Assert + Assert.Equal(201, context.HttpContext.Response.StatusCode); + } + + [Fact] + public async Task CreatedAtRouteNegotiatedContentResult_SetsLocation() + { + // Arrange + var urlHelper = new Mock(MockBehavior.Strict); + urlHelper + .Setup(u => u.RouteUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns("http://contoso.com/api/Products/5"); + + var httpContext = new DefaultHttpContext(); + httpContext.RequestServices = CreateServices(urlHelper.Object); + + var stream = new MemoryStream(); + httpContext.Response.Body = stream; + + var context = new ActionContext(new RouteContext(httpContext), new ActionDescriptor()); + var result = new CreatedAtRouteNegotiatedContentResult( + "api_route", + new RouteValueDictionary(new { controller = "Products", id = 5 }), + new Product()); + + // Act + await result.ExecuteResultAsync(context); + + // Assert + Assert.Equal("http://contoso.com/api/Products/5", httpContext.Response.Headers["Location"]); + } + + [Fact] + public async Task CreatedAtRouteNegotiatedContentResult_Fails() + { + // Arrange + var urlHelper = new Mock(MockBehavior.Strict); + urlHelper + .Setup(u => u.RouteUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((string)null); + + var httpContext = new DefaultHttpContext(); + httpContext.RequestServices = CreateServices(urlHelper.Object); + + var stream = new MemoryStream(); + httpContext.Response.Body = stream; + + var context = new ActionContext(new RouteContext(httpContext), new ActionDescriptor()); + var result = new CreatedAtRouteNegotiatedContentResult( + "api_route", + new RouteValueDictionary(new { controller = "Products", id = 5 }), + new Product()); + + // Act + var ex = await Assert.ThrowsAsync(async () => await result.ExecuteResultAsync(context)); + + // Assert + Assert.Equal("Failed to generate a URL using route 'api_route'.", ex.Message); + } + + private IServiceProvider CreateServices(IUrlHelper urlHelper) + { + var services = new Mock(MockBehavior.Strict); + + services + .Setup(s => s.GetService(typeof(IUrlHelper))) + .Returns(urlHelper); + + var formatters = new Mock(MockBehavior.Strict); + formatters + .SetupGet(f => f.OutputFormatters) + .Returns(new List() { new JsonOutputFormatter(), }); + + services + .Setup(s => s.GetService(typeof(IOutputFormattersProvider))) + .Returns(formatters.Object); + + return services.Object; + } + + private class Product + { + public int Id { get; set; } + + public string Name { get; set; } + }; + } +} +#endif \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/CreatedNegotiatedContentResultTest.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/CreatedNegotiatedContentResultTest.cs new file mode 100644 index 0000000000..ee2312c856 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/CreatedNegotiatedContentResultTest.cs @@ -0,0 +1,111 @@ +// 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. + +#if ASPNET50 +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Microsoft.AspNet.Mvc; +using Microsoft.AspNet.PipelineCore; +using Microsoft.AspNet.Routing; +using Moq; +using Xunit; + +namespace System.Web.Http +{ + public class CreatedNegotiatedContentResultTest + { + [Fact] + public async Task CreatedNegotiatedContentResult_SetsStatusCode() + { + // Arrange + var httpContext = new DefaultHttpContext(); + httpContext.RequestServices = CreateServices(); + + var stream = new MemoryStream(); + httpContext.Response.Body = stream; + + var uri = new Uri("http://contoso.com"); + + var context = new ActionContext(new RouteContext(httpContext), new ActionDescriptor()); + var result = new CreatedNegotiatedContentResult(uri, new Product()); + + // Act + await result.ExecuteResultAsync(context); + + // Assert + Assert.Equal(201, context.HttpContext.Response.StatusCode); + } + + [Fact] + public async Task CreatedNegotiatedContentResult_SetsLocation_Uri() + { + // Arrange + var httpContext = new DefaultHttpContext(); + httpContext.RequestServices = CreateServices(); + + var stream = new MemoryStream(); + httpContext.Response.Body = stream; + + var uri = new Uri("http://contoso.com"); + + var context = new ActionContext(new RouteContext(httpContext), new ActionDescriptor()); + var result = new CreatedNegotiatedContentResult(uri, new Product()); + + // Act + await result.ExecuteResultAsync(context); + + // Assert + Assert.Equal("http://contoso.com/", httpContext.Response.Headers["Location"]); + } + + [Theory] + [InlineData("http://contoso.com/Api/Products")] + [InlineData("/Api/Products")] + [InlineData("Products")] + public async Task CreatedNegotiatedContentResult_SetsLocation_String(string uri) + { + // Arrange + var httpContext = new DefaultHttpContext(); + httpContext.RequestServices = CreateServices(); + + var stream = new MemoryStream(); + httpContext.Response.Body = stream; + + var context = new ActionContext(new RouteContext(httpContext), new ActionDescriptor()); + var result = new CreatedNegotiatedContentResult( + new Uri(uri, UriKind.RelativeOrAbsolute), + new Product()); + + // Act + await result.ExecuteResultAsync(context); + + // Assert + Assert.Equal(uri, httpContext.Response.Headers["Location"]); + } + + private IServiceProvider CreateServices() + { + var services = new Mock(MockBehavior.Strict); + + var formatters = new Mock(MockBehavior.Strict); + formatters + .SetupGet(f => f.OutputFormatters) + .Returns(new List() { new JsonOutputFormatter(), }); + + services + .Setup(s => s.GetService(typeof(IOutputFormattersProvider))) + .Returns(formatters.Object); + + return services.Object; + } + + private class Product + { + public int Id { get; set; } + + public string Name { get; set; } + }; + } +} +#endif \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/ExceptionResultTest.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/ExceptionResultTest.cs new file mode 100644 index 0000000000..8ea7374979 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/ExceptionResultTest.cs @@ -0,0 +1,80 @@ +// 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. + +#if ASPNET50 +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Microsoft.AspNet.Mvc; +using Microsoft.AspNet.PipelineCore; +using Microsoft.AspNet.Routing; +using Moq; +using Xunit; + +namespace System.Web.Http +{ + public class ExceptionResultTest + { + [Fact] + public async Task ExceptionResult_SetsStatusCode() + { + // Arrange + var httpContext = new DefaultHttpContext(); + httpContext.RequestServices = CreateServices(); + + var stream = new MemoryStream(); + httpContext.Response.Body = stream; + + var context = new ActionContext(new RouteContext(httpContext), new ActionDescriptor()); + var result = new ExceptionResult(new Exception("hello, world!"), includeErrorDetail: false); + + // Act + await result.ExecuteResultAsync(context); + + // Assert + Assert.Equal(500, context.HttpContext.Response.StatusCode); + } + + [Fact] + public async Task ExceptionResult_WritesHttpError() + { + // Arrange + var httpContext = new DefaultHttpContext(); + httpContext.RequestServices = CreateServices(); + + var stream = new MemoryStream(); + httpContext.Response.Body = stream; + + var context = new ActionContext(new RouteContext(httpContext), new ActionDescriptor()); + var result = new ExceptionResult(new Exception("hello, world!"), includeErrorDetail: false); + + // Act + await result.ExecuteResultAsync(context); + + // Assert + using (var reader = new StreamReader(stream)) + { + stream.Seek(0, SeekOrigin.Begin); + var content = reader.ReadToEnd(); + Assert.Equal("{\"Message\":\"An error has occurred.\"}", content); + } + } + + private IServiceProvider CreateServices() + { + var services = new Mock(MockBehavior.Strict); + + var formatters = new Mock(MockBehavior.Strict); + formatters + .SetupGet(f => f.OutputFormatters) + .Returns(new List() { new JsonOutputFormatter(), }); + + services + .Setup(s => s.GetService(typeof(IOutputFormattersProvider))) + .Returns(formatters.Object); + + return services.Object; + } + } +} +#endif \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/InternalServerErrorResultTest.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/InternalServerErrorResultTest.cs new file mode 100644 index 0000000000..7ed2f12750 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/InternalServerErrorResultTest.cs @@ -0,0 +1,28 @@ +// 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.Threading.Tasks; +using Microsoft.AspNet.Mvc; +using Microsoft.AspNet.PipelineCore; +using Microsoft.AspNet.Routing; +using Xunit; + +namespace System.Web.Http +{ + public class InternalServerErrorResultTest + { + [Fact] + public async Task InternalServerErrorResult_SetsStatusCode() + { + // Arrange + var context = new ActionContext(new RouteContext(new DefaultHttpContext()), new ActionDescriptor()); + var result = new InternalServerErrorResult(); + + // Act + await result.ExecuteResultAsync(context); + + // Assert + Assert.Equal(500, context.HttpContext.Response.StatusCode); + } + } +} diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/InvalidModelStateResultTest.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/InvalidModelStateResultTest.cs new file mode 100644 index 0000000000..ed49124d5f --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/InvalidModelStateResultTest.cs @@ -0,0 +1,93 @@ +// 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. + +#if ASPNET50 +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Microsoft.AspNet.Mvc; +using Microsoft.AspNet.PipelineCore; +using Microsoft.AspNet.Routing; +using Moq; +using Xunit; +using Microsoft.AspNet.Mvc.ModelBinding; + +namespace System.Web.Http +{ + public class InvalidModelStateResultTest + { + [Fact] + public async Task InvalidModelStateResult_SetsStatusCode() + { + // Arrange + var httpContext = new DefaultHttpContext(); + httpContext.RequestServices = CreateServices(); + + var stream = new MemoryStream(); + httpContext.Response.Body = stream; + + var context = new ActionContext(new RouteContext(httpContext), new ActionDescriptor()); + + var modelState = new ModelStateDictionary(); + modelState.AddModelError("product.Name", "Name is required."); + + var result = new InvalidModelStateResult(modelState, includeErrorDetail: false); + + // Act + await result.ExecuteResultAsync(context); + + // Assert + Assert.Equal(400, context.HttpContext.Response.StatusCode); + } + + [Fact] + public async Task InvalidModelStateResult_WritesHttpError() + { + // Arrange + var httpContext = new DefaultHttpContext(); + httpContext.RequestServices = CreateServices(); + + var stream = new MemoryStream(); + httpContext.Response.Body = stream; + + var context = new ActionContext(new RouteContext(httpContext), new ActionDescriptor()); + + var modelState = new ModelStateDictionary(); + modelState.AddModelError("product.Name", "Name is required."); + + var expected = + "{\"Message\":\"The request is invalid.\"," + + "\"ModelState\":{\"product.Name\":[\"Name is required.\"]}}"; + + var result = new InvalidModelStateResult(modelState, includeErrorDetail: false); + + // Act + await result.ExecuteResultAsync(context); + + // Assert + using (var reader = new StreamReader(stream)) + { + stream.Seek(0, SeekOrigin.Begin); + var content = reader.ReadToEnd(); + Assert.Equal(expected, content); + } + } + + private IServiceProvider CreateServices() + { + var services = new Mock(MockBehavior.Strict); + + var formatters = new Mock(MockBehavior.Strict); + formatters + .SetupGet(f => f.OutputFormatters) + .Returns(new List() { new JsonOutputFormatter(), }); + + services + .Setup(s => s.GetService(typeof(IOutputFormattersProvider))) + .Returns(formatters.Object); + + return services.Object; + } + } +} +#endif \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/NegotiatedContentResultTest.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/NegotiatedContentResultTest.cs new file mode 100644 index 0000000000..1788dd2013 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/NegotiatedContentResultTest.cs @@ -0,0 +1,89 @@ +// 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. + +#if ASPNET50 +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Threading.Tasks; +using Microsoft.AspNet.Mvc; +using Microsoft.AspNet.PipelineCore; +using Microsoft.AspNet.Routing; +using Moq; +using Xunit; + +namespace System.Web.Http +{ + public class NegotiatedContentResultTest + { + [Fact] + public async Task NegotiatedContentResult_SetsStatusCode() + { + // Arrange + var httpContext = new DefaultHttpContext(); + httpContext.RequestServices = CreateServices(); + + var stream = new MemoryStream(); + httpContext.Response.Body = stream; + + var context = new ActionContext(new RouteContext(httpContext), new ActionDescriptor()); + var result = new NegotiatedContentResult(HttpStatusCode.Ambiguous, new Product()); + + // Act + await result.ExecuteResultAsync(context); + + // Assert + Assert.Equal(300, context.HttpContext.Response.StatusCode); + } + + [Fact] + public async Task NegotiatedContentResult_WritesHttpError() + { + // Arrange + var httpContext = new DefaultHttpContext(); + httpContext.RequestServices = CreateServices(); + + var stream = new MemoryStream(); + httpContext.Response.Body = stream; + + var context = new ActionContext(new RouteContext(httpContext), new ActionDescriptor()); + var result = new NegotiatedContentResult(HttpStatusCode.Ambiguous, new Product()); + + // Act + await result.ExecuteResultAsync(context); + + // Assert + using (var reader = new StreamReader(stream)) + { + stream.Seek(0, SeekOrigin.Begin); + var content = reader.ReadToEnd(); + Assert.Equal("{\"Id\":0,\"Name\":null}", content); + } + } + + + private IServiceProvider CreateServices() + { + var services = new Mock(MockBehavior.Strict); + + var formatters = new Mock(MockBehavior.Strict); + formatters + .SetupGet(f => f.OutputFormatters) + .Returns(new List() { new JsonOutputFormatter(), }); + + services + .Setup(s => s.GetService(typeof(IOutputFormattersProvider))) + .Returns(formatters.Object); + + return services.Object; + } + + private class Product + { + public int Id { get; set; } + + public string Name { get; set; } + }; + } +} +#endif \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/OkNegotiatedContentResultTest.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/OkNegotiatedContentResultTest.cs new file mode 100644 index 0000000000..427c67413c --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/OkNegotiatedContentResultTest.cs @@ -0,0 +1,62 @@ +// 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. + +#if ASPNET50 +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Microsoft.AspNet.Mvc; +using Microsoft.AspNet.PipelineCore; +using Microsoft.AspNet.Routing; +using Moq; +using Xunit; + +namespace System.Web.Http +{ + public class OkNegotiatedContentResultTest + { + [Fact] + public async Task OkNegotiatedContentResult_SetsStatusCode() + { + // Arrange + var httpContext = new DefaultHttpContext(); + httpContext.RequestServices = CreateServices(); + + var stream = new MemoryStream(); + httpContext.Response.Body = stream; + + var context = new ActionContext(new RouteContext(httpContext), new ActionDescriptor()); + var result = new OkNegotiatedContentResult(new Product()); + + // Act + await result.ExecuteResultAsync(context); + + // Assert + Assert.Equal(200, context.HttpContext.Response.StatusCode); + } + + private IServiceProvider CreateServices() + { + var services = new Mock(MockBehavior.Strict); + + var formatters = new Mock(MockBehavior.Strict); + formatters + .SetupGet(f => f.OutputFormatters) + .Returns(new List() { new JsonOutputFormatter(), }); + + services + .Setup(s => s.GetService(typeof(IOutputFormattersProvider))) + .Returns(formatters.Object); + + return services.Object; + } + + private class Product + { + public int Id { get; set; } + + public string Name { get; set; } + }; + } +} +#endif \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/OkResultTest.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/OkResultTest.cs new file mode 100644 index 0000000000..f51bcc037e --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/OkResultTest.cs @@ -0,0 +1,28 @@ +// 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.Threading.Tasks; +using Microsoft.AspNet.Mvc; +using Microsoft.AspNet.PipelineCore; +using Microsoft.AspNet.Routing; +using Xunit; + +namespace System.Web.Http +{ + public class OkResultTest + { + [Fact] + public async Task OkResult_SetsStatusCode() + { + // Arrange + var context = new ActionContext(new RouteContext(new DefaultHttpContext()), new ActionDescriptor()); + var result = new OkResult(); + + // Act + await result.ExecuteResultAsync(context); + + // Assert + Assert.Equal(200, context.HttpContext.Response.StatusCode); + } + } +} diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ApiControllerTest.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ApiControllerTest.cs index 1aa96e5cac..4b15bdddc3 100644 --- a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ApiControllerTest.cs +++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ApiControllerTest.cs @@ -1,10 +1,15 @@ // 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.Net; +using System.Net.Http; using System.Security.Claims; +using System.Text; using Microsoft.AspNet.Mvc; +using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.PipelineCore; using Microsoft.AspNet.Routing; +using Newtonsoft.Json; using Xunit; namespace System.Web.Http @@ -44,6 +49,385 @@ namespace System.Web.Http Assert.Null(controller.User); } + [Fact] + public void ApiController_BadRequest() + { + // Arrange + var controller = new ConcreteApiController(); + + // Act + var result = controller.BadRequest(); + + // Assert + Assert.Equal(400, Assert.IsType(result).StatusCode); + } + + [Fact] + public void ApiController_BadRequest_Message() + { + // Arrange + var controller = new ConcreteApiController(); + + // Act + var result = controller.BadRequest("Error"); + + // Assert + var badRequest = Assert.IsType(result); + Assert.Equal("Error", badRequest.Message); + + var httpError = Assert.IsType(badRequest.Value); + Assert.Equal("Error", httpError.Message); + } + + [Fact] + public void ApiController_BadRequest_ModelState() + { + // Arrange + var controller = new ConcreteApiController(); + + var modelState = new ModelStateDictionary(); + modelState.AddModelError("product.Name", "Name is required"); + + // Act + var result = controller.BadRequest(modelState); + + // Assert + var badRequest = Assert.IsType(result); + + var modelError = Assert.IsType(badRequest.Value).ModelState; + Assert.Equal(new string[] { "Name is required" }, modelError["product.Name"]); + } + + [Fact] + public void ApiController_Created_Uri() + { + // Arrange + var controller = new ConcreteApiController(); + + var uri = new Uri("http://contoso.com"); + var product = new Product(); + + // Act + var result = controller.Created(uri, product); + + // Assert + var created = Assert.IsType>(result); + Assert.Same(product, created.Content); + Assert.Same(uri, created.Location); + } + + [Theory] + [InlineData("http://contoso.com/Api/Products")] + [InlineData("/Api/Products")] + [InlineData("Products")] + public void ApiController_Created_String(string uri) + { + // Arrange + var controller = new ConcreteApiController(); + + var product = new Product(); + + // Act + var result = controller.Created(uri, product); + + // Assert + var created = Assert.IsType>(result); + Assert.Same(product, created.Content); + Assert.Equal(uri, created.Location.OriginalString); + } + + [Fact] + public void ApiController_CreatedAtRoute() + { + // Arrange + var controller = new ConcreteApiController(); + + var product = new Product(); + + // Act + var result = controller.CreatedAtRoute("api_route", new { controller = "Products" }, product); + + // Assert + var created = Assert.IsType>(result); + Assert.Same(product, created.Content); + Assert.Equal("api_route", created.RouteName); + Assert.Equal("Products", created.RouteValues["controller"]); + } + + [Fact] + public void ApiController_CreatedAtRoute_Dictionary() + { + // Arrange + var controller = new ConcreteApiController(); + + var product = new Product(); + var values = new RouteValueDictionary(new { controller = "Products" }); + + // Act + var result = controller.CreatedAtRoute("api_route", values, product); + + // Assert + var created = Assert.IsType>(result); + Assert.Same(product, created.Content); + Assert.Equal("api_route", created.RouteName); + Assert.Equal("Products", created.RouteValues["controller"]); + Assert.Same(values, created.RouteValues); + } + + [Fact] + public void ApiController_Conflict() + { + // Arrange + var controller = new ConcreteApiController(); + + // Act + var result = controller.Conflict(); + + // Assert + Assert.Equal(409, Assert.IsType(result).StatusCode); + } + + [Fact] + public void ApiController_Content() + { + // Arrange + var controller = new ConcreteApiController(); + + var content = new Product(); + + // Act + var result = controller.Content(HttpStatusCode.Found, content); + + // Assert + var contentResult = Assert.IsType>(result); + Assert.Equal(HttpStatusCode.Found, contentResult.StatusCode); + Assert.Equal(content, contentResult.Value); + } + + [Fact] + public void ApiController_InternalServerError() + { + // Arrange + var controller = new ConcreteApiController(); + + // Act + var result = controller.InternalServerError(); + + // Assert + Assert.Equal(500, Assert.IsType(result).StatusCode); + } + + [Fact] + public void ApiController_InternalServerError_Exception() + { + // Arrange + var controller = new ConcreteApiController(); + var exception = new ArgumentException(); + + // Act + var result = controller.InternalServerError(exception); + + // Assert + var exceptionResult = Assert.IsType(result); + Assert.Same(exception, exceptionResult.Exception); + } + + [Fact] + public void ApiController_Json() + { + // Arrange + var controller = new ConcreteApiController(); + var product = new Product(); + + // Act + var result = controller.Json(product); + + // Assert + var objectResult = Assert.IsType(result); + Assert.Same(product, objectResult.Value); + } + + [Fact] + public void ApiController_Json_Settings() + { + // Arrange + var controller = new ConcreteApiController(); + var product = new Product(); + var settings = new JsonSerializerSettings(); + + // Act + var result = controller.Json(product, settings); + + // Assert + var objectResult = Assert.IsType(result); + Assert.Same(product, objectResult.Value); + + var formatter = Assert.IsType(Assert.Single(objectResult.Formatters)); + Assert.Same(settings, formatter.SerializerSettings); + } + + [Fact] + public void ApiController_Json_Settings_Encoding() + { + // Arrange + var controller = new ConcreteApiController(); + var product = new Product(); + var settings = new JsonSerializerSettings(); + + // Act + var result = controller.Json(product, settings, Encoding.UTF8); + + // Assert + var objectResult = Assert.IsType(result); + Assert.Same(product, objectResult.Value); + + var formatter = Assert.IsType(Assert.Single(objectResult.Formatters)); + Assert.Same(settings, formatter.SerializerSettings); + Assert.Same(Encoding.UTF8, Assert.Single(formatter.SupportedEncodings)); + } + + [Fact] + public void ApiController_NotFound() + { + // Arrange + var controller = new ConcreteApiController(); + + // Act + var result = controller.NotFound(); + + // Assert + Assert.Equal(404, Assert.IsType(result).StatusCode); + } + + [Fact] + public void ApiController_Ok() + { + // Arrange + var controller = new ConcreteApiController(); + + // Act + var result = controller.Ok(); + + // Assert + Assert.Equal(200, Assert.IsType(result).StatusCode); + } + + + [Fact] + public void ApiController_Ok_Content() + { + // Arrange + var controller = new ConcreteApiController(); + var product = new Product(); + + // Act + var result = controller.Ok(product); + + // Assert + var okResult = Assert.IsType>(result); + Assert.Same(product, okResult.Content); + } + + [Fact] + public void ApiController_Redirect() + { + // Arrange + var controller = new ConcreteApiController(); + + var uri = new Uri("http://contoso.com"); + + // Act + var result = controller.Redirect(uri); + + // Assert + var redirect = Assert.IsType(result); + Assert.Equal(uri.AbsoluteUri, result.Url); + } + + [Theory] + [InlineData("http://contoso.com/Api/Products")] + public void ApiController_Redirect_String(string uri) + { + // Arrange + var controller = new ConcreteApiController(); + + // Act + var result = controller.Redirect(uri); + + // Assert + var redirect = Assert.IsType(result); + Assert.Equal(uri, result.Url); + } + + [Fact] + public void ApiController_RedirectToRoute() + { + // Arrange + var controller = new ConcreteApiController(); + + // Act + var result = controller.RedirectToRoute("api_route", new { controller = "Products" }); + + // Assert + var created = Assert.IsType(result); + Assert.Equal("api_route", created.RouteName); + Assert.Equal("Products", created.RouteValues["controller"]); + } + + [Fact] + public void ApiController_RedirectToRoute_Dictionary() + { + // Arrange + var controller = new ConcreteApiController(); + + var product = new Product(); + var values = new RouteValueDictionary(new { controller = "Products" }); + + // Act + var result = controller.RedirectToRoute("api_route", values); + + // Assert + var created = Assert.IsType(result); + Assert.Equal("api_route", created.RouteName); + Assert.Equal("Products", created.RouteValues["controller"]); + } + + [Fact] + public void ApiController_ResponseMessage() + { + // Arrange + var controller = new ConcreteApiController(); + + var response = new HttpResponseMessage(HttpStatusCode.NoContent); + + // Act + var result = controller.ResponseMessage(response); + + // Assert + var responseResult = Assert.IsType(result); + Assert.Same(response, responseResult.Response); + } + + [Fact] + public void ApiController_StatusCode() + { + // Arrange + var controller = new ConcreteApiController(); + + // Act + var result = controller.StatusCode(HttpStatusCode.ExpectationFailed); + + // Assert + Assert.Equal(417, Assert.IsType(result).StatusCode); + } + + private class Product + { + public string Name { get; set; } + + public int Id { get; set; } + } + private class ConcreteApiController : ApiController { } diff --git a/test/WebSites/WebApiCompatShimWebSite/Controllers/ActionResults/ActionResultController.cs b/test/WebSites/WebApiCompatShimWebSite/Controllers/ActionResults/ActionResultController.cs new file mode 100644 index 0000000000..282dbaf2c5 --- /dev/null +++ b/test/WebSites/WebApiCompatShimWebSite/Controllers/ActionResults/ActionResultController.cs @@ -0,0 +1,153 @@ +// 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.Http; +using System.Threading.Tasks; +using System.Web.Http; +using Microsoft.AspNet.Http; +using Microsoft.AspNet.Mvc; +using System.Net; +using System.Net.Http.Formatting; +using Newtonsoft.Json; +using System.Text; + +namespace WebApiCompatShimWebSite +{ + public class ActionResultController : ApiController + { + public IActionResult GetBadRequest() + { + return BadRequest(); + } + + public IActionResult GetBadRequestMessage() + { + return BadRequest("Hello, world!"); + } + + public IActionResult GetBadRequestModelState() + { + ModelState.AddModelError("product.Name", "Name is required."); + return BadRequest(ModelState); + } + + public IActionResult GetConflict() + { + return Conflict(); + } + + public IActionResult GetContent() + { + return Content(HttpStatusCode.Ambiguous, CreateUser()); + } + + public IActionResult GetCreatedRelative() + { + return Created("5", CreateUser()); + } + + public IActionResult GetCreatedAbsolute() + { + return Created("/api/Blog/ActionResult/GetUser/5", CreateUser()); + } + + public IActionResult GetCreatedQualified() + { + return Created("http://localhost/api/Blog/ActionResult/5", CreateUser()); + } + + public IActionResult GetCreatedUri() + { + return Created(new Uri("/api/Blog/ActionResult/GetUser/5", UriKind.Relative), CreateUser()); + } + + public IActionResult GetCreatedAtRoute() + { + var values = new { controller = "ActionResult", action = "GetUser", id = 5 }; + return CreatedAtRoute("named-action", values, CreateUser()); + } + + public IActionResult GetInternalServerError() + { + return InternalServerError(); + } + + public IActionResult GetInternalServerErrorException() + { + return InternalServerError(new Exception("Error not passed to client.")); + } + + public IActionResult GetJson() + { + return Json(CreateUser()); + } + + public IActionResult GetJsonSettings() + { + return Json(CreateUser(), new JsonSerializerSettings() { Formatting = Formatting.Indented }); + } + + public IActionResult GetJsonSettingsEncoding() + { + return Json( + CreateUser(), + new JsonSerializerSettings() { Formatting = Formatting.Indented }, + Encoding.UTF32); + } + + public IActionResult GetNotFound() + { + return NotFound(); + } + + public IActionResult GetOk() + { + return Ok(); + } + + public IActionResult GetOkContent() + { + return Ok(CreateUser()); + } + + public IActionResult GetRedirectString() + { + // strings must be absolute URIs + return Redirect("http://localhost/api/Users"); + } + + public IActionResult GetRedirectUri() + { + // Uris can be absolute or relative + return Redirect(new Uri("api/Blog", UriKind.RelativeOrAbsolute)); + } + + public IActionResult GetResponseMessage() + { + var response = new HttpResponseMessage(HttpStatusCode.OK); + response.Headers.Add("X-Test", "Hello"); + + return ResponseMessage(response); + } + + public IActionResult GetStatusCode() + { + return StatusCode(HttpStatusCode.PaymentRequired); + } + + // Used for generating links + public User GetUser(int id) + { + return CreateUser(); + } + + private User CreateUser() + { + return new User() + { + Name = "Test User", + }; + } + } +} \ No newline at end of file