// Copyright (c) .NET Foundation. 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.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.AspNetCore.Testing; using Microsoft.AspNetCore.Testing.xunit; using Newtonsoft.Json; using Xunit; namespace Microsoft.AspNetCore.Mvc.FunctionalTests { public class WebApiCompatShimBasicTest : IClassFixture> { public WebApiCompatShimBasicTest(MvcTestFixture fixture) { Client = fixture.Client; } public HttpClient Client { get; } [Fact] public async Task ApiController_Activates_HttpContextAndUser() { // Arrange & Act var response = await Client.GetAsync("http://localhost/api/Blog/BasicApi/WriteToHttpContext"); var content = await response.Content.ReadAsStringAsync(); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal( "Hello, Anonymous User from WebApiCompatShimWebSite.BasicApiController.WriteToHttpContext (WebApiCompatShimWebSite)", content); } [Fact] public async Task ApiController_Activates_UrlHelper() { // Arrange & Act var response = await Client.GetAsync("http://localhost/api/Blog/BasicApi/GenerateUrl"); var content = await response.Content.ReadAsStringAsync(); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal( "Visited: /api/Blog/BasicApi/GenerateUrl", content); } [Fact] public async Task Options_SetsDefaultFormatters() { // Arrange var expected = new string[] { typeof(JsonMediaTypeFormatter).FullName, typeof(XmlMediaTypeFormatter).FullName, #if !NETSTANDARDAPP1_5 typeof(FormUrlEncodedMediaTypeFormatter).FullName, #endif }; // Act var response = await Client.GetAsync("http://localhost/api/Blog/BasicApi/GetFormatters"); var content = await response.Content.ReadAsStringAsync(); var formatters = JsonConvert.DeserializeObject(content); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(expected, formatters); } [Fact] public async Task ActionThrowsHttpResponseException_WithStatusCode() { // Arrange & Act var response = await Client.GetAsync( "http://localhost/api/Blog/HttpResponseException/ThrowsHttpResponseExceptionWithHttpStatusCode"); // Assert Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); Assert.Equal(string.Empty, content); } [Fact] public async Task ActionThrowsHttpResponseException_WithResponse() { // Arrange & Act var response = await Client.GetAsync( "http://localhost/api/Blog/HttpResponseException" + "/ThrowsHttpResponseExceptionWithHttpResponseMessage?message=send some message"); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); Assert.Equal("send some message", content); } [Fact] public async Task ActionThrowsHttpResponseException_EnsureGlobalHttpresponseExceptionActionFilter_IsInvoked() { // Arrange & Act var response = await Client.GetAsync( "http://localhost/api/Blog/HttpResponseException/ThrowsHttpResponseExceptionEnsureGlobalFilterRunsLast"); // Assert // Ensure we do not get a no content result. Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); Assert.Equal(string.Empty, content); } [Fact] public async Task ActionThrowsHttpResponseException_EnsureGlobalFilterConvention_IsApplied() { // Arrange & Act var response = await Client.GetAsync( "http://localhost/api/Blog/" + "HttpResponseException/ThrowsHttpResponseExceptionInjectAFilterToHandleHttpResponseException"); // Assert // Ensure we do get a no content result. Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); Assert.Equal(string.Empty, content); } [Fact] public async Task ApiController_CanValidateCustomObjectWithPrefix_Fails() { // Arrange & Act var response = await Client.GetStringAsync( "http://localhost/api/Blog/BasicApi/ValidateObjectWithPrefixFails?prefix=prefix"); // Assert var json = JsonConvert.DeserializeObject>(response); Assert.Equal(1, json.Count); Assert.Equal("The field ID must be between 0 and 100.", json["prefix.ID"]); } [Fact] public async Task ApiController_CanValidateCustomObject_IsSuccessFul() { // Arrange & Act var response = await Client.GetStringAsync("http://localhost/api/Blog/BasicApi/ValidateObject_Passes"); // Assert Assert.Equal("true", response); } [Fact] public async Task ApiController_CanValidateCustomObject_Fails() { // Arrange & Act var response = await Client.GetStringAsync("http://localhost/api/Blog/BasicApi/ValidateObjectFails"); // Assert var json = JsonConvert.DeserializeObject>(response); Assert.Equal(1, json.Count); Assert.Equal("The field ID must be between 0 and 100.", json["ID"]); } [ConditionalFact] // Mono issue - https://github.com/aspnet/External/issues/24 [FrameworkSkipCondition(RuntimeFrameworks.Mono)] public async Task ApiController_RequestProperty() { // Arrange var expected = "POST http://localhost/api/Blog/HttpRequestMessage/EchoProperty localhost " + "13 Hello, world!"; // Act var response = await Client.PostAsync( "http://localhost/api/Blog/HttpRequestMessage/EchoProperty", new StringContent("Hello, world!")); var content = await response.Content.ReadAsStringAsync(); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(expected, content); } [ConditionalFact] // Mono issue - https://github.com/aspnet/External/issues/24 [FrameworkSkipCondition(RuntimeFrameworks.Mono)] public async Task ApiController_RequestParameter() { // Arrange var expected = "POST http://localhost/api/Blog/HttpRequestMessage/EchoParameter localhost " + "17 Hello, the world!"; // Act var response = await Client.PostAsync( "http://localhost/api/Blog/HttpRequestMessage/EchoParameter", new StringContent("Hello, the world!")); var content = await response.Content.ReadAsStringAsync(); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(expected, content); } [Fact] public async Task ApiController_ResponseReturned() { // Arrange var expected = "POST Hello, HttpResponseMessage world!"; // Act var response = await Client.PostAsync( "http://localhost/api/Blog/HttpRequestMessage/EchoWithResponseMessage", new StringContent("Hello, HttpResponseMessage world!")); var content = await response.Content.ReadAsStringAsync(); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal(expected, content); IEnumerable values; Assert.True(response.Headers.TryGetValues("X-Test", out values)); Assert.Equal(new string[] { "Hello!" }, values); Assert.Equal(38, response.Content.Headers.ContentLength); } [Fact] public async Task ApiController_ExplicitChunkedEncoding_IsIgnored() { // Arrange var expected = "POST Hello, HttpResponseMessage world!"; var request = new HttpRequestMessage(); request.Method = HttpMethod.Post; request.RequestUri = new Uri("http://localhost/api/Blog/HttpRequestMessage/EchoWithResponseMessageChunked"); request.Content = new StringContent("Hello, HttpResponseMessage world!"); // Act // HttpClient buffers the response by default and this would set the Content-Length header and so // this will not provide us accurate information as to whether the server set the header or // the client. So here we explicitly mention to only read the headers and not the body. var response = await Client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.NotNull(response.Content); if (!TestPlatformHelper.IsMono) { // Mono issue - https://github.com/aspnet/External/issues/20 Assert.NotNull(response.Content.Headers.ContentLength); } Assert.Null(response.Headers.TransferEncodingChunked); // When HttpClient by default reads and buffers the response body, it disposes the // response stream for us. But since we are reading the content explicitly, we need // to close it. var responseStream = await response.Content.ReadAsStreamAsync(); using (var streamReader = new StreamReader(responseStream)) { Assert.Equal(expected, streamReader.ReadToEnd()); } IEnumerable values; Assert.True(response.Headers.TryGetValues("X-Test", out values)); Assert.Equal(new string[] { "Hello!" }, values); } [Theory] [InlineData("application/json", "application/json")] [InlineData("text/xml", "text/xml")] [InlineData("text/plain, text/xml; q=0.5", "text/xml")] [InlineData("application/*", "application/json")] public async Task ApiController_CreateResponse_Conneg(string accept, string mediaType) { // Arrange var request = new HttpRequestMessage( HttpMethod.Get, "http://localhost/api/Blog/HttpRequestMessage/GetUser"); request.Headers.Accept.ParseAdd(accept); // Act var response = await Client.SendAsync(request); var user = await response.Content.ReadAsAsync(); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("Test User", user.Name); Assert.Equal(mediaType, response.Content.Headers.ContentType.MediaType); } [Theory] [InlineData("application/json")] [InlineData("text/xml")] public async Task ApiController_CreateResponse_HardcodedMediaType(string mediaType) { // Arrange var request = new HttpRequestMessage( HttpMethod.Get, "http://localhost/api/Blog/HttpRequestMessage/GetUser?mediaType=" + mediaType); // Act var response = await Client.SendAsync(request); var user = await response.Content.ReadAsAsync(); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("Test User", user.Name); Assert.Equal(mediaType, response.Content.Headers.ContentType.MediaType); } [Theory] [InlineData("application/json", "application/json")] [InlineData("text/xml", "text/xml")] [InlineData("text/plain, text/xml; q=0.5", "text/xml")] [InlineData("application/*", "application/json")] public async Task ApiController_CreateResponse_Conneg_Error(string accept, string mediaType) { // Arrange var request = new HttpRequestMessage( HttpMethod.Get, "http://localhost/api/Blog/HttpRequestMessage/Fail"); request.Headers.Accept.ParseAdd(accept); // Act var response = await Client.SendAsync(request); var error = await response.Content.ReadAsAsync(); // Assert Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); Assert.Equal("It failed.", error.Message); Assert.Equal(mediaType, response.Content.Headers.ContentType.MediaType); } [Fact] public async Task ApiController_CreateResponse_HardcodedFormatter() { // Arrange var request = new HttpRequestMessage( HttpMethod.Get, "http://localhost/api/Blog/HttpRequestMessage/GetUserJson"); // Accept header will be ignored request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/xml")); // Act var response = await Client.SendAsync(request); var user = await response.Content.ReadAsAsync(); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("Test User", user.Name); Assert.Equal("text/json", response.Content.Headers.ContentType.MediaType); } [Theory] [InlineData("http://localhost/Mvc/Index", HttpStatusCode.OK)] [InlineData("http://localhost/api/Blog/Mvc/Index", HttpStatusCode.NotFound)] public async Task WebApiRouting_AccessMvcController(string url, HttpStatusCode expected) { // Arrange var request = new HttpRequestMessage(HttpMethod.Get, url); // Act var response = await Client.SendAsync(request); // Assert Assert.Equal(expected, response.StatusCode); } [Theory] [InlineData("http://localhost/BasicApi/GenerateUrl", HttpStatusCode.NotFound)] [InlineData("http://localhost/api/Blog/BasicApi/GenerateUrl", HttpStatusCode.OK)] public async Task WebApiRouting_AccessWebApiController(string url, HttpStatusCode expected) { // Arrange var request = new HttpRequestMessage(HttpMethod.Get, url); // Act var response = await Client.SendAsync(request); // Assert Assert.Equal(expected, response.StatusCode); } [Fact] public async Task ApiController_Returns_ByteArrayContent() { // Arrange var expectedBody = "Hello from ByteArrayContent!!"; // Act var response = await Client.GetAsync("http://localhost/api/Blog/HttpRequestMessage/ReturnByteArrayContent"); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.NotNull(response.Content); Assert.Equal("text/plain", response.Content.Headers.ContentType.MediaType); var actualBody = await response.Content.ReadAsStringAsync(); Assert.Equal(expectedBody, actualBody); } [Fact] public async Task ApiController_Returns_StreamContent() { // Arrange var expectedBody = "This content is from a file"; // Act var response = await Client.GetAsync("http://localhost/api/Blog/HttpRequestMessage/ReturnStreamContent"); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.NotNull(response.Content); Assert.Equal("image/jpeg", response.Content.Headers.ContentType.MediaType); Assert.NotNull(response.Content.Headers.ContentDisposition); Assert.Equal("attachment", response.Content.Headers.ContentDisposition.DispositionType); var actualBody = await response.Content.ReadAsStringAsync(); Assert.Equal(expectedBody, actualBody); } [Theory] [InlineData("ReturnPushStreamContent", "Hello from PushStreamContent!!")] [InlineData("ReturnPushStreamContentSync", "Hello from PushStreamContent Sync!!")] public async Task ApiController_Returns_PushStreamContent(string action, string expectedBody) { // Arrange & Act var response = await Client.GetAsync("http://localhost/api/Blog/HttpRequestMessage/" + action); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.NotNull(response.Content); Assert.Equal("application/pdf", response.Content.Headers.ContentType.MediaType); var actualBody = await response.Content.ReadAsStringAsync(); Assert.Equal(expectedBody, actualBody); } [Fact] public async Task ApiController_Returns_PushStreamContentWithCustomHeaders() { // Arrange var expectedBody = "Hello from PushStreamContent with custom headers!!"; var multipleValues = new[] { "value1", "value2" }; // Act var response = await Client.GetAsync( "http://localhost/api/Blog/HttpRequestMessage/ReturnPushStreamContentWithCustomHeaders"); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.NotNull(response.Content); Assert.Equal("application/octet-stream", response.Content.Headers.ContentType.ToString()); var actualBody = await response.Content.ReadAsStringAsync(); Assert.Equal(expectedBody, actualBody); Assert.Equal(multipleValues, response.Headers.GetValues("Multiple")); } } }