Remove WebApiCompatShim

This commit is contained in:
Pranav K 2018-11-08 17:28:31 -08:00
parent b18526cdc8
commit ecb7edadc8
87 changed files with 5 additions and 13653 deletions

View File

@ -28,10 +28,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Co
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Test", "test\Microsoft.AspNetCore.Mvc.Test\Microsoft.AspNetCore.Mvc.Test.csproj", "{5F945B82-FE5F-425C-956C-8BC2F2020254}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.WebApiCompatShim", "src\Microsoft.AspNetCore.Mvc.WebApiCompatShim\Microsoft.AspNetCore.Mvc.WebApiCompatShim.csproj", "{23D30B8C-04B1-4577-A604-ED27EA1E4A0E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.WebApiCompatShimTest", "test\Microsoft.AspNetCore.Mvc.WebApiCompatShimTest\Microsoft.AspNetCore.Mvc.WebApiCompatShimTest.csproj", "{5DE8E4D9-AACD-4B5F-819F-F091383FB996}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.TagHelpers", "src\Microsoft.AspNetCore.Mvc.TagHelpers\Microsoft.AspNetCore.Mvc.TagHelpers.csproj", "{B2347320-308E-4D2B-AEC8-005DFA68B0C9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.TagHelpers.Test", "test\Microsoft.AspNetCore.Mvc.TagHelpers.Test\Microsoft.AspNetCore.Mvc.TagHelpers.Test.csproj", "{860119ED-3DB1-424D-8D0A-30132A8A7D96}"
@ -193,26 +189,6 @@ Global
{5F945B82-FE5F-425C-956C-8BC2F2020254}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{5F945B82-FE5F-425C-956C-8BC2F2020254}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{5F945B82-FE5F-425C-956C-8BC2F2020254}.Release|x86.ActiveCfg = Release|Any CPU
{23D30B8C-04B1-4577-A604-ED27EA1E4A0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{23D30B8C-04B1-4577-A604-ED27EA1E4A0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{23D30B8C-04B1-4577-A604-ED27EA1E4A0E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{23D30B8C-04B1-4577-A604-ED27EA1E4A0E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{23D30B8C-04B1-4577-A604-ED27EA1E4A0E}.Debug|x86.ActiveCfg = Debug|Any CPU
{23D30B8C-04B1-4577-A604-ED27EA1E4A0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{23D30B8C-04B1-4577-A604-ED27EA1E4A0E}.Release|Any CPU.Build.0 = Release|Any CPU
{23D30B8C-04B1-4577-A604-ED27EA1E4A0E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{23D30B8C-04B1-4577-A604-ED27EA1E4A0E}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{23D30B8C-04B1-4577-A604-ED27EA1E4A0E}.Release|x86.ActiveCfg = Release|Any CPU
{5DE8E4D9-AACD-4B5F-819F-F091383FB996}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5DE8E4D9-AACD-4B5F-819F-F091383FB996}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5DE8E4D9-AACD-4B5F-819F-F091383FB996}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{5DE8E4D9-AACD-4B5F-819F-F091383FB996}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{5DE8E4D9-AACD-4B5F-819F-F091383FB996}.Debug|x86.ActiveCfg = Debug|Any CPU
{5DE8E4D9-AACD-4B5F-819F-F091383FB996}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5DE8E4D9-AACD-4B5F-819F-F091383FB996}.Release|Any CPU.Build.0 = Release|Any CPU
{5DE8E4D9-AACD-4B5F-819F-F091383FB996}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{5DE8E4D9-AACD-4B5F-819F-F091383FB996}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{5DE8E4D9-AACD-4B5F-819F-F091383FB996}.Release|x86.ActiveCfg = Release|Any CPU
{B2347320-308E-4D2B-AEC8-005DFA68B0C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B2347320-308E-4D2B-AEC8-005DFA68B0C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B2347320-308E-4D2B-AEC8-005DFA68B0C9}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@ -628,8 +604,6 @@ Global
{3F6E355E-4869-41D9-943B-D54771221A7F} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{A8AA326E-8EE8-4F11-B750-23028E0949D7} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{5F945B82-FE5F-425C-956C-8BC2F2020254} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{23D30B8C-04B1-4577-A604-ED27EA1E4A0E} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
{5DE8E4D9-AACD-4B5F-819F-F091383FB996} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{B2347320-308E-4D2B-AEC8-005DFA68B0C9} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
{860119ED-3DB1-424D-8D0A-30132A8A7D96} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{F504357E-C2E1-4818-BA5C-9A2EAC25FEE5} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}

39
Mvc.sln
View File

@ -53,12 +53,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FilesWebSite", "test\WebSit
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApplicationModelWebSite", "test\WebSites\ApplicationModelWebSite\ApplicationModelWebSite.csproj", "{CAE52CB7-0FAC-4B5B-8251-B0FF837DB657}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.WebApiCompatShim", "src\Microsoft.AspNetCore.Mvc.WebApiCompatShim\Microsoft.AspNetCore.Mvc.WebApiCompatShim.csproj", "{23D30B8C-04B1-4577-A604-ED27EA1E4A0E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApiCompatShimWebSite", "test\WebSites\WebApiCompatShimWebSite\WebApiCompatShimWebSite.csproj", "{B2B7BC91-688E-4C1E-A71F-CE948D958DDF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.WebApiCompatShimTest", "test\Microsoft.AspNetCore.Mvc.WebApiCompatShimTest\Microsoft.AspNetCore.Mvc.WebApiCompatShimTest.csproj", "{5DE8E4D9-AACD-4B5F-819F-F091383FB996}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.TagHelpers", "src\Microsoft.AspNetCore.Mvc.TagHelpers\Microsoft.AspNetCore.Mvc.TagHelpers.csproj", "{B2347320-308E-4D2B-AEC8-005DFA68B0C9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.TagHelpers.Test", "test\Microsoft.AspNetCore.Mvc.TagHelpers.Test\Microsoft.AspNetCore.Mvc.TagHelpers.Test.csproj", "{860119ED-3DB1-424D-8D0A-30132A8A7D96}"
@ -354,36 +348,6 @@ Global
{CAE52CB7-0FAC-4B5B-8251-B0FF837DB657}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{CAE52CB7-0FAC-4B5B-8251-B0FF837DB657}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{CAE52CB7-0FAC-4B5B-8251-B0FF837DB657}.Release|x86.ActiveCfg = Release|Any CPU
{23D30B8C-04B1-4577-A604-ED27EA1E4A0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{23D30B8C-04B1-4577-A604-ED27EA1E4A0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{23D30B8C-04B1-4577-A604-ED27EA1E4A0E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{23D30B8C-04B1-4577-A604-ED27EA1E4A0E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{23D30B8C-04B1-4577-A604-ED27EA1E4A0E}.Debug|x86.ActiveCfg = Debug|Any CPU
{23D30B8C-04B1-4577-A604-ED27EA1E4A0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{23D30B8C-04B1-4577-A604-ED27EA1E4A0E}.Release|Any CPU.Build.0 = Release|Any CPU
{23D30B8C-04B1-4577-A604-ED27EA1E4A0E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{23D30B8C-04B1-4577-A604-ED27EA1E4A0E}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{23D30B8C-04B1-4577-A604-ED27EA1E4A0E}.Release|x86.ActiveCfg = Release|Any CPU
{B2B7BC91-688E-4C1E-A71F-CE948D958DDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B2B7BC91-688E-4C1E-A71F-CE948D958DDF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B2B7BC91-688E-4C1E-A71F-CE948D958DDF}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{B2B7BC91-688E-4C1E-A71F-CE948D958DDF}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{B2B7BC91-688E-4C1E-A71F-CE948D958DDF}.Debug|x86.ActiveCfg = Debug|Any CPU
{B2B7BC91-688E-4C1E-A71F-CE948D958DDF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B2B7BC91-688E-4C1E-A71F-CE948D958DDF}.Release|Any CPU.Build.0 = Release|Any CPU
{B2B7BC91-688E-4C1E-A71F-CE948D958DDF}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{B2B7BC91-688E-4C1E-A71F-CE948D958DDF}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{B2B7BC91-688E-4C1E-A71F-CE948D958DDF}.Release|x86.ActiveCfg = Release|Any CPU
{5DE8E4D9-AACD-4B5F-819F-F091383FB996}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5DE8E4D9-AACD-4B5F-819F-F091383FB996}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5DE8E4D9-AACD-4B5F-819F-F091383FB996}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{5DE8E4D9-AACD-4B5F-819F-F091383FB996}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{5DE8E4D9-AACD-4B5F-819F-F091383FB996}.Debug|x86.ActiveCfg = Debug|Any CPU
{5DE8E4D9-AACD-4B5F-819F-F091383FB996}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5DE8E4D9-AACD-4B5F-819F-F091383FB996}.Release|Any CPU.Build.0 = Release|Any CPU
{5DE8E4D9-AACD-4B5F-819F-F091383FB996}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{5DE8E4D9-AACD-4B5F-819F-F091383FB996}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{5DE8E4D9-AACD-4B5F-819F-F091383FB996}.Release|x86.ActiveCfg = Release|Any CPU
{B2347320-308E-4D2B-AEC8-005DFA68B0C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B2347320-308E-4D2B-AEC8-005DFA68B0C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B2347320-308E-4D2B-AEC8-005DFA68B0C9}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@ -1002,9 +966,6 @@ Global
{6DB9B8D0-80F7-4E70-BBB0-0B4C04D79A47} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{0EF9860B-10D7-452F-B0F4-A405B88BEBB3} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{CAE52CB7-0FAC-4B5B-8251-B0FF837DB657} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{23D30B8C-04B1-4577-A604-ED27EA1E4A0E} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
{B2B7BC91-688E-4C1E-A71F-CE948D958DDF} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{5DE8E4D9-AACD-4B5F-819F-F091383FB996} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{B2347320-308E-4D2B-AEC8-005DFA68B0C9} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
{860119ED-3DB1-424D-8D0A-30132A8A7D96} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{920F8A0E-6F7D-4BBE-84FF-840B89099BE6} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}

View File

@ -18,7 +18,6 @@ using Microsoft.AspNetCore.Mvc.Formatters;
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.TagHelpers, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Testing, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.ViewFeatures, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.WebApiCompatShim, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Abstractions.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.ApiExplorer.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
@ -36,7 +35,6 @@ using Microsoft.AspNetCore.Mvc.Formatters;
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.ViewFeatures.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Views.TestCommon, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.WebApiCompatShimTest, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Performance, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]

View File

@ -1,580 +0,0 @@
// 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.Net;
using System.Net.Http;
using System.Security.Principal;
using System.Text;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Mvc.WebApiCompatShim;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
namespace System.Web.Http
{
[UseWebApiRoutes]
[UseWebApiActionConventions]
[UseWebApiParameterConventions]
[UseWebApiOverloading]
[Controller]
public abstract class ApiController : IDisposable
{
private ControllerContext _controllerContext;
private HttpRequestMessage _request;
private IModelMetadataProvider _metadataProvider;
private IObjectModelValidator _objectValidator;
private IUrlHelper _urlHelper;
/// <summary>
/// Gets the <see cref="Microsoft.AspNetCore.Mvc.ActionContext"/>.
/// </summary>
public ActionContext ActionContext => ControllerContext;
/// <summary>
/// Gets or sets the <see cref="ControllerContext"/>.
/// </summary>
/// <remarks>The setter is intended for unit testing purposes only.</remarks>
[ControllerContext]
public ControllerContext ControllerContext
{
get
{
if (_controllerContext == null)
{
_controllerContext = new ControllerContext();
}
return _controllerContext;
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_controllerContext = value;
}
}
/// <summary>
/// Gets the http context.
/// </summary>
public HttpContext Context
{
get
{
return ControllerContext.HttpContext;
}
}
/// <summary>
/// Gets the <see cref="IModelMetadataProvider"/>.
/// </summary>
/// <remarks>The setter is intended for unit testing purposes only.</remarks>
public IModelMetadataProvider MetadataProvider
{
get
{
if (_metadataProvider == null)
{
_metadataProvider = Context?.RequestServices.GetRequiredService<IModelMetadataProvider>();
}
return _metadataProvider;
}
set
{
_metadataProvider = value;
}
}
/// <summary>
/// Gets or sets the <see cref="IObjectModelValidator"/>.
/// </summary>
public IObjectModelValidator ObjectValidator
{
get
{
if (_objectValidator == null)
{
_objectValidator = Context?.RequestServices.GetRequiredService<IObjectModelValidator>();
}
return _objectValidator;
}
set
{
_objectValidator = value;
}
}
/// <summary>
/// Gets model state after the model binding process. This ModelState will be empty before model binding
/// happens.
/// </summary>
public ModelStateDictionary ModelState
{
get
{
return ControllerContext.ModelState;
}
}
/// <summary>
/// Gets or sets the HTTP request message.
/// </summary>
/// <remarks>The setter is intended for unit testing purposes only.</remarks>
public HttpRequestMessage Request
{
get
{
if (_request == null && ActionContext != null)
{
_request = ControllerContext.HttpContext.GetHttpRequestMessage();
}
return _request;
}
set
{
_request = value;
}
}
/// <summary>
/// Gets a factory used to generate URLs to other APIs.
/// </summary>
/// <remarks>The setter is intended for unit testing purposes only.</remarks>
public IUrlHelper Url
{
get
{
if (_urlHelper == null)
{
var factory = Context?.RequestServices.GetRequiredService<IUrlHelperFactory>();
_urlHelper = factory?.GetUrlHelper(ActionContext);
}
return _urlHelper;
}
set
{
_urlHelper = value;
}
}
/// <summary>
/// Gets or sets the current principal associated with this request.
/// </summary>
public IPrincipal User
{
get
{
return Context?.User;
}
}
/// <summary>
/// Creates a <see cref="BadRequestResult"/> (400 Bad Request).
/// </summary>
/// <returns>A <see cref="BadRequestResult"/>.</returns>
[NonAction]
public virtual BadRequestResult BadRequest()
{
return new BadRequestResult();
}
/// <summary>
/// Creates a <see cref="BadRequestErrorMessageResult"/> (400 Bad Request) with the specified error message.
/// </summary>
/// <param name="message">The user-visible error message.</param>
/// <returns>A <see cref="BadRequestErrorMessageResult"/> with the specified error message.</returns>
[NonAction]
public virtual BadRequestErrorMessageResult BadRequest(string message)
{
if (message == null)
{
throw new ArgumentNullException(nameof(message));
}
return new BadRequestErrorMessageResult(message);
}
/// <summary>
/// Creates an <see cref="InvalidModelStateResult"/> (400 Bad Request) with the specified model state.
/// </summary>
/// <param name="modelState">The model state to include in the error.</param>
/// <returns>An <see cref="InvalidModelStateResult"/> with the specified model state.</returns>
[NonAction]
public virtual InvalidModelStateResult BadRequest(ModelStateDictionary modelState)
{
if (modelState == null)
{
throw new ArgumentNullException(nameof(modelState));
}
return new InvalidModelStateResult(modelState, includeErrorDetail: false);
}
/// <summary>Creates a <see cref="ConflictResult"/> (409 Conflict).</summary>
/// <returns>A <see cref="ConflictResult"/>.</returns>
[NonAction]
public virtual ConflictResult Conflict()
{
return new ConflictResult();
}
/// <summary>
/// Creates a <see cref="NegotiatedContentResult{T}"/> with the specified values.
/// </summary>
/// <typeparam name="T">The type of content in the entity body.</typeparam>
/// <param name="statusCode">The HTTP status code for the response message.</param>
/// <param name="value">The content value to negotiate and format in the entity body.</param>
/// <returns>A <see cref="NegotiatedContentResult{T}"/> with the specified values.</returns>
[NonAction]
public virtual NegotiatedContentResult<T> Content<T>(HttpStatusCode statusCode, T value)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
return new NegotiatedContentResult<T>(statusCode, value);
}
/// <summary>
/// Creates a <see cref="CreatedResult"/> (201 Created) with the specified values.
/// </summary>
/// <param name="location">
/// The location at which the content has been created. Must be a relative or absolute URL.
/// </param>
/// <param name="content">The content value to format in the entity body.</param>
/// <returns>A <see cref="CreatedResult"/> with the specified values.</returns>
[NonAction]
public virtual CreatedResult Created(string location, object content)
{
if (location == null)
{
throw new ArgumentNullException(nameof(location));
}
return new CreatedResult(location, content);
}
/// <summary>
/// Creates a <see cref="CreatedResult"/> (201 Created) with the specified values.
/// </summary>
/// <param name="uri">The location at which the content has been created.</param>
/// <param name="content">The content value to format in the entity body.</param>
/// <returns>A <see cref="CreatedResult"/> with the specified values.</returns>
[NonAction]
public virtual CreatedResult Created(Uri uri, object content)
{
if (uri == null)
{
throw new ArgumentNullException(nameof(uri));
}
string location;
if (uri.IsAbsoluteUri)
{
location = uri.AbsoluteUri;
}
else
{
location = uri.GetComponents(UriComponents.SerializationInfoString, UriFormat.UriEscaped);
}
return Created(location, content);
}
/// <summary>
/// Creates a <see cref="CreatedAtRouteResult"/> (201 Created) with the specified values.
/// </summary>
/// <param name="routeName">The name of the route to use for generating the URL.</param>
/// <param name="routeValues">The route data to use for generating the URL.</param>
/// <param name="content">The content value to format in the entity body.</param>
/// <returns>A <see cref="CreatedAtRouteResult"/> with the specified values.</returns>
[NonAction]
public virtual CreatedAtRouteResult CreatedAtRoute(
string routeName,
object routeValues,
object content)
{
if (routeName == null)
{
throw new ArgumentNullException(nameof(routeName));
}
return new CreatedAtRouteResult(routeName, routeValues, content);
}
/// <summary
/// >Creates an <see cref="InternalServerErrorResult"/> (500 Internal Server Error).
/// </summary>
/// <returns>A <see cref="InternalServerErrorResult"/>.</returns>
[NonAction]
public virtual InternalServerErrorResult InternalServerError()
{
return new InternalServerErrorResult();
}
/// <summary>
/// Creates an <see cref="ExceptionResult"/> (500 Internal Server Error) with the specified exception.
/// </summary>
/// <param name="exception">The exception to include in the error.</param>
/// <returns>An <see cref="ExceptionResult"/> with the specified exception.</returns>
[NonAction]
public virtual ExceptionResult InternalServerError(Exception exception)
{
if (exception == null)
{
throw new ArgumentNullException(nameof(exception));
}
return new ExceptionResult(exception, includeErrorDetail: false);
}
/// <summary>
/// Creates an <see cref="JsonResult"/> (200 OK) with the specified value.
/// </summary>
/// <typeparam name="T">The type of content in the entity body.</typeparam>
/// <param name="content">The content value to serialize in the entity body.</param>
/// <returns>A <see cref="JsonResult"/> with the specified value.</returns>
[NonAction]
public virtual JsonResult Json<T>(T content)
{
if (content == null)
{
throw new ArgumentNullException(nameof(content));
}
return new JsonResult(content);
}
/// <summary>
/// Creates an <see cref="JsonResult"/> (200 OK) with the specified values.
/// </summary>
/// <typeparam name="T">The type of content in the entity body.</typeparam>
/// <param name="content">The content value to serialize in the entity body.</param>
/// <param name="serializerSettings">The serializer settings.</param>
/// <returns>A <see cref="JsonResult"/> with the specified values.</returns>
[NonAction]
public virtual JsonResult Json<T>(T content, JsonSerializerSettings serializerSettings)
{
if (content == null)
{
throw new ArgumentNullException(nameof(content));
}
if (serializerSettings == null)
{
throw new ArgumentNullException(nameof(serializerSettings));
}
return new JsonResult(content, serializerSettings);
}
/// <summary>
/// Creates an <see cref="JsonResult"/> (200 OK) with the specified values.
/// </summary>
/// <typeparam name="T">The type of content in the entity body.</typeparam>
/// <param name="content">The content value to serialize in the entity body.</param>
/// <param name="serializerSettings">The serializer settings.</param>
/// <param name="encoding">The content encoding.</param>
/// <returns>A <see cref="JsonResult"/> with the specified values.</returns>
[NonAction]
public virtual JsonResult Json<T>(
T content,
JsonSerializerSettings serializerSettings,
Encoding encoding)
{
if (content == null)
{
throw new ArgumentNullException(nameof(content));
}
if (serializerSettings == null)
{
throw new ArgumentNullException(nameof(serializerSettings));
}
if (encoding == null)
{
throw new ArgumentNullException(nameof(encoding));
}
var result = new JsonResult(content, serializerSettings);
result.ContentType = $"application/json; charset={encoding.WebName}";
return result;
}
/// <summary>
/// Creates an <see cref="NotFoundResult"/> (404 Not Found).
/// </summary>
/// <returns>A <see cref="NotFoundResult"/>.</returns>
[NonAction]
public virtual NotFoundResult NotFound()
{
return new NotFoundResult();
}
/// <summary>
/// Creates an <see cref="OkResult"/> (200 OK).
/// </summary>
/// <returns>An <see cref="OkResult"/>.</returns>
[NonAction]
public virtual OkResult Ok()
{
return new OkResult();
}
/// <summary>
/// Creates an <see cref="OkObjectResult"/> (200 OK) with the specified values.
/// </summary>
/// <typeparam name="T">The type of content in the entity body.</typeparam>
/// <param name="content">The content value to negotiate and format in the entity body.</param>
/// <returns>An <see cref="OkObjectResult"/> with the specified values.</returns>
[NonAction]
public virtual OkObjectResult Ok<T>(T content)
{
return new OkObjectResult(content);
}
/// <summary>
/// Creates a <see cref="RedirectResult"/> (302 Found) with the specified value.
/// </summary>
/// <param name="location">The location to which to redirect.</param>
/// <returns>A <see cref="RedirectResult"/> with the specified value.</returns>
[NonAction]
public virtual RedirectResult Redirect(string location)
{
if (location == null)
{
throw new ArgumentNullException(nameof(location));
}
// This is how redirect was implemented in legacy webapi - string URIs are assumed to be absolute.
return Redirect(new Uri(location));
}
/// <summary>
/// Creates a <see cref="RedirectResult"/> (302 Found) with the specified value.
/// </summary>
/// <param name="location">The location to which to redirect.</param>
/// <returns>A <see cref="RedirectResult"/> with the specified value.</returns>
[NonAction]
public virtual RedirectResult Redirect(Uri location)
{
if (location == null)
{
throw new ArgumentNullException(nameof(location));
}
string uri;
if (location.IsAbsoluteUri)
{
uri = location.AbsoluteUri;
}
else
{
uri = location.GetComponents(UriComponents.SerializationInfoString, UriFormat.UriEscaped);
}
return new RedirectResult(uri);
}
/// <summary>
/// Creates a <see cref="RedirectToRouteResult"/> (302 Found) with the specified values.
/// </summary>
/// <param name="routeName">The name of the route to use for generating the URL.</param>
/// <param name="routeValues">The route data to use for generating the URL.</param>
/// <returns>A <see cref="RedirectToRouteResult"/> with the specified values.</returns>
[NonAction]
public virtual RedirectToRouteResult RedirectToRoute(string routeName, object routeValues)
{
if (routeName == null)
{
throw new ArgumentNullException(nameof(routeName));
}
if (routeValues == null)
{
throw new ArgumentNullException(nameof(routeValues));
}
return new RedirectToRouteResult(routeName, routeValues)
{
UrlHelper = Url,
};
}
/// <summary>
/// Creates a <see cref="ResponseMessageResult"/> with the specified response.
/// </summary>
/// <param name="response">The HTTP response message.</param>
/// <returns>A <see cref="ResponseMessageResult"/> for the specified response.</returns>
[NonAction]
public virtual ResponseMessageResult ResponseMessage(HttpResponseMessage response)
{
if (response == null)
{
throw new ArgumentNullException(nameof(response));
}
return new ResponseMessageResult(response);
}
/// <summary>
/// Creates a <see cref="StatusCodeResult"/> with the specified status code.
/// </summary>
/// <param name="status">The HTTP status code for the response message</param>
/// <returns>A <see cref="StatusCodeResult"/> with the specified status code.</returns>
[NonAction]
public virtual StatusCodeResult StatusCode(HttpStatusCode status)
{
return new StatusCodeResult((int)status);
}
/// <inheritdoc />
public void Dispose() => Dispose(disposing: true);
/// <summary>
/// Validates the given entity and adds the validation errors to the <see cref="ApiController.ModelState"/>
/// under an empty prefix.
/// </summary>
/// <typeparam name="TEntity">The type of the entity to be validated.</typeparam>
/// <param name="entity">The entity being validated.</param>
public void Validate<TEntity>(TEntity entity)
{
Validate(entity, keyPrefix: string.Empty);
}
/// <summary>
/// Validates the given entity and adds the validation errors to the <see cref="ApiController.ModelState"/>.
/// </summary>
/// <typeparam name="TEntity">The type of the entity to be validated.</typeparam>
/// <param name="entity">The entity being validated.</param>
/// <param name="keyPrefix">
/// The key prefix under which the model state errors would be added in the
/// <see cref="ApiController.ModelState"/>.
/// </param>
public void Validate<TEntity>(TEntity entity, string keyPrefix)
{
ObjectValidator.Validate(
ControllerContext,
validationState: null,
prefix: keyPrefix,
model: entity);
}
protected virtual void Dispose(bool disposing)
{
}
}
}

View File

@ -1,41 +0,0 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace System.Web.Http
{
/// <summary>
/// An action result that returns a <see cref="StatusCodes.Status400BadRequest"/> response and performs
/// content negotiation on an <see cref="HttpError"/> with a <see cref="HttpError.Message"/>.
/// </summary>
public class BadRequestErrorMessageResult : ObjectResult
{
/// <summary>Initializes a new instance of the <see cref="BadRequestErrorMessageResult"/> class.</summary>
/// <param name="message">The user-visible error message.</param>
public BadRequestErrorMessageResult(string message)
: base(new HttpError(message))
{
if (message == null)
{
throw new ArgumentNullException(nameof(message));
}
Message = message;
}
/// <summary>
/// Gets the error message.
/// </summary>
public string Message { get; private set; }
/// <inheritdoc />
public override Task ExecuteResultAsync(ActionContext context)
{
context.HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
return base.ExecuteResultAsync(context);
}
}
}

View File

@ -1,22 +0,0 @@
// 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 Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace System.Web.Http
{
/// <summary>
/// An action result that returns an empty <see cref="StatusCodes.Status409Conflict"/> response.
/// </summary>
public class ConflictResult : StatusCodeResult
{
/// <summary>
/// Initializes a new instance of the <see cref="ConflictResult"/> class.
/// </summary>
public ConflictResult()
: base(StatusCodes.Status409Conflict)
{
}
}
}

View File

@ -1,12 +0,0 @@
// 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.
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
/// <summary>
/// Indicates actions without attribute routes in a controller use ASP.NET Web API routing conventions.
/// </summary>
public interface IUseWebApiActionConventions
{
}
}

View File

@ -1,13 +0,0 @@
// 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.
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
/// <summary>
/// Indicates actions in a controller should be selected only if all non-optional parameters are satisfied. Applies
/// the <see cref="OverloadActionConstraint"/> to all actions in the controller.
/// </summary>
public interface IUseWebApiOverloading
{
}
}

View File

@ -1,13 +0,0 @@
// 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.
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
/// <summary>
/// Indicates the model binding system should use ASP.NET Web API conventions for parameters of a controller's
/// actions. For example, bind simple types from the URI.
/// </summary>
public interface IUseWebApiParameterConventions
{
}
}

View File

@ -1,12 +0,0 @@
// 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.
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
/// <summary>
/// Indicates the controller is in the "api" area.
/// </summary>
public interface IUseWebApiRoutes
{
}
}

View File

@ -1,15 +0,0 @@
// 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;
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
/// <summary>
/// Indicates actions without attribute routes in a controller use ASP.NET Web API routing conventions.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class UseWebApiActionConventionsAttribute : Attribute, IUseWebApiActionConventions
{
}
}

View File

@ -1,16 +0,0 @@
// 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;
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
/// <summary>
/// Indicates actions in a controller should be selected only if all non-optional parameters are satisfied. Applies
/// the <see cref="OverloadActionConstraint"/> to all actions in the controller.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class UseWebApiOverloadingAttribute : Attribute, IUseWebApiOverloading
{
}
}

View File

@ -1,16 +0,0 @@
// 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;
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
/// <summary>
/// Indicates the model binding system should use ASP.NET Web API conventions for parameters of a controller's
/// actions. For example, bind simple types from the URI.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class UseWebApiParameterConventionsAttribute : Attribute, IUseWebApiParameterConventions
{
}
}

View File

@ -1,15 +0,0 @@
// 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;
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
/// <summary>
/// Indicates the controller is in the "api" area.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class UseWebApiRoutesAttribute : Attribute, IUseWebApiRoutes
{
}
}

View File

@ -1,117 +0,0 @@
// 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.Linq;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
public class WebApiActionConventionsApplicationModelConvention : IControllerModelConvention
{
private static readonly string[] SupportedHttpMethodConventions = new string[]
{
"GET",
"PUT",
"POST",
"DELETE",
"PATCH",
"HEAD",
"OPTIONS",
};
public void Apply(ControllerModel controller)
{
if (controller == null)
{
throw new ArgumentNullException(nameof(controller));
}
if (IsConventionApplicable(controller))
{
var newActions = new List<ActionModel>();
foreach (var action in controller.Actions)
{
SetHttpMethodFromConvention(action);
// Action Name doesn't really come into play with attribute routed actions. However for a
// non-attribute-routed action we need to create a 'named' version and an 'unnamed' version.
if (!IsActionAttributeRouted(action))
{
var namedAction = action;
var unnamedAction = new ActionModel(namedAction);
unnamedAction.RouteValues.Add("action", null);
newActions.Add(unnamedAction);
}
}
foreach (var action in newActions)
{
controller.Actions.Add(action);
}
}
}
private bool IsConventionApplicable(ControllerModel controller)
{
return controller.Attributes.OfType<IUseWebApiActionConventions>().Any();
}
private bool IsActionAttributeRouted(ActionModel action)
{
foreach (var controllerSelectorModel in action.Controller.Selectors)
{
if (controllerSelectorModel.AttributeRouteModel?.Template != null)
{
return true;
}
}
foreach (var actionSelectorModel in action.Selectors)
{
if (actionSelectorModel.AttributeRouteModel?.Template != null)
{
return true;
}
}
return false;
}
private void SetHttpMethodFromConvention(ActionModel action)
{
foreach (var selector in action.Selectors)
{
if (selector.ActionConstraints.OfType<HttpMethodActionConstraint>().Count() > 0)
{
// If the HttpMethods are set from attributes, don't override it with the convention
return;
}
}
// The Method name is used to infer verb constraints. Changing the action name has no impact.
foreach (var verb in SupportedHttpMethodConventions)
{
if (action.ActionMethod.Name.StartsWith(verb, StringComparison.OrdinalIgnoreCase))
{
foreach (var selector in action.Selectors)
{
selector.ActionConstraints.Add(new HttpMethodActionConstraint(new[] { verb }));
}
return;
}
}
// If no convention matches, then assume POST
foreach (var actionSelectorModel in action.Selectors)
{
actionSelectorModel.ActionConstraints.Add(new HttpMethodActionConstraint(new[] { "POST" }));
}
}
}
}

View File

@ -1,33 +0,0 @@
// 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.Linq;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
public class WebApiOverloadingApplicationModelConvention : IActionModelConvention
{
public void Apply(ActionModel action)
{
if (action == null)
{
throw new ArgumentNullException(nameof(action));
}
if (IsConventionApplicable(action.Controller))
{
foreach (var actionSelectorModel in action.Selectors)
{
actionSelectorModel.ActionConstraints.Add(new OverloadActionConstraint());
}
}
}
private bool IsConventionApplicable(ControllerModel controller)
{
return controller.Attributes.OfType<IUseWebApiOverloading>().Any();
}
}
}

View File

@ -1,90 +0,0 @@
// 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.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Web.Http;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
public class WebApiParameterConventionsApplicationModelConvention : IActionModelConvention
{
public void Apply(ActionModel action)
{
if (action == null)
{
throw new ArgumentNullException(nameof(action));
}
if (IsConventionApplicable(action.Controller))
{
var optionalParameters = new HashSet<string>();
var uriBindingSource = (new FromUriAttribute()).BindingSource;
foreach (var parameter in action.Parameters)
{
// Some IBindingSourceMetadata attributes like ModelBinder attribute return null
// as their binding source. Special case to ensure we do not ignore them.
if (parameter.BindingInfo?.BindingSource != null ||
parameter.Attributes.OfType<IBindingSourceMetadata>().Any())
{
// This has a binding behavior configured, just leave it alone.
}
else if (CanConvertFromString(parameter.ParameterInfo.ParameterType))
{
// Simple types are by-default from the URI.
parameter.BindingInfo = parameter.BindingInfo ?? new BindingInfo();
parameter.BindingInfo.BindingSource = uriBindingSource;
}
else
{
// Complex types are by-default from the body.
parameter.BindingInfo = parameter.BindingInfo ?? new BindingInfo();
parameter.BindingInfo.BindingSource = BindingSource.Body;
}
// For all non IOptionalBinderMetadata, which are not URL source (like FromQuery etc.) do not
// participate in overload selection and hence are added to the hashset so that they can be
// ignored in OverloadActionConstraint.
var optionalMetadata = parameter.Attributes.OfType<IOptionalBinderMetadata>().SingleOrDefault();
if (parameter.ParameterInfo.HasDefaultValue && parameter.BindingInfo.BindingSource == uriBindingSource ||
optionalMetadata != null && optionalMetadata.IsOptional ||
optionalMetadata == null && parameter.BindingInfo.BindingSource != uriBindingSource)
{
optionalParameters.Add(parameter.ParameterName);
}
}
action.Properties.Add("OptionalParameters", optionalParameters);
}
}
private bool IsConventionApplicable(ControllerModel controller)
{
return controller.Attributes.OfType<IUseWebApiParameterConventions>().Any();
}
private static bool CanConvertFromString(Type destinationType)
{
destinationType = Nullable.GetUnderlyingType(destinationType) ?? destinationType;
return IsSimpleType(destinationType) ||
TypeDescriptor.GetConverter(destinationType).CanConvertFrom(typeof(string));
}
private static bool IsSimpleType(Type type)
{
return type.GetTypeInfo().IsPrimitive ||
type.Equals(typeof(decimal)) ||
type.Equals(typeof(string)) ||
type.Equals(typeof(DateTime)) ||
type.Equals(typeof(Guid)) ||
type.Equals(typeof(DateTimeOffset)) ||
type.Equals(typeof(TimeSpan)) ||
type.Equals(typeof(Uri));
}
}
}

View File

@ -1,37 +0,0 @@
// 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.Linq;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
public class WebApiRoutesApplicationModelConvention : IControllerModelConvention
{
private readonly string _area;
public WebApiRoutesApplicationModelConvention(string area)
{
_area = area;
}
public void Apply(ControllerModel controller)
{
if (controller == null)
{
throw new ArgumentNullException(nameof(controller));
}
if (IsConventionApplicable(controller))
{
controller.RouteValues.Add("area", _area);
}
}
private bool IsConventionApplicable(ControllerModel controller)
{
return controller.Attributes.OfType<IUseWebApiRoutes>().Any();
}
}
}

View File

@ -1,45 +0,0 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace System.Web.Http
{
/// <summary>
/// An action result that returns a <see cref="StatusCodes.Status500InternalServerError"/> response and
/// performs content negotiation on an <see cref="HttpError"/> based on an <see cref="Exception"/>.
/// </summary>
public class ExceptionResult : ObjectResult
{
/// <summary>Initializes a new instance of the <see cref="ExceptionResult"/> class.</summary>
/// <param name="exception">The exception to include in the error.</param>
/// <param name="includeErrorDetail">
/// <see langword="true"/> if the error should include exception messages; otherwise, <see langword="false"/>.
/// </param>
public ExceptionResult(Exception exception, bool includeErrorDetail)
: base(new HttpError(exception, includeErrorDetail))
{
Exception = exception;
IncludeErrorDetail = includeErrorDetail;
}
/// <summary>
/// Gets the exception to include in the error.
/// </summary>
public Exception Exception { get; private set; }
/// <summary>
/// Gets a value indicating whether the error should include exception messages.
/// </summary>
public bool IncludeErrorDetail { get; private set; }
/// <inheritdoc />
public override Task ExecuteResultAsync(ActionContext context)
{
context.HttpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
return base.ExecuteResultAsync(context);
}
}
}

View File

@ -1,114 +0,0 @@
// 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.Net.Http.Formatting;
using System.Text;
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
public static class FormDataCollectionExtensions
{
// This is a helper method to use Model Binding over a JQuery syntax.
// Normalize from JQuery to MVC keys. The model binding infrastructure uses MVC keys
// x[] --> x
// [] --> ""
// x[field] --> x.field, where field is not a number
public static string NormalizeJQueryToMvc(string key)
{
if (key == null)
{
return string.Empty;
}
StringBuilder sb = null;
var i = 0;
while (true)
{
var indexOpen = key.IndexOf('[', i);
if (indexOpen < 0)
{
// Fast path, no normalization needed.
// This skips the string conversion and allocating the string builder.
if (i == 0)
{
return key;
}
sb = sb ?? new StringBuilder();
sb.Append(key, i, key.Length - i);
break; // no more brackets
}
sb = sb ?? new StringBuilder();
sb.Append(key, i, indexOpen - i); // everything up to "["
// Find closing bracket.
var indexClose = key.IndexOf(']', indexOpen);
if (indexClose == -1)
{
throw new ArgumentException(Resources.JQuerySyntaxMissingClosingBracket, nameof(key));
}
if (indexClose == indexOpen + 1)
{
// Empty bracket. Signifies array. Just remove.
}
else
{
if (char.IsDigit(key[indexOpen + 1]))
{
// array index. Leave unchanged.
sb.Append(key, indexOpen, indexClose - indexOpen + 1);
}
else
{
// Field name. Convert to dot notation.
sb.Append('.');
sb.Append(key, indexOpen + 1, indexClose - indexOpen - 1);
}
}
i = indexClose + 1;
if (i >= key.Length)
{
break; // end of string
}
}
return sb.ToString();
}
public static IEnumerable<KeyValuePair<string, string>> GetJQueryNameValuePairs(
this FormDataCollection formData)
{
if (formData == null)
{
throw new ArgumentNullException(nameof(formData));
}
var count = 0;
foreach (var kv in formData)
{
ThrowIfMaxHttpCollectionKeysExceeded(count);
var key = NormalizeJQueryToMvc(kv.Key);
var value = kv.Value ?? string.Empty;
yield return new KeyValuePair<string, string>(key, value);
count++;
}
}
private static void ThrowIfMaxHttpCollectionKeysExceeded(int count)
{
if (count >= MediaTypeFormatter.MaxHttpCollectionKeys)
{
var message = Resources.FormatMaxHttpCollectionKeyLimitReached(
MediaTypeFormatter.MaxHttpCollectionKeys,
typeof(MediaTypeFormatter));
throw new InvalidOperationException(message);
}
}
}
}

View File

@ -1,79 +0,0 @@
// 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.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc.Formatters;
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
public class HttpResponseMessageOutputFormatter : IOutputFormatter
{
public bool CanWriteResult(OutputFormatterCanWriteContext context)
{
return context.Object is HttpResponseMessage;
}
public async Task WriteAsync(OutputFormatterWriteContext context)
{
var response = context.HttpContext.Response;
var responseMessage = context.Object as HttpResponseMessage;
if (responseMessage == null)
{
var message = Resources.FormatHttpResponseMessageFormatter_UnsupportedType(
nameof(HttpResponseMessageOutputFormatter),
nameof(HttpResponseMessage));
throw new InvalidOperationException(message);
}
using (responseMessage)
{
response.StatusCode = (int)responseMessage.StatusCode;
var responseFeature = context.HttpContext.Features.Get<IHttpResponseFeature>();
if (responseFeature != null)
{
responseFeature.ReasonPhrase = responseMessage.ReasonPhrase;
}
var responseHeaders = responseMessage.Headers;
// Ignore the Transfer-Encoding header if it is just "chunked".
// We let the host decide about whether the response should be chunked or not.
if (responseHeaders.TransferEncodingChunked == true &&
responseHeaders.TransferEncoding.Count == 1)
{
responseHeaders.TransferEncoding.Clear();
}
foreach (var header in responseHeaders)
{
response.Headers.Append(header.Key, header.Value.ToArray());
}
if (responseMessage.Content != null)
{
var contentHeaders = responseMessage.Content.Headers;
// Copy the response content headers only after ensuring they are complete.
// We ask for Content-Length first because HttpContent lazily computes this
// and only afterwards writes the value into the content headers.
var unused = contentHeaders.ContentLength;
foreach (var header in contentHeaders)
{
response.Headers.Append(header.Key, header.Value.ToArray());
}
await responseMessage.Content.CopyToAsync(response.Body);
}
}
}
}
}

View File

@ -1,285 +0,0 @@
// 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.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using ShimResources = Microsoft.AspNetCore.Mvc.WebApiCompatShim.Resources;
namespace System.Web.Http
{
/// <summary>
/// Defines a serializable container for storing error information. This information is stored
/// as key/value pairs. The dictionary keys to look up standard error information are available
/// on the <see cref="HttpErrorKeys"/> type.
/// </summary>
[XmlRoot("Error")]
public sealed class HttpError : Dictionary<string, object>, IXmlSerializable
{
/// <summary>
/// Initializes a new instance of the <see cref="HttpError"/> class.
/// </summary>
public HttpError()
: base(StringComparer.OrdinalIgnoreCase)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="HttpError"/> class containing error message
/// <paramref name="message"/>.
/// </summary>
/// <param name="message">The error message to associate with this instance.</param>
public HttpError(string message)
: this()
{
if (message == null)
{
throw new ArgumentNullException(nameof(message));
}
Message = message;
}
/// <summary>
/// Initializes a new instance of the <see cref="HttpError"/> class for <paramref name="exception"/>.
/// </summary>
/// <param name="exception">The exception to use for error information.</param>
/// <param name="includeErrorDetail">
/// <c>true</c> to include the exception information in the error;<c>false</c> otherwise.
/// </param>
public HttpError(Exception exception, bool includeErrorDetail)
: this()
{
if (exception == null)
{
throw new ArgumentNullException(nameof(exception));
}
Message = ShimResources.HttpError_GenericError;
if (includeErrorDetail)
{
Add(HttpErrorKeys.ExceptionMessageKey, exception.Message);
Add(HttpErrorKeys.ExceptionTypeKey, exception.GetType().FullName);
Add(HttpErrorKeys.StackTraceKey, exception.StackTrace);
if (exception.InnerException != null)
{
Add(HttpErrorKeys.InnerExceptionKey, new HttpError(exception.InnerException, includeErrorDetail));
}
}
}
/// <summary>
/// Initializes a new instance of the <see cref="HttpError"/> class for <paramref name="modelState"/>.
/// </summary>
/// <param name="modelState">The invalid model state to use for error information.</param>
/// <param name="includeErrorDetail">
/// <c>true</c> to include exception messages in the error; <c>false</c> otherwise.
/// </param>
public HttpError(ModelStateDictionary modelState, bool includeErrorDetail)
: this()
{
if (modelState == null)
{
throw new ArgumentNullException(nameof(modelState));
}
if (modelState.IsValid)
{
throw new ArgumentException(ShimResources.HttpError_ValidModelState, nameof(modelState));
}
Message = ShimResources.HttpError_BadRequest;
var modelStateError = new HttpError();
foreach (KeyValuePair<string, ModelStateEntry> keyModelStatePair in modelState)
{
var key = keyModelStatePair.Key;
var errors = keyModelStatePair.Value.Errors;
if (errors != null && errors.Count > 0)
{
var errorMessages = errors.Select(error =>
{
if (includeErrorDetail && error.Exception != null)
{
return error.Exception.Message;
}
else
{
return
string.IsNullOrEmpty(error.ErrorMessage) ?
ShimResources.HttpError_GenericError :
error.ErrorMessage;
}
}).ToArray();
modelStateError.Add(key, errorMessages);
}
}
Add(HttpErrorKeys.ModelStateKey, modelStateError);
}
/// <summary>
/// The high-level, user-visible message explaining the cause of the error. Information carried in this field
/// should be considered public in that it will go over the wire regardless of the value of error detail
/// policy. As a result care should be taken not to disclose sensitive information about the server or the
/// application.
/// </summary>
public string Message
{
get { return GetPropertyValue<string>(HttpErrorKeys.MessageKey); }
set { this[HttpErrorKeys.MessageKey] = value; }
}
/// <summary>
/// The <see cref="ModelState"/> containing information about the errors that occurred during model binding.
/// </summary>
/// <remarks>
/// The inclusion of <see cref="System.Exception"/> information carried in the <see cref="ModelState"/> is
/// controlled by the error detail policy. All other information in the <see cref="ModelState"/>
/// should be considered public in that it will go over the wire. As a result care should be taken not to
/// disclose sensitive information about the server or the application.
/// </remarks>
public HttpError ModelState
{
get { return GetPropertyValue<HttpError>(HttpErrorKeys.ModelStateKey); }
}
/// <summary>
/// A detailed description of the error intended for the developer to understand exactly what failed.
/// </summary>
/// <remarks>
/// The inclusion of this field is controlled by the error detail policy. The
/// field is expected to contain information about the server or the application that should not
/// be disclosed broadly.
/// </remarks>
public string MessageDetail
{
get { return GetPropertyValue<string>(HttpErrorKeys.MessageDetailKey); }
set { this[HttpErrorKeys.MessageDetailKey] = value; }
}
/// <summary>
/// The message of the <see cref="System.Exception"/> if available.
/// </summary>
/// <remarks>
/// The inclusion of this field is controlled by the error detail policy. The
/// field is expected to contain information about the server or the application that should not
/// be disclosed broadly.
/// </remarks>
public string ExceptionMessage
{
get { return GetPropertyValue<string>(HttpErrorKeys.ExceptionMessageKey); }
set { this[HttpErrorKeys.ExceptionMessageKey] = value; }
}
/// <summary>
/// The type of the <see cref="System.Exception"/> if available.
/// </summary>
/// <remarks>
/// The inclusion of this field is controlled by the error detail policy. The
/// field is expected to contain information about the server or the application that should not
/// be disclosed broadly.
/// </remarks>
public string ExceptionType
{
get { return GetPropertyValue<string>(HttpErrorKeys.ExceptionTypeKey); }
set { this[HttpErrorKeys.ExceptionTypeKey] = value; }
}
/// <summary>
/// The stack trace information associated with this instance if available.
/// </summary>
/// <remarks>
/// The inclusion of this field is controlled by the error detail policy. The
/// field is expected to contain information about the server or the application that should not
/// be disclosed broadly.
/// </remarks>
public string StackTrace
{
get { return GetPropertyValue<string>(HttpErrorKeys.StackTraceKey); }
set { this[HttpErrorKeys.StackTraceKey] = value; }
}
/// <summary>
/// The inner <see cref="System.Exception"/> associated with this instance if available.
/// </summary>
/// <remarks>
/// The inclusion of this field is controlled by the error detail policy. The
/// field is expected to contain information about the server or the application that should not
/// be disclosed broadly.
/// </remarks>
public HttpError InnerException
{
get { return GetPropertyValue<HttpError>(HttpErrorKeys.InnerExceptionKey); }
}
/// <summary>
/// Gets a particular property value from this error instance.
/// </summary>
/// <typeparam name="TValue">The type of the property.</typeparam>
/// <param name="key">The name of the error property.</param>
/// <returns>The value of the error property.</returns>
public TValue GetPropertyValue<TValue>(string key)
{
object value;
if (TryGetValue(key, out value) && value is TValue)
{
return (TValue)value;
}
return default(TValue);
}
XmlSchema IXmlSerializable.GetSchema()
{
return null;
}
void IXmlSerializable.ReadXml(XmlReader reader)
{
if (reader.IsEmptyElement)
{
reader.Read();
return;
}
reader.ReadStartElement();
while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
{
var key = XmlConvert.DecodeName(reader.LocalName);
var value = reader.ReadInnerXml();
Add(key, value);
reader.MoveToContent();
}
reader.ReadEndElement();
}
void IXmlSerializable.WriteXml(XmlWriter writer)
{
foreach (var keyValuePair in this)
{
var key = keyValuePair.Key;
var value = keyValuePair.Value;
writer.WriteStartElement(XmlConvert.EncodeLocalName(key));
if (value != null)
{
var innerError = value as HttpError;
if (innerError == null)
{
writer.WriteValue(value);
}
else
{
((IXmlSerializable)innerError).WriteXml(writer);
}
}
writer.WriteEndElement();
}
}
}
}

View File

@ -1,56 +0,0 @@
// 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.
namespace System.Web.Http
{
/// <summary>
/// Provides keys to look up error information stored in the <see cref="HttpError"/> dictionary.
/// </summary>
public static class HttpErrorKeys
{
/// <summary>
/// Provides a key for the Message.
/// </summary>
public static readonly string MessageKey = "Message";
/// <summary>
/// Provides a key for the MessageDetail.
/// </summary>
public static readonly string MessageDetailKey = "MessageDetail";
/// <summary>
/// Provides a key for the ModelState.
/// </summary>
public static readonly string ModelStateKey = "ModelState";
/// <summary>
/// Provides a key for the ExceptionMessage.
/// </summary>
public static readonly string ExceptionMessageKey = "ExceptionMessage";
/// <summary>
/// Provides a key for the ExceptionType.
/// </summary>
public static readonly string ExceptionTypeKey = "ExceptionType";
/// <summary>
/// Provides a key for the StackTrace.
/// </summary>
public static readonly string StackTraceKey = "StackTrace";
/// <summary>
/// Provides a key for the InnerException.
/// </summary>
public static readonly string InnerExceptionKey = "InnerException";
/// <summary>
/// Provides a key for the MessageLanguage.
/// </summary>
public static readonly string MessageLanguageKey = "MessageLanguage";
/// <summary>
/// Provides a key for the ErrorCode.
/// </summary>
public static readonly string ErrorCodeKey = "ErrorCode";
}
}

View File

@ -1,521 +0,0 @@
// 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.Collections.Generic;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Web.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.WebApiCompatShim;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using ShimResources = Microsoft.AspNetCore.Mvc.WebApiCompatShim.Resources;
namespace System.Net.Http
{
/// <summary>
/// Provides extension methods for the <see cref="HttpRequestMessage"/> class.
/// </summary>
public static class HttpRequestMessageExtensions
{
/// <summary>
/// Helper method that performs content negotiation and creates a <see cref="HttpResponseMessage"/>
/// representing an error with an instance of <see cref="ObjectContent{T}"/> wrapping an
/// <see cref="HttpError"/> with message <paramref name="message"/>. If no formatter is found, this method
/// returns a response with status 406 NotAcceptable.
/// </summary>
/// <remarks>
/// This method requires that <paramref name="request"/> has been associated with an instance of
/// <see cref="HttpContext"/>.
/// </remarks>
/// <param name="request">The request.</param>
/// <param name="statusCode">The status code of the created response.</param>
/// <param name="message">The error message.</param>
/// <returns>
/// An error response with error message <paramref name="message"/> and status code
/// <paramref name="statusCode"/>.
/// </returns>
public static HttpResponseMessage CreateErrorResponse(
this HttpRequestMessage request,
HttpStatusCode statusCode,
string message)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
if (message == null)
{
throw new ArgumentNullException(nameof(message));
}
return request.CreateErrorResponse(statusCode, new HttpError(message));
}
/// <summary>
/// Helper method that performs content negotiation and creates a <see cref="HttpResponseMessage"/>
/// representing an error with an instance of <see cref="ObjectContent{T}"/> wrapping an
/// <see cref="HttpError"/> with error message <paramref name="message"/> for exception
/// <paramref name="exception"/>. If no formatter is found, this method returns a response with status 406
/// NotAcceptable.
/// </summary>
/// <remarks>
/// This method requires that <paramref name="request"/> has been associated with an instance of
/// <see cref="HttpContext"/>.
/// </remarks>
/// <param name="request">The request.</param>
/// <param name="statusCode">The status code of the created response.</param>
/// <param name="message">The error message.</param>
/// <param name="exception">The exception.</param>
/// <returns>An error response for <paramref name="exception"/> with error message <paramref name="message"/>
/// and status code <paramref name="statusCode"/>.</returns>
public static HttpResponseMessage CreateErrorResponse(
this HttpRequestMessage request,
HttpStatusCode statusCode,
string message,
Exception exception)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
if (message == null)
{
throw new ArgumentNullException(nameof(message));
}
if (exception == null)
{
throw new ArgumentNullException(nameof(exception));
}
var error = new HttpError(exception, includeErrorDetail: false) { Message = message };
return request.CreateErrorResponse(statusCode, error);
}
/// <summary>
/// Helper method that performs content negotiation and creates a <see cref="HttpResponseMessage"/>
/// representing an error with an instance of <see cref="ObjectContent{T}"/> wrapping an
/// <see cref="HttpError"/> for exception <paramref name="exception"/>. If no formatter is found, this method
/// returns a response with status 406 NotAcceptable.
/// </summary>
/// <remarks>
/// This method requires that <paramref name="request"/> has been associated with an instance of
/// <see cref="HttpContext"/>.
/// </remarks>
/// <param name="request">The request.</param>
/// <param name="statusCode">The status code of the created response.</param>
/// <param name="exception">The exception.</param>
/// <returns>
/// An error response for <paramref name="exception"/> with status code <paramref name="statusCode"/>.
/// </returns>
public static HttpResponseMessage CreateErrorResponse(
this HttpRequestMessage request,
HttpStatusCode statusCode,
Exception exception)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
if (exception == null)
{
throw new ArgumentNullException(nameof(exception));
}
return request.CreateErrorResponse(statusCode, new HttpError(exception, includeErrorDetail: false));
}
/// <summary>
/// Helper method that performs content negotiation and creates a <see cref="HttpResponseMessage"/>
/// representing an error with an instance of <see cref="ObjectContent{T}"/> wrapping an
/// <see cref="HttpError"/> for model state <paramref name="modelState"/>. If no formatter is found, this
/// method returns a response with status 406 NotAcceptable.
/// </summary>
/// <remarks>
/// This method requires that <paramref name="request"/> has been associated with an instance of
/// <see cref="HttpContext"/>.
/// </remarks>
/// <param name="request">The request.</param>
/// <param name="statusCode">The status code of the created response.</param>
/// <param name="modelState">The model state.</param>
/// <returns>
/// An error response for <paramref name="modelState"/> with status code <paramref name="statusCode"/>.
/// </returns>
public static HttpResponseMessage CreateErrorResponse(
this HttpRequestMessage request,
HttpStatusCode statusCode,
ModelStateDictionary modelState)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
if (modelState == null)
{
throw new ArgumentNullException(nameof(modelState));
}
return request.CreateErrorResponse(statusCode, new HttpError(modelState, includeErrorDetail: false));
}
/// <summary>
/// Helper method that performs content negotiation and creates a <see cref="HttpResponseMessage"/>
/// representing an error with an instance of <see cref="ObjectContent{T}"/> wrapping <paramref name="error"/>
/// as the content. If no formatter is found, this method returns a response with status 406 NotAcceptable.
/// </summary>
/// <remarks>
/// This method requires that <paramref name="request"/> has been associated with an instance of
/// <see cref="HttpContext"/>.
/// </remarks>
/// <param name="request">The request.</param>
/// <param name="statusCode">The status code of the created response.</param>
/// <param name="error">The error to wrap.</param>
/// <returns>
/// An error response wrapping <paramref name="error"/> with status code <paramref name="statusCode"/>.
/// </returns>
public static HttpResponseMessage CreateErrorResponse(
this HttpRequestMessage request,
HttpStatusCode statusCode,
HttpError error)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
if (error == null)
{
throw new ArgumentNullException(nameof(error));
}
return request.CreateResponse<HttpError>(statusCode, error);
}
/// <summary>
/// Helper method that performs content negotiation and creates a <see cref="HttpResponseMessage"/> with an
/// instance of <see cref="ObjectContent{T}"/> as the content and <see cref="System.Net.HttpStatusCode.OK"/>
/// as the status code if a formatter can be found. If no formatter is found, this method returns a response
/// with status 406 NotAcceptable.
/// </summary>
/// <remarks>
/// This method requires that <paramref name="request"/> has been associated with an instance of
/// <see cref="HttpContext"/>.
/// </remarks>
/// <typeparam name="T">The type of the value.</typeparam>
/// <param name="request">The request.</param>
/// <param name="value">The value to wrap. Can be <c>null</c>.</param>
/// <returns>
/// A response wrapping <paramref name="value"/> with <see cref="System.Net.HttpStatusCode.OK"/> status code.
/// </returns>
public static HttpResponseMessage CreateResponse<T>(this HttpRequestMessage request, T value)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
return request.CreateResponse<T>(HttpStatusCode.OK, value, formatters: null);
}
/// <summary>
/// Helper method that performs content negotiation and creates a <see cref="HttpResponseMessage"/> with an
/// instance of <see cref="ObjectContent{T}"/> as the content if a formatter can be found. If no formatter is
/// found, this method returns a response with status 406 NotAcceptable.
/// configuration.
/// </summary>
/// <remarks>
/// This method requires that <paramref name="request"/> has been associated with an instance of
/// <see cref="HttpContext"/>.
/// </remarks>
/// <typeparam name="T">The type of the value.</typeparam>
/// <param name="request">The request.</param>
/// <param name="statusCode">The status code of the created response.</param>
/// <param name="value">The value to wrap. Can be <c>null</c>.</param>
/// <returns>A response wrapping <paramref name="value"/> with <paramref name="statusCode"/>.</returns>
public static HttpResponseMessage CreateResponse<T>(
this HttpRequestMessage request,
HttpStatusCode statusCode,
T value)
{
return request.CreateResponse<T>(statusCode, value, formatters: null);
}
/// <summary>
/// Helper method that performs content negotiation and creates a <see cref="HttpResponseMessage"/> with an
/// instance of <see cref="ObjectContent{T}"/> as the content if a formatter can be found. If no formatter is
/// found, this method returns a response with status 406 NotAcceptable.
/// </summary>
/// <remarks>
/// This method will get the <see cref="HttpContext"/> instance associated with <paramref name="request"/>.
/// </remarks>
/// <typeparam name="T">The type of the value.</typeparam>
/// <param name="request">The request.</param>
/// <param name="statusCode">The status code of the created response.</param>
/// <param name="value">The value to wrap. Can be <c>null</c>.</param>
/// <param name="formatters">The set of <see cref="MediaTypeFormatter"/> objects from which to choose.</param>
/// <returns>A response wrapping <paramref name="value"/> with <paramref name="statusCode"/>.</returns>
public static HttpResponseMessage CreateResponse<T>(
this HttpRequestMessage request,
HttpStatusCode statusCode,
T value,
IEnumerable<MediaTypeFormatter> formatters)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
var context = GetHttpContext(request);
if (formatters == null)
{
// Get the default formatters from options
var options = context.RequestServices.GetRequiredService<IOptions<WebApiCompatShimOptions>>();
formatters = options.Value.Formatters;
}
var contentNegotiator = context.RequestServices.GetRequiredService<IContentNegotiator>();
var result = contentNegotiator.Negotiate(typeof(T), request, formatters);
if (result?.Formatter == null)
{
// Return a 406 when we're actually performing conneg and it fails to find a formatter.
return new HttpResponseMessage(HttpStatusCode.NotAcceptable)
{
RequestMessage = request
};
}
else
{
return request.CreateResponse(statusCode, value, result.Formatter, result.MediaType);
}
}
/// <summary>
/// Helper method that creates a <see cref="HttpResponseMessage"/> with an <see cref="ObjectContent{T}"/>
/// instance containing the provided <paramref name="value"/>. The given <paramref name="mediaType"/> is used
/// to find an instance of <see cref="MediaTypeFormatter"/>.
/// </summary>
/// <typeparam name="T">The type of the value.</typeparam>
/// <param name="request">The request.</param>
/// <param name="statusCode">The status code of the created response.</param>
/// <param name="value">The value to wrap. Can be <c>null</c>.</param>
/// <param name="mediaType">
/// The media type used to look up an instance of <see cref="MediaTypeFormatter"/>.
/// </param>
/// <returns>A response wrapping <paramref name="value"/> with <paramref name="statusCode"/>.</returns>
public static HttpResponseMessage CreateResponse<T>(
this HttpRequestMessage request,
HttpStatusCode statusCode,
T value,
string mediaType)
{
return request.CreateResponse(statusCode, value, new MediaTypeHeaderValue(mediaType));
}
/// <summary>
/// Helper method that creates a <see cref="HttpResponseMessage"/> with an <see cref="ObjectContent{T}"/>
/// instance containing the provided <paramref name="value"/>. The given <paramref name="mediaType"/> is used
/// to find an instance of <see cref="MediaTypeFormatter"/>.
/// </summary>
/// <typeparam name="T">The type of the value.</typeparam>
/// <param name="request">The request.</param>
/// <param name="statusCode">The status code of the created response.</param>
/// <param name="value">The value to wrap. Can be <c>null</c>.</param>
/// <param name="mediaType">
/// The media type used to look up an instance of <see cref="MediaTypeFormatter"/>.
/// </param>
/// <returns>A response wrapping <paramref name="value"/> with <paramref name="statusCode"/>.</returns>
public static HttpResponseMessage CreateResponse<T>(
this HttpRequestMessage request,
HttpStatusCode statusCode,
T value,
MediaTypeHeaderValue mediaType)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
if (mediaType == null)
{
throw new ArgumentNullException(nameof(mediaType));
}
var context = GetHttpContext(request);
// Get the default formatters from options
var options = context.RequestServices.GetRequiredService<IOptions<WebApiCompatShimOptions>>();
var formatters = options.Value.Formatters;
var formatter = formatters.FindWriter(typeof(T), mediaType);
if (formatter == null)
{
var message = ShimResources.FormatHttpRequestMessage_CouldNotFindMatchingFormatter(
mediaType.ToString(),
value.GetType());
throw new InvalidOperationException(message);
}
return request.CreateResponse(statusCode, value, formatter, mediaType);
}
/// <summary>
/// Helper method that creates a <see cref="HttpResponseMessage"/> with an <see cref="ObjectContent{T}"/>
/// instance containing the provided <paramref name="value"/> and the given <paramref name="formatter"/>.
/// </summary>
/// <typeparam name="T">The type of the value.</typeparam>
/// <param name="request">The request.</param>
/// <param name="statusCode">The status code of the created response.</param>
/// <param name="value">The value to wrap. Can be <c>null</c>.</param>
/// <param name="formatter">The formatter to use.</param>
/// <returns>A response wrapping <paramref name="value"/> with <paramref name="statusCode"/>.</returns>
public static HttpResponseMessage CreateResponse<T>(
this HttpRequestMessage request,
HttpStatusCode statusCode,
T value,
MediaTypeFormatter formatter)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
if (formatter == null)
{
throw new ArgumentNullException(nameof(formatter));
}
return request.CreateResponse(statusCode, value, formatter, (MediaTypeHeaderValue)null);
}
/// <summary>
/// Helper method that creates a <see cref="HttpResponseMessage"/> with an <see cref="ObjectContent{T}"/>
/// instance containing the provided <paramref name="value"/> and the given <paramref name="formatter"/>.
/// </summary>
/// <typeparam name="T">The type of the value.</typeparam>
/// <param name="request">The request.</param>
/// <param name="statusCode">The status code of the created response.</param>
/// <param name="value">The value to wrap. Can be <c>null</c>.</param>
/// <param name="formatter">The formatter to use.</param>
/// <param name="mediaType">
/// The media type override to set on the response's content. Can be <c>null</c>.
/// </param>
/// <returns>A response wrapping <paramref name="value"/> with <paramref name="statusCode"/>.</returns>
public static HttpResponseMessage CreateResponse<T>(
this HttpRequestMessage request,
HttpStatusCode statusCode,
T value,
MediaTypeFormatter formatter,
string mediaType)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
if (formatter == null)
{
throw new ArgumentNullException(nameof(formatter));
}
var mediaTypeHeader = mediaType != null ? new MediaTypeHeaderValue(mediaType) : null;
return request.CreateResponse(statusCode, value, formatter, mediaTypeHeader);
}
/// <summary>
/// Helper method that creates a <see cref="HttpResponseMessage"/> with an <see cref="ObjectContent{T}"/>
/// instance containing the provided <paramref name="value"/> and the given <paramref name="formatter"/>.
/// </summary>
/// <typeparam name="T">The type of the value.</typeparam>
/// <param name="request">The request.</param>
/// <param name="statusCode">The status code of the created response.</param>
/// <param name="value">The value to wrap. Can be <c>null</c>.</param>
/// <param name="formatter">The formatter to use.</param>
/// <param name="mediaType">
/// The media type override to set on the response's content. Can be <c>null</c>.
/// </param>
/// <returns>A response wrapping <paramref name="value"/> with <paramref name="statusCode"/>.</returns>
public static HttpResponseMessage CreateResponse<T>(
this HttpRequestMessage request,
HttpStatusCode statusCode,
T value,
MediaTypeFormatter formatter,
MediaTypeHeaderValue mediaType)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
if (formatter == null)
{
throw new ArgumentNullException(nameof(formatter));
}
var response = new HttpResponseMessage(statusCode)
{
RequestMessage = request,
};
response.Content = new ObjectContent<T>(value, formatter, mediaType);
return response;
}
private static HttpContext GetHttpContext(HttpRequestMessage request)
{
var context = request.GetProperty<HttpContext>(nameof(HttpContext));
if (context == null)
{
var message = ShimResources.FormatHttpRequestMessage_MustHaveHttpContext(
nameof(HttpRequestMessage),
"HttpRequestMessageHttpContextExtensions.GetHttpRequestMessage");
throw new InvalidOperationException(message);
}
return context;
}
private static T GetProperty<T>(this HttpRequestMessage request, string key)
{
object value;
request.Properties.TryGetValue(key, out value);
if (value is T)
{
return (T)value;
}
else
{
return default(T);
}
}
}
}

View File

@ -1,77 +0,0 @@
// 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.Diagnostics;
using System.Net.Http;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
public class HttpRequestMessageFeature : IHttpRequestMessageFeature
{
private readonly HttpContext _httpContext;
private HttpRequestMessage _httpRequestMessage;
public HttpRequestMessageFeature(HttpContext httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException(nameof(httpContext));
}
_httpContext = httpContext;
}
public HttpRequestMessage HttpRequestMessage
{
get
{
if (_httpRequestMessage == null)
{
_httpRequestMessage = CreateHttpRequestMessage(_httpContext);
}
return _httpRequestMessage;
}
set
{
_httpRequestMessage = value;
}
}
private static HttpRequestMessage CreateHttpRequestMessage(HttpContext httpContext)
{
var httpRequest = httpContext.Request;
var uriString =
httpRequest.Scheme + "://" +
httpRequest.Host +
httpRequest.PathBase +
httpRequest.Path +
httpRequest.QueryString;
var message = new HttpRequestMessage(new HttpMethod(httpRequest.Method), uriString);
// This allows us to pass the message through APIs defined in legacy code and then
// operate on the HttpContext inside.
message.Properties[nameof(HttpContext)] = httpContext;
message.Content = new StreamContent(httpRequest.Body);
foreach (var header in httpRequest.Headers)
{
// Every header should be able to fit into one of the two header collections.
// Try message.Headers first since that accepts more of them.
if (!message.Headers.TryAddWithoutValidation(header.Key, (IEnumerable<string>)header.Value))
{
var added = message.Content.Headers.TryAddWithoutValidation(header.Key, (IEnumerable<string>)header.Value);
Debug.Assert(added);
}
}
return message;
}
}
}

View File

@ -1,24 +0,0 @@
// 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.Net.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
public static class HttpRequestMessageHttpContextExtensions
{
public static HttpRequestMessage GetHttpRequestMessage(this HttpContext httpContext)
{
var feature = httpContext.Features.Get<IHttpRequestMessageFeature>();
if (feature == null)
{
feature = new HttpRequestMessageFeature(httpContext);
httpContext.Features.Set(feature);
}
return feature.HttpRequestMessage;
}
}
}

View File

@ -1,26 +0,0 @@
// 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.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
/// <summary>
/// <see cref="IModelBinder"/> implementation to bind models of type <see cref="HttpRequestMessage"/>.
/// </summary>
public class HttpRequestMessageModelBinder : IModelBinder
{
/// <inheritdoc />
public Task BindModelAsync(ModelBindingContext bindingContext)
{
var model = bindingContext.HttpContext.GetHttpRequestMessage();
bindingContext.ValidationState.Add(model, new ValidationStateEntry() { SuppressValidation = true });
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
}

View File

@ -1,25 +0,0 @@
// 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.Net.Http;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
/// <summary>
/// <see cref="IModelBinderProvider"/> implementation to bind models of type <see cref="HttpRequestMessage"/>.
/// </summary>
public class HttpRequestMessageModelBinderProvider : IModelBinderProvider
{
/// <inheritdoc />
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context.Metadata.ModelType == typeof(HttpRequestMessage))
{
return new HttpRequestMessageModelBinder();
}
return null;
}
}
}

View File

@ -1,12 +0,0 @@
// 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.Net.Http;
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
public interface IHttpRequestMessageFeature
{
HttpRequestMessage HttpRequestMessage { get; set; }
}
}

View File

@ -1,41 +0,0 @@
// 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.Net;
using System.Net.Http;
using ShimResources = Microsoft.AspNetCore.Mvc.WebApiCompatShim.Resources;
namespace System.Web.Http
{
public class HttpResponseException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="HttpResponseException"/> class.
/// </summary>
/// <param name="statusCode">The status code of the response.</param>
public HttpResponseException(HttpStatusCode statusCode)
: this(new HttpResponseMessage(statusCode))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="HttpResponseException"/> class.
/// </summary>
/// <param name="response">The response message.</param>
public HttpResponseException(HttpResponseMessage response)
: base(ShimResources.HttpResponseExceptionMessage)
{
if (response == null)
{
throw new ArgumentNullException(nameof(response));
}
Response = response;
}
/// <summary>
/// Gets the <see cref="HttpResponseMessage"/> to return to the client.
/// </summary>
public HttpResponseMessage Response { get; private set; }
}
}

View File

@ -1,59 +0,0 @@
// 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.Net.Http;
using System.Web.Http;
using Microsoft.AspNetCore.Mvc.Filters;
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
/// <summary>
/// An action filter that sets <see cref="ActionExecutedContext.Result"/> to an <see cref="ObjectResult"/>
/// if the exception type is <see cref="HttpResponseException"/>.
/// This filter runs immediately after the action.
/// </summary>
public class HttpResponseExceptionActionFilter : IActionFilter, IOrderedFilter
{
/// <inheritdoc />
// Return a high number by default so that it runs closest to the action.
public int Order { get; set; } = int.MaxValue - 10;
/// <inheritdoc />
public void OnActionExecuting(ActionExecutingContext context)
{
}
/// <inheritdoc />
public void OnActionExecuted(ActionExecutedContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var httpResponseException = context.Exception as HttpResponseException;
if (httpResponseException != null)
{
var request = context.HttpContext.GetHttpRequestMessage();
var response = httpResponseException.Response;
if (response != null && response.RequestMessage == null)
{
response.RequestMessage = request;
}
var objectResult = new ObjectResult(response)
{
DeclaredType = typeof(HttpResponseMessage)
};
context.Result = objectResult;
// Its marked as handled as in webapi because an HttpResponseException
// was considered as a 'success' response.
context.ExceptionHandled = true;
}
}
}
}

View File

@ -1,22 +0,0 @@
// 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 Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace System.Web.Http
{
/// <summary>
/// An action result that returns an empty <see cref="StatusCodes.Status500InternalServerError"/> response.
/// </summary>
public class InternalServerErrorResult : StatusCodeResult
{
/// <summary>
/// Initializes a new instance of the <see cref="InternalServerErrorResult"/> class.
/// </summary>
public InternalServerErrorResult()
: base(StatusCodes.Status500InternalServerError)
{
}
}
}

View File

@ -1,51 +0,0 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace System.Web.Http
{
/// <summary>
/// An action result that returns a <see cref="StatusCodes.Status400BadRequest"/> response and performs
/// content negotiation on an <see cref="HttpError"/> based on a <see cref="ModelStateDictionary"/>.
/// </summary>
public class InvalidModelStateResult : ObjectResult
{
/// <summary>Initializes a new instance of the <see cref="InvalidModelStateResult"/> class.</summary>
/// <param name="modelState">The model state to include in the error.</param>
/// <param name="includeErrorDetail">
/// <see langword="true"/> if the error should include exception messages; otherwise, <see langword="false"/>.
/// </param>
public InvalidModelStateResult(ModelStateDictionary modelState, bool includeErrorDetail)
: base(new HttpError(modelState, includeErrorDetail))
{
if (modelState == null)
{
throw new ArgumentNullException(nameof(modelState));
}
ModelState = modelState;
IncludeErrorDetail = includeErrorDetail;
}
/// <summary>
/// Gets the model state to include in the error.
/// </summary>
public ModelStateDictionary ModelState { get; private set; }
/// <summary>
/// Gets a value indicating whether the error should include exception messages.
/// </summary>
public bool IncludeErrorDetail { get; private set; }
/// <inheritdoc />
public override Task ExecuteResultAsync(ActionContext context)
{
context.HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
return base.ExecuteResultAsync(context);
}
}
}

View File

@ -1,21 +0,0 @@
<Project Sdk="Internal.AspNetCore.Sdk">
<PropertyGroup>
<Description>Provides compatibility in ASP.NET Core MVC with ASP.NET Web API 2 to simplify migration of existing Web API implementations.
Commonly used types:
System.Web.Http.ApiController</Description>
<TargetFramework>netstandard2.0</TargetFramework>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;aspnetcoremvc;aspnetwebapi</PackageTags>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Core\Microsoft.AspNetCore.Mvc.Core.csproj" />
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Formatters.Json\Microsoft.AspNetCore.Mvc.Formatters.Json.csproj" />
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="$(MicrosoftAspNetWebApiClientPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="$(MicrosoftAspNetCoreWebUtilitiesPackageVersion)" />
</ItemGroup>
</Project>

View File

@ -1,40 +0,0 @@
// 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.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace System.Web.Http
{
/// <summary>
/// An action result that performs content negotiation.
/// </summary>
/// <typeparam name="T">The type of content in the entity body.</typeparam>
public class NegotiatedContentResult<T> : ObjectResult
{
/// <summary>
/// Initializes a new instance of the <see cref="NegotiatedContentResult{T}"/> class with the values provided.
/// </summary>
/// <param name="statusCode">The HTTP status code for the response message.</param>
/// <param name="content">The content value to negotiate and format in the entity body.</param>
public NegotiatedContentResult(HttpStatusCode statusCode, T content)
: base(content)
{
StatusCode = (int)statusCode;
Content = content;
}
/// <summary>
/// Gets the content value to negotiate and format in the entity body.
/// </summary>
public T Content { get; private set; }
/// <inheritdoc />
public override Task ExecuteResultAsync(ActionContext context)
{
context.HttpContext.Response.StatusCode = (int)StatusCode;
return base.ExecuteResultAsync(context);
}
}
}

View File

@ -1,193 +0,0 @@
// 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.ComponentModel;
using System.Linq;
using System.Net.Http.Formatting;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Routing;
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
/// <summary>
/// An <see cref="IActionConstraint"/> limiting candidate actions to those for which the request satisfies all
/// non-optional parameters.
/// </summary>
public class OverloadActionConstraint : IActionConstraint
{
/// <inheritdoc />
public int Order => int.MaxValue;
/// <inheritdoc />
public bool Accept(ActionConstraintContext context)
{
var candidates = context.Candidates.Select(c => new
{
Action = c,
Parameters = GetOverloadableParameters(c),
});
// Combined route value keys and query string keys. These are the values available for overload selection.
var requestKeys = GetCombinedKeys(context.RouteContext);
// Group candidates by the highest number of keys, and then process them until we find an action
// with all parameters satisfied.
foreach (var group in candidates.GroupBy(c => c.Parameters?.Count ?? 0).OrderByDescending(g => g.Key))
{
var foundMatch = false;
foreach (var candidate in group)
{
var allFound = true;
if (candidate.Parameters != null)
{
foreach (var parameter in candidate.Parameters)
{
if (!requestKeys.Contains(parameter.Prefix))
{
if (candidate.Action.Action == context.CurrentCandidate.Action)
{
return false;
}
allFound = false;
break;
}
}
}
if (allFound)
{
foundMatch = true;
}
}
if (foundMatch)
{
return group.Any(c => c.Action.Action == context.CurrentCandidate.Action);
}
}
return false;
}
private List<OverloadedParameter> GetOverloadableParameters(ActionSelectorCandidate candidate)
{
if (candidate.Action.Parameters == null)
{
return null;
}
var isOverloaded = false;
foreach (var constraint in candidate.Constraints)
{
if (constraint is OverloadActionConstraint)
{
isOverloaded = true;
}
}
if (!isOverloaded)
{
return null;
}
var parameters = new List<OverloadedParameter>();
candidate.Action.Properties.TryGetValue("OptionalParameters", out var optionalParametersObject);
var optionalParameters = (HashSet<string>)optionalParametersObject;
foreach (var parameter in candidate.Action.Parameters)
{
// We only consider parameters that are marked as bound from the URL.
var source = parameter.BindingInfo?.BindingSource;
if (source == null)
{
continue;
}
if ((source.CanAcceptDataFrom(BindingSource.Path) ||
source.CanAcceptDataFrom(BindingSource.Query)) &&
CanConvertFromString(parameter.ParameterType))
{
if (optionalParameters != null)
{
var isOptional = optionalParameters.Contains(parameter.Name);
if (isOptional)
{
// Optional parameters are ignored in overloading. If a parameter doesn't specify that it's
// required then treat it as optional (MVC default). WebAPI parameters will all by-default
// specify themselves as required unless they have a default value.
continue;
}
}
var prefix = parameter.BindingInfo?.BinderModelName ?? parameter.Name;
parameters.Add(new OverloadedParameter()
{
ParameterDescriptor = parameter,
Prefix = prefix,
});
}
}
return parameters;
}
private static ISet<string> GetCombinedKeys(RouteContext routeContext)
{
var keys = new HashSet<string>(routeContext.RouteData.Values.Keys, StringComparer.OrdinalIgnoreCase);
keys.Remove("controller");
keys.Remove("action");
var queryString = routeContext.HttpContext.Request.QueryString.ToUriComponent();
if (queryString.Length > 0)
{
// We need to chop off the leading '?'
var queryData = new FormDataCollection(queryString.Substring(1));
var queryNameValuePairs = queryData.GetJQueryNameValuePairs();
if (queryNameValuePairs != null)
{
foreach (var queryNameValuePair in queryNameValuePairs)
{
keys.Add(queryNameValuePair.Key);
}
}
}
return keys;
}
private static bool CanConvertFromString(Type destinationType)
{
destinationType = Nullable.GetUnderlyingType(destinationType) ?? destinationType;
return IsSimpleType(destinationType) ||
TypeDescriptor.GetConverter(destinationType).CanConvertFrom(typeof(string));
}
private static bool IsSimpleType(Type type)
{
return type.GetTypeInfo().IsPrimitive ||
type.Equals(typeof(decimal)) ||
type.Equals(typeof(string)) ||
type.Equals(typeof(DateTime)) ||
type.Equals(typeof(Guid)) ||
type.Equals(typeof(DateTimeOffset)) ||
type.Equals(typeof(TimeSpan)) ||
type.Equals(typeof(Uri));
}
private class OverloadedParameter
{
public ParameterDescriptor ParameterDescriptor { get; set; }
public string Prefix { get; set; }
}
}
}

View File

@ -1,32 +0,0 @@
// 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 Microsoft.AspNetCore.Mvc.ModelBinding;
using WebApiShimResources = Microsoft.AspNetCore.Mvc.WebApiCompatShim.Resources;
namespace System.Web.Http
{
/// <summary>
/// An attribute that specifies that the value can be bound from the query string or route data.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
public class FromUriAttribute :
Attribute,
IOptionalBinderMetadata,
IBindingSourceMetadata,
IModelNameProvider
{
private static readonly BindingSource FromUriSource = CompositeBindingSource.Create(
new BindingSource[] { BindingSource.Path, BindingSource.Query },
WebApiShimResources.BindingSource_URL);
/// <inheritdoc />
public BindingSource BindingSource { get { return FromUriSource; } }
/// <inheritdoc />
public bool IsOptional { get; set; }
/// <inheritdoc />
public string Name { get; set; }
}
}

View File

@ -1,24 +0,0 @@
// 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.
namespace Microsoft.AspNetCore.Mvc.ModelBinding
{
/// <summary>
/// <para>
/// An type that designates an optional parameter for the purposes
/// of ASP.NET Web API action overloading. Optional parameters do not participate in overloading, and
/// do not have to have a value for the action to be selected.
/// </para>
/// <para>
/// This has no impact when used without ASP.NET Web API action overloading.
/// </para>
/// </summary>
public interface IOptionalBinderMetadata
{
/// <summary>
/// Gets a value indicating whether the parameter participates in ASP.NET Web API action overloading. If
/// <c>true</c>, the parameter does not participate in overloading. Otherwise, it does.
/// </summary>
bool IsOptional { get; }
}
}

View File

@ -1,11 +0,0 @@
// 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.Runtime.CompilerServices;
[assembly: TypeForwardedTo(typeof(System.Net.Http.Formatting.ContentNegotiationResult))]
[assembly: TypeForwardedTo(typeof(System.Net.Http.Formatting.DefaultContentNegotiator))]
[assembly: TypeForwardedTo(typeof(System.Net.Http.Formatting.FormDataCollection ))]
[assembly: TypeForwardedTo(typeof(System.Net.Http.Formatting.IContentNegotiator))]
[assembly: TypeForwardedTo(typeof(System.Net.Http.Formatting.MediaTypeFormatterMatch))]
[assembly: TypeForwardedTo(typeof(System.Net.Http.Formatting.MediaTypeFormatterMatchRanking))]

View File

@ -1,184 +0,0 @@
// <auto-generated />
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
using System.Globalization;
using System.Reflection;
using System.Resources;
internal static class Resources
{
private static readonly ResourceManager _resourceManager
= new ResourceManager("Microsoft.AspNetCore.Mvc.WebApiCompatShim.Resources", typeof(Resources).GetTypeInfo().Assembly);
/// <summary>
/// The request is invalid.
/// </summary>
internal static string HttpError_BadRequest
{
get => GetString("HttpError_BadRequest");
}
/// <summary>
/// The request is invalid.
/// </summary>
internal static string FormatHttpError_BadRequest()
=> GetString("HttpError_BadRequest");
/// <summary>
/// An error has occurred.
/// </summary>
internal static string HttpError_GenericError
{
get => GetString("HttpError_GenericError");
}
/// <summary>
/// An error has occurred.
/// </summary>
internal static string FormatHttpError_GenericError()
=> GetString("HttpError_GenericError");
/// <summary>
/// The model state is valid.
/// </summary>
internal static string HttpError_ValidModelState
{
get => GetString("HttpError_ValidModelState");
}
/// <summary>
/// The model state is valid.
/// </summary>
internal static string FormatHttpError_ValidModelState()
=> GetString("HttpError_ValidModelState");
/// <summary>
/// Could not find a formatter matching the media type '{0}' that can write an instance of '{1}'.
/// </summary>
internal static string HttpRequestMessage_CouldNotFindMatchingFormatter
{
get => GetString("HttpRequestMessage_CouldNotFindMatchingFormatter");
}
/// <summary>
/// Could not find a formatter matching the media type '{0}' that can write an instance of '{1}'.
/// </summary>
internal static string FormatHttpRequestMessage_CouldNotFindMatchingFormatter(object p0, object p1)
=> string.Format(CultureInfo.CurrentCulture, GetString("HttpRequestMessage_CouldNotFindMatchingFormatter"), p0, p1);
/// <summary>
/// The {0} instance is not properly initialized. Use {1} to create an {0} for the current request.
/// </summary>
internal static string HttpRequestMessage_MustHaveHttpContext
{
get => GetString("HttpRequestMessage_MustHaveHttpContext");
}
/// <summary>
/// The {0} instance is not properly initialized. Use {1} to create an {0} for the current request.
/// </summary>
internal static string FormatHttpRequestMessage_MustHaveHttpContext(object p0, object p1)
=> string.Format(CultureInfo.CurrentCulture, GetString("HttpRequestMessage_MustHaveHttpContext"), p0, p1);
/// <summary>
/// The {0} only supports writing objects of type {1}.
/// </summary>
internal static string HttpResponseMessageFormatter_UnsupportedType
{
get => GetString("HttpResponseMessageFormatter_UnsupportedType");
}
/// <summary>
/// The {0} only supports writing objects of type {1}.
/// </summary>
internal static string FormatHttpResponseMessageFormatter_UnsupportedType(object p0, object p1)
=> string.Format(CultureInfo.CurrentCulture, GetString("HttpResponseMessageFormatter_UnsupportedType"), p0, p1);
/// <summary>
/// The key is invalid JQuery syntax because it is missing a closing bracket.
/// </summary>
internal static string JQuerySyntaxMissingClosingBracket
{
get => GetString("JQuerySyntaxMissingClosingBracket");
}
/// <summary>
/// The key is invalid JQuery syntax because it is missing a closing bracket.
/// </summary>
internal static string FormatJQuerySyntaxMissingClosingBracket()
=> GetString("JQuerySyntaxMissingClosingBracket");
/// <summary>
/// The number of keys in a NameValueCollection has exceeded the limit of '{0}'. You can adjust it by modifying the MaxHttpCollectionKeys property on the '{1}' class.
/// </summary>
internal static string MaxHttpCollectionKeyLimitReached
{
get => GetString("MaxHttpCollectionKeyLimitReached");
}
/// <summary>
/// The number of keys in a NameValueCollection has exceeded the limit of '{0}'. You can adjust it by modifying the MaxHttpCollectionKeys property on the '{1}' class.
/// </summary>
internal static string FormatMaxHttpCollectionKeyLimitReached(object p0, object p1)
=> string.Format(CultureInfo.CurrentCulture, GetString("MaxHttpCollectionKeyLimitReached"), p0, p1);
/// <summary>
/// Processing of the HTTP request resulted in an exception. Please see the HTTP response returned by the 'Response' property of this exception for details.
/// </summary>
internal static string HttpResponseExceptionMessage
{
get => GetString("HttpResponseExceptionMessage");
}
/// <summary>
/// Processing of the HTTP request resulted in an exception. Please see the HTTP response returned by the 'Response' property of this exception for details.
/// </summary>
internal static string FormatHttpResponseExceptionMessage()
=> GetString("HttpResponseExceptionMessage");
/// <summary>
/// Failed to generate a URL using route '{0}'.
/// </summary>
internal static string CreatedAtRoute_RouteFailed
{
get => GetString("CreatedAtRoute_RouteFailed");
}
/// <summary>
/// Failed to generate a URL using route '{0}'.
/// </summary>
internal static string FormatCreatedAtRoute_RouteFailed(object p0)
=> string.Format(CultureInfo.CurrentCulture, GetString("CreatedAtRoute_RouteFailed"), p0);
/// <summary>
/// URL
/// </summary>
internal static string BindingSource_URL
{
get => GetString("BindingSource_URL");
}
/// <summary>
/// URL
/// </summary>
internal static string FormatBindingSource_URL()
=> GetString("BindingSource_URL");
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
System.Diagnostics.Debug.Assert(value != null);
if (formatterNames != null)
{
for (var i = 0; i < formatterNames.Length; i++)
{
value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
}
}
return value;
}
}
}

View File

@ -1,153 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="HttpError_BadRequest" xml:space="preserve">
<value>The request is invalid.</value>
</data>
<data name="HttpError_GenericError" xml:space="preserve">
<value>An error has occurred.</value>
</data>
<data name="HttpError_ValidModelState" xml:space="preserve">
<value>The model state is valid.</value>
</data>
<data name="HttpRequestMessage_CouldNotFindMatchingFormatter" xml:space="preserve">
<value>Could not find a formatter matching the media type '{0}' that can write an instance of '{1}'.</value>
</data>
<data name="HttpRequestMessage_MustHaveHttpContext" xml:space="preserve">
<value>The {0} instance is not properly initialized. Use {1} to create an {0} for the current request.</value>
</data>
<data name="HttpResponseMessageFormatter_UnsupportedType" xml:space="preserve">
<value>The {0} only supports writing objects of type {1}.</value>
</data>
<data name="JQuerySyntaxMissingClosingBracket" xml:space="preserve">
<value>The key is invalid JQuery syntax because it is missing a closing bracket.</value>
</data>
<data name="MaxHttpCollectionKeyLimitReached" xml:space="preserve">
<value>The number of keys in a NameValueCollection has exceeded the limit of '{0}'. You can adjust it by modifying the MaxHttpCollectionKeys property on the '{1}' class.</value>
</data>
<data name="HttpResponseExceptionMessage" xml:space="preserve">
<value>Processing of the HTTP request resulted in an exception. Please see the HTTP response returned by the 'Response' property of this exception for details.</value>
</data>
<data name="CreatedAtRoute_RouteFailed" xml:space="preserve">
<value>Failed to generate a URL using route '{0}'.</value>
</data>
<data name="BindingSource_URL" xml:space="preserve">
<value>URL</value>
</data>
</root>

View File

@ -1,34 +0,0 @@
// 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.Net.Http;
using Microsoft.AspNetCore.Mvc;
namespace System.Web.Http
{
/// <summary>
/// An action result that returns a specified response message.
/// </summary>
public class ResponseMessageResult : ObjectResult
{
/// <summary>
/// Initializes a new instance of the <see cref="ResponseMessageResult"/> class.
/// </summary>
/// <param name="response">The response message.</param>
public ResponseMessageResult(HttpResponseMessage response)
: base(response)
{
if (response == null)
{
throw new ArgumentNullException(nameof(response));
}
Response = response;
}
/// <summary>
/// Gets the response message.
/// </summary>
public HttpResponseMessage Response { get; private set; }
}
}

View File

@ -1,70 +0,0 @@
// 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.Collections.Generic;
using Microsoft.AspNetCore.Mvc.WebApiCompatShim;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Constraints;
namespace Microsoft.AspNetCore.Builder
{
public static class WebApiCompatShimRouteBuilderExtensions
{
public static IRouteBuilder MapWebApiRoute(
this IRouteBuilder routeCollectionBuilder,
string name,
string template)
{
return MapWebApiRoute(routeCollectionBuilder, name, template, defaults: null);
}
public static IRouteBuilder MapWebApiRoute(
this IRouteBuilder routeCollectionBuilder,
string name,
string template,
object defaults)
{
return MapWebApiRoute(routeCollectionBuilder, name, template, defaults, constraints: null);
}
public static IRouteBuilder MapWebApiRoute(
this IRouteBuilder routeCollectionBuilder,
string name,
string template,
object defaults,
object constraints)
{
return MapWebApiRoute(routeCollectionBuilder, name, template, defaults, constraints, dataTokens: null);
}
public static IRouteBuilder MapWebApiRoute(
this IRouteBuilder routeCollectionBuilder,
string name,
string template,
object defaults,
object constraints,
object dataTokens)
{
var mutableDefaults = ObjectToDictionary(defaults);
mutableDefaults.Add("area", WebApiCompatShimOptionsSetup.DefaultAreaName);
var mutableConstraints = ObjectToDictionary(constraints);
mutableConstraints.Add("area", new RequiredRouteConstraint());
return routeCollectionBuilder.MapRoute(name, template, mutableDefaults, mutableConstraints, dataTokens);
}
private static IDictionary<string, object> ObjectToDictionary(object value)
{
var dictionary = value as IDictionary<string, object>;
if (dictionary != null)
{
return new RouteValueDictionary(dictionary);
}
else
{
return new RouteValueDictionary(value);
}
}
}
}

View File

@ -1,26 +0,0 @@
// 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.Net.Http.Formatting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.WebApiCompatShim;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.DependencyInjection
{
public static class WebApiCompatShimMvcBuilderExtensions
{
public static IMvcBuilder AddWebApiConventions(this IMvcBuilder builder)
{
builder.Services.TryAddEnumerable(
ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, WebApiCompatShimOptionsSetup>());
builder.Services.TryAddEnumerable(
ServiceDescriptor.Transient<IConfigureOptions<WebApiCompatShimOptions>, WebApiCompatShimOptionsSetup>());
builder.Services.TryAddSingleton<IContentNegotiator, DefaultContentNegotiator>();
return builder;
}
}
}

View File

@ -1,19 +0,0 @@
// 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.Linq;
using System.Net.Http.Formatting;
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
public class WebApiCompatShimOptions
{
public WebApiCompatShimOptions()
{
// Start with an empty collection, our options setup will add the default formatters.
Formatters = new MediaTypeFormatterCollection(Enumerable.Empty<MediaTypeFormatter>());
}
public MediaTypeFormatterCollection Formatters { get; set; }
}
}

View File

@ -1,46 +0,0 @@
// 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.Net.Http;
using System.Net.Http.Formatting;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
public class WebApiCompatShimOptionsSetup
: IConfigureOptions<MvcOptions>, IConfigureOptions<WebApiCompatShimOptions>
{
public static readonly string DefaultAreaName = "api";
public string Name { get; set; }
public void Configure(MvcOptions options)
{
// Add webapi behaviors to controllers with the appropriate attributes
options.Conventions.Add(new WebApiActionConventionsApplicationModelConvention());
options.Conventions.Add(new WebApiParameterConventionsApplicationModelConvention());
options.Conventions.Add(new WebApiOverloadingApplicationModelConvention());
options.Conventions.Add(new WebApiRoutesApplicationModelConvention(area: DefaultAreaName));
// Add an action filter for handling the HttpResponseException.
options.Filters.Add(new HttpResponseExceptionActionFilter());
// Add a model binder to be able to bind HttpRequestMessage
options.ModelBinderProviders.Insert(0, new HttpRequestMessageModelBinderProvider());
// Add a formatter to write out an HttpResponseMessage to the response
options.OutputFormatters.Insert(0, new HttpResponseMessageOutputFormatter());
options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(HttpRequestMessage)));
options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(HttpResponseMessage)));
}
public void Configure(WebApiCompatShimOptions options)
{
// Add the default formatters
options.Formatters.AddRange(new MediaTypeFormatterCollection());
}
}
}

View File

@ -47,10 +47,10 @@
<ProjectReference Include="..\WebSites\SimpleWebSite\SimpleWebSite.csproj" />
<ProjectReference Include="..\WebSites\TagHelpersWebSite\TagHelpersWebSite.csproj" />
<ProjectReference Include="..\WebSites\VersioningWebSite\VersioningWebSite.csproj" />
<ProjectReference Include="..\WebSites\WebApiCompatShimWebSite\WebApiCompatShimWebSite.csproj" />
<ProjectReference Include="..\WebSites\XmlFormattersWebSite\XmlFormattersWebSite.csproj" />
<PackageReference Include="AngleSharp" Version="$(AngleSharpPackageVersion)" />
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="$(MicrosoftAspNetWebApiClientPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.ChunkingCookieManager.Sources" PrivateAssets="All" Version="$(MicrosoftAspNetCoreChunkingCookieManagerSourcesPackageVersion)" />
<!--

View File

@ -1,4 +1,7 @@
using System;
// 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.Linq;
using System.Net;
using System.Net.Http;

View File

@ -1,295 +0,0 @@
// 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.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public class WebApiCompatShimActionResultTest : IClassFixture<MvcTestFixture<WebApiCompatShimWebSite.Startup>>
{
public WebApiCompatShimActionResultTest(MvcTestFixture<WebApiCompatShimWebSite.Startup> fixture)
{
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }
[Fact]
public async Task ApiController_BadRequest()
{
// Arrange & 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 & 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 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 & 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 & 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 & 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 & 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 & 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 & 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 & 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 & 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 & 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 & 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 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 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 & 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 & 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 & 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 & 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);
}
[Theory]
[InlineData("http://localhost/api/Blog/ActionResult/GetRedirectUri", "api/Blog")]
[InlineData(
"http://localhost/api/Blog/ActionResult/GetRedirectUrlUsingRouteName",
"/api/Blog/BasicApi/WriteToHttpContext")]
public async Task ApiController_RedirectUri(string url, string expected)
{
// Arrange & Act
var response = await Client.GetAsync(url);
// Assert
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
Assert.Equal(expected, response.Headers.Location.OriginalString);
}
[Fact]
public async Task ApiController_ResponseMessage()
{
// Arrange & 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 & Act
var response = await Client.GetAsync("http://localhost/api/Blog/ActionResult/GetStatusCode");
// Assert
Assert.Equal(HttpStatusCode.PaymentRequired, response.StatusCode);
}
}
}

View File

@ -1,592 +0,0 @@
// 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.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public class WebApiCompatShimActionSelectionTest : IClassFixture<MvcTestFixture<WebApiCompatShimWebSite.Startup>>
{
public WebApiCompatShimActionSelectionTest(MvcTestFixture<WebApiCompatShimWebSite.Startup> fixture)
{
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }
[Theory]
[InlineData("GET", "GetItems")]
[InlineData("PUT", "PutItems")]
[InlineData("POST", "PostItems")]
[InlineData("DELETE", "DeleteItems")]
[InlineData("PATCH", "PatchItems")]
[InlineData("HEAD", "HeadItems")]
[InlineData("OPTIONS", "OptionsItems")]
public async Task WebAPIConvention_TakesHttpMethodFromPrefix_UnnamedAction(string httpMethod, string actionName)
{
// Arrange
var request = new HttpRequestMessage(
new HttpMethod(httpMethod),
"http://localhost/api/Admin/WebAPIActionConventions");
// Act
var response = await Client.SendAsync(request);
// Assert
var data = Assert.Single(response.Headers.GetValues("ActionSelection"));
var result = JsonConvert.DeserializeObject<ActionSelectionResult>(data);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(actionName, result.ActionName);
}
[Theory]
[InlineData("GET", "GetItems")]
[InlineData("PUT", "PutItems")]
[InlineData("POST", "PostItems")]
[InlineData("DELETE", "DeleteItems")]
[InlineData("PATCH", "PatchItems")]
[InlineData("HEAD", "HeadItems")]
[InlineData("OPTIONS", "OptionsItems")]
public async Task WebAPIConvention_TakesHttpMethodFromPrefix_NamedAction(string httpMethod, string actionName)
{
// Arrange
var request = new HttpRequestMessage(
new HttpMethod(httpMethod),
"http://localhost/api/Blog/WebAPIActionConventions/" + actionName);
// Act
var response = await Client.SendAsync(request);
// Assert
var data = Assert.Single(response.Headers.GetValues("ActionSelection"));
var result = JsonConvert.DeserializeObject<ActionSelectionResult>(data);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(actionName, result.ActionName);
}
[Fact]
public async Task WebAPIConvention_TakesHttpMethodFromPrefix_NamedAction_MismatchedVerb()
{
// Arrange
var request = new HttpRequestMessage(
new HttpMethod("POST"),
"http://localhost/api/Blog/WebAPIActionConventions/GetItems");
// Act
var response = await Client.SendAsync(request);
// Assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task WebAPIConvention_TakesHttpMethodFromPrefix_UnnamedAction_DefaultVerbIsPost_Success()
{
// Arrange
var request = new HttpRequestMessage(
new HttpMethod("POST"),
"http://localhost/api/Admin/WebApiActionConventionsDefaultPost");
// Act
var response = await Client.SendAsync(request);
// Assert
var data = Assert.Single(response.Headers.GetValues("ActionSelection"));
var result = JsonConvert.DeserializeObject<ActionSelectionResult>(data);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("DefaultVerbIsPost", result.ActionName);
}
[Fact]
public async Task WebAPIConvention_TakesHttpMethodFromPrefix_NamedAction_DefaultVerbIsPost_Success()
{
// Arrange
var request = new HttpRequestMessage(
new HttpMethod("POST"),
"http://localhost/api/Blog/WebAPIActionConventionsDefaultPost/DefaultVerbIsPost");
// Act
var response = await Client.SendAsync(request);
// Assert
var data = Assert.Single(response.Headers.GetValues("ActionSelection"));
var result = JsonConvert.DeserializeObject<ActionSelectionResult>(data);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("DefaultVerbIsPost", result.ActionName);
}
[Fact]
public async Task WebAPIConvention_TakesHttpMethodFromPrefix_UnnamedAction_DefaultVerbIsPost_VerbMismatch()
{
// Arrange
var request = new HttpRequestMessage(
new HttpMethod("GET"),
"http://localhost/api/Admin/WebApiActionConventionsDefaultPost");
// Act
var response = await Client.SendAsync(request);
// Assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task WebAPIConvention_TakesHttpMethodFromPrefix_NamedAction_DefaultVerbIsPost_VerbMismatch()
{
// Arrange
var request = new HttpRequestMessage(
new HttpMethod("PUT"),
"http://localhost/api/Blog/WebApiActionConventionsDefaultPost/DefaultVerbIsPost");
// Act
var response = await Client.SendAsync(request);
// Assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task WebAPIConvention_TakesHttpMethodFromMethodName_NotActionName_UnnamedAction_Success()
{
// Arrange
var request = new HttpRequestMessage(
new HttpMethod("POST"),
"http://localhost/api/Admin/WebAPIActionConventionsActionName");
// Act
var response = await Client.SendAsync(request);
// Assert
var data = Assert.Single(response.Headers.GetValues("ActionSelection"));
var result = JsonConvert.DeserializeObject<ActionSelectionResult>(data);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("GetItems", result.ActionName);
}
[Fact]
public async Task WebAPIConvention_TakesHttpMethodFromMethodName_NotActionName_NamedAction_Success()
{
// Arrange
var request = new HttpRequestMessage(
new HttpMethod("POST"),
"http://localhost/api/Blog/WebAPIActionConventionsActionName/GetItems");
// Act
var response = await Client.SendAsync(request);
// Assert
var data = Assert.Single(response.Headers.GetValues("ActionSelection"));
var result = JsonConvert.DeserializeObject<ActionSelectionResult>(data);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("GetItems", result.ActionName);
}
[Fact]
public async Task WebAPIConvention_TakesHttpMethodFromMethodName_NotActionName_UnnamedAction_VerbMismatch()
{
// Arrange
var request = new HttpRequestMessage(
new HttpMethod("Get"),
"http://localhost/api/Admin/WebAPIActionConventionsActionName");
// Act
var response = await Client.SendAsync(request);
// Assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task WebAPIConvention_TakesHttpMethodFromMethodName_NotActionName_NamedAction_VerbMismatch()
{
// Arrange
var request = new HttpRequestMessage(
new HttpMethod("GET"),
"http://localhost/api/Blog/WebAPIActionConventionsActionName/GetItems");
// Act
var response = await Client.SendAsync(request);
// Assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task WebAPIConvention_HttpMethodOverride_UnnamedAction_Success()
{
// Arrange
var request = new HttpRequestMessage(
new HttpMethod("GET"),
"http://localhost/api/Admin/WebAPIActionConventionsVerbOverride");
// Act
var response = await Client.SendAsync(request);
// Assert
var data = Assert.Single(response.Headers.GetValues("ActionSelection"));
var result = JsonConvert.DeserializeObject<ActionSelectionResult>(data);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("PostItems", result.ActionName);
}
[Fact]
public async Task WebAPIConvention_HttpMethodOverride_NamedAction_Success()
{
// Arrange
var request = new HttpRequestMessage(
new HttpMethod("GET"),
"http://localhost/api/Blog/WebAPIActionConventionsVerbOverride/PostItems");
// Act
var response = await Client.SendAsync(request);
// Assert
var data = Assert.Single(response.Headers.GetValues("ActionSelection"));
var result = JsonConvert.DeserializeObject<ActionSelectionResult>(data);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("PostItems", result.ActionName);
}
[Fact]
public async Task WebAPIConvention_HttpMethodOverride_UnnamedAction_VerbMismatch()
{
// Arrange
var request = new HttpRequestMessage(
new HttpMethod("POST"),
"http://localhost/api/Admin/WebAPIActionConventionsVerbOverride");
// Act
var response = await Client.SendAsync(request);
// Assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task WebAPIConvention_HttpMethodOverride_NamedAction_VerbMismatch()
{
// Arrange
var request = new HttpRequestMessage(
new HttpMethod("POST"),
"http://localhost/api/Blog/WebAPIActionConventionsVerbOverride/PostItems");
// Act
var response = await Client.SendAsync(request);
// Assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
// This was ported from the WebAPI 5.2 codebase. Kept the same intentionally for compatibility.
[Theory]
[InlineData("GET", "api/Admin/Test", "GetUsers")]
[InlineData("GET", "api/Admin/Test/2", "GetUser")]
[InlineData("GET", "api/Admin/Test/3?name=mario", "GetUserByNameAndId")]
[InlineData("GET", "api/Admin/Test/3?name=mario&ssn=123456", "GetUserByNameIdAndSsn")]
[InlineData("GET", "api/Admin/Test?name=mario&ssn=123456", "GetUserByNameAndSsn")]
[InlineData("GET", "api/Admin/Test?name=mario&ssn=123456&age=3", "GetUserByNameAgeAndSsn")]
[InlineData("GET", "api/Admin/Test/5?random=9", "GetUser")]
// Note: Normally the following would not match DeleteUserByIdAndOptName because it has 'id' and 'age' as parameters while the DeleteUserByIdAndOptName action has 'id' and 'name'.
// However, because the default value is provided on action parameter 'name', having the 'id' in the request was enough to match the action.
[InlineData("DELETE", "api/Admin/Test/6?age=10", "DeleteUserByIdAndOptName")]
[InlineData("DELETE", "api/Admin/Test", "DeleteUserByOptName")]
[InlineData("DELETE", "api/Admin/Test?name=user", "DeleteUserByOptName")]
[InlineData("DELETE", "api/Admin/Test/6?email=user@test.com", "DeleteUserById_Email_OptName_OptPhone")]
[InlineData("DELETE", "api/Admin/Test/6?email=user@test.com&name=user", "DeleteUserById_Email_OptName_OptPhone")]
[InlineData("DELETE", "api/Admin/Test/6?email=user@test.com&name=user&phone=123456789", "DeleteUserById_Email_OptName_OptPhone")]
[InlineData("DELETE", "api/Admin/Test/6?email=user@test.com&height=1.8", "DeleteUserById_Email_Height_OptName_OptPhone")]
[InlineData("DELETE", "api/Admin/Test/6?email=user@test.com&height=1.8&name=user", "DeleteUserById_Email_Height_OptName_OptPhone")]
[InlineData("DELETE", "api/Admin/Test/6?email=user@test.com&height=1.8&name=user&phone=12345678", "DeleteUserById_Email_Height_OptName_OptPhone")]
[InlineData("HEAD", "api/Admin/Test/6", "Head_Id_OptSize_OptIndex")]
[InlineData("HEAD", "api/Admin/Test/6?size=2", "Head_Id_OptSize_OptIndex")]
[InlineData("HEAD", "api/Admin/Test/6?index=2", "Head_Id_OptSize_OptIndex")]
[InlineData("HEAD", "api/Admin/Test/6?index=2&size=10", "Head_Id_OptSize_OptIndex")]
[InlineData("HEAD", "api/Admin/Test/6?index=2&otherParameter=10", "Head_Id_OptSize_OptIndex")]
[InlineData("HEAD", "api/Admin/Test/6?otherQueryParameter=1234", "Head_Id_OptSize_OptIndex")]
[InlineData("HEAD", "api/Admin/Test", "Head")]
[InlineData("HEAD", "api/Admin/Test?otherParam=2", "Head")]
[InlineData("HEAD", "api/Admin/Test?index=2&size=10", "Head")]
public async Task LegacyActionSelection_OverloadedAction_WithUnnamedAction(string httpMethod, string requestUrl, string expectedActionName)
{
// Arrange
var request = new HttpRequestMessage(new HttpMethod(httpMethod), "http://localhost/" + requestUrl);
// Act
var response = await Client.SendAsync(request);
// Assert
var data = Assert.Single(response.Headers.GetValues("ActionSelection"));
var result = JsonConvert.DeserializeObject<ActionSelectionResult>(data);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(expectedActionName, result.ActionName);
}
[Theory]
[InlineData("api/Admin/Test", "PostUser")]
[InlineData("api/Admin/Test?name=mario&age=10", "PostUserByNameAndAge")]
public async Task LegacyActionSelection_OverloadedAction_WithUnnamedActionAndRequestContent(string requestUrl, string expectedActionName)
{
// Arrange
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/" + requestUrl);
request.Content = new StringContent("{}", Encoding.UTF8, "application/json");
// Act
var response = await Client.SendAsync(request);
// Assert
var data = Assert.Single(response.Headers.GetValues("ActionSelection"));
var result = JsonConvert.DeserializeObject<ActionSelectionResult>(data);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(expectedActionName, result.ActionName);
}
[Theory]
[InlineData("GET", "api/Store/Test", "GetUsers")]
[InlineData("GET", "api/Store/Test/2", "GetUsersByName")]
[InlineData("GET", "api/Store/Test/luigi?ssn=123456", "GetUserByNameAndSsn")]
[InlineData("GET", "api/Store/Test/luigi?ssn=123456&id=2&ssn=12345", "GetUserByNameIdAndSsn")]
[InlineData("GET", "api/Store/Test?age=10&ssn=123456", "GetUsers")]
[InlineData("GET", "api/Store/Test?id=3&ssn=123456&name=luigi", "GetUserByNameIdAndSsn")]
[InlineData("POST", "api/Store/Test/luigi?age=20", "PostUserByNameAndAge")]
public async Task LegacyActionSelection_OverloadedAction_NonIdRouteParameter(string httpMethod, string requestUrl, string expectedActionName)
{
// Arrange
var request = new HttpRequestMessage(new HttpMethod(httpMethod), "http://localhost/" + requestUrl);
// Act
var response = await Client.SendAsync(request);
// Assert
var data = Assert.Single(response.Headers.GetValues("ActionSelection"));
var result = JsonConvert.DeserializeObject<ActionSelectionResult>(data);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(expectedActionName, result.ActionName);
}
[Theory]
[InlineData("GET", "api/Admin/Test/3?NAME=mario", "GetUserByNameAndId")]
[InlineData("GET", "api/Admin/Test/3?name=mario&SSN=123456", "GetUserByNameIdAndSsn")]
[InlineData("GET", "api/Admin/Test?nAmE=mario&ssn=123456&AgE=3", "GetUserByNameAgeAndSsn")]
[InlineData("DELETE", "api/Admin/Test/6?AGe=10", "DeleteUserByIdAndOptName")]
public async Task LegacyActionSelection_OverloadedAction_Parameter_Casing(string httpMethod, string requestUrl, string expectedActionName)
{
// Arrange
var request = new HttpRequestMessage(new HttpMethod(httpMethod), "http://localhost/" + requestUrl);
// Act
var response = await Client.SendAsync(request);
// Assert
var data = Assert.Single(response.Headers.GetValues("ActionSelection"));
var result = JsonConvert.DeserializeObject<ActionSelectionResult>(data);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(expectedActionName, result.ActionName);
}
[Theory]
[InlineData("GET", "api/Blog/Test/GetUsers", "GetUsers")]
[InlineData("GET", "api/Blog/Test/GetUser/7", "GetUser")]
[InlineData("GET", "api/Blog/Test/GetUser?id=3", "GetUser")]
[InlineData("GET", "api/Blog/Test/GetUser/4?id=3", "GetUser")]
[InlineData("GET", "api/Blog/Test/GetUserByNameAgeAndSsn?name=user&age=90&ssn=123456789", "GetUserByNameAgeAndSsn")]
[InlineData("GET", "api/Blog/Test/GetUserByNameAndSsn?name=user&ssn=123456789", "GetUserByNameAndSsn")]
public async Task LegacyActionSelection_RouteWithActionName(string httpMethod, string requestUrl, string expectedActionName)
{
// Arrange
var request = new HttpRequestMessage(new HttpMethod(httpMethod), "http://localhost/" + requestUrl);
// Act
var response = await Client.SendAsync(request);
// Assert
var data = Assert.Single(response.Headers.GetValues("ActionSelection"));
var result = JsonConvert.DeserializeObject<ActionSelectionResult>(data);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(expectedActionName, result.ActionName);
}
[Fact]
public async Task LegacyActionSelection_RouteWithActionNameAndRequestContent()
{
// Arrange
var request = new HttpRequestMessage(
HttpMethod.Post,
"http://localhost/api/Blog/Test/PostUserByNameAndAddress?name=user");
request.Content = new StringContent("{}", Encoding.UTF8, "application/json");
// Act
var response = await Client.SendAsync(request);
// Assert
var data = Assert.Single(response.Headers.GetValues("ActionSelection"));
var result = JsonConvert.DeserializeObject<ActionSelectionResult>(data);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("PostUserByNameAndAddress", result.ActionName);
}
[Theory]
[InlineData("GET", "api/Blog/Test/getusers", "GetUsers")]
[InlineData("GET", "api/Blog/Test/getuseR/1", "GetUser")]
[InlineData("GET", "api/Blog/Test/Getuser?iD=3", "GetUser")]
[InlineData("GET", "api/Blog/Test/GetUser/4?Id=3", "GetUser")]
[InlineData("GET", "api/Blog/Test/GetUserByNameAgeandSsn?name=user&age=90&ssn=123456789", "GetUserByNameAgeAndSsn")]
[InlineData("GET", "api/Blog/Test/getUserByNameAndSsn?name=user&ssn=123456789", "GetUserByNameAndSsn")]
public async Task LegacyActionSelection_RouteWithActionName_Casing(string httpMethod, string requestUrl, string expectedActionName)
{
// Arrange
var request = new HttpRequestMessage(new HttpMethod(httpMethod), "http://localhost/" + requestUrl);
// Act
var response = await Client.SendAsync(request);
// Assert
var data = Assert.Single(response.Headers.GetValues("ActionSelection"));
var result = JsonConvert.DeserializeObject<ActionSelectionResult>(data);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(expectedActionName, result.ActionName);
}
[Fact]
public async Task LegacyActionSelection_RouteWithActionNameAndRequestContent_Casing()
{
// Arrange
var request = new HttpRequestMessage(
HttpMethod.Post,
"http://localhost/api/Blog/Test/PostUserByNameAndAddress?name=user");
request.Content = new StringContent("{}", Encoding.UTF8, "application/json");
// Act
var response = await Client.SendAsync(request);
// Assert
var data = Assert.Single(response.Headers.GetValues("ActionSelection"));
var result = JsonConvert.DeserializeObject<ActionSelectionResult>(data);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("PostUserByNameAndAddress", result.ActionName);
}
[Theory]
[InlineData("GET", "api/Admin/Test", "GetUsers")]
[InlineData("GET", "api/Admin/Test/?name=peach", "GetUsersByName")]
[InlineData("GET", "api/Admin/Test?name=peach", "GetUsersByName")]
[InlineData("GET", "api/Admin/Test?name=peach&ssn=123456", "GetUserByNameAndSsn")]
[InlineData("GET", "api/Admin/Test?name=peach&ssn=123456&age=3", "GetUserByNameAgeAndSsn")]
public async Task LegacyActionSelection_RouteWithoutActionName(string httpMethod, string requestUrl, string expectedActionName)
{
// Arrange
var request = new HttpRequestMessage(new HttpMethod(httpMethod), "http://localhost/" + requestUrl);
// Act
var response = await Client.SendAsync(request);
// Assert
var data = Assert.Single(response.Headers.GetValues("ActionSelection"));
var result = JsonConvert.DeserializeObject<ActionSelectionResult>(data);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(expectedActionName, result.ActionName);
}
[Theory]
[InlineData("GET", "api/Admin/ParameterAttribute/2", "GetUser")]
[InlineData("GET", "api/Admin/ParameterAttribute?id=2", "GetUser")]
[InlineData("GET", "api/Admin/ParameterAttribute?myId=2", "GetUserByMyId")]
[InlineData("DELETE", "api/Admin/ParameterAttribute/3?name=user", "DeleteUserWithNullableIdAndName")]
[InlineData("DELETE", "api/Admin/ParameterAttribute?address=userStreet", "DeleteUser")]
public async Task LegacyActionSelection_ModelBindingParameterAttribute_AreAppliedWhenSelectingActions(string httpMethod, string requestUrl, string expectedActionName)
{
// Arrange
var request = new HttpRequestMessage(new HttpMethod(httpMethod), "http://localhost/" + requestUrl);
request.Content = new StringContent("{}", Encoding.UTF8, "application/json");
// Act
var response = await Client.SendAsync(request);
// Assert
var data = Assert.Single(response.Headers.GetValues("ActionSelection"));
var result = JsonConvert.DeserializeObject<ActionSelectionResult>(data);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(expectedActionName, result.ActionName);
}
[Theory]
[InlineData("api/Admin/ParameterAttribute/3?name=user", "PostUserNameFromUri")]
[InlineData("api/Admin/ParameterAttribute/3", "PostUserNameFromBody")]
public async Task LegacyActionSelection_Post_ModelBindingParameterAttribute_AreAppliedWhenSelectingActions(string requestUrl, string expectedActionName)
{
// Arrange
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/" + requestUrl);
request.Content = new StringContent("{}", Encoding.UTF8, "application/json");
// Act
var response = await Client.SendAsync(request);
// Assert
var data = Assert.Single(response.Headers.GetValues("ActionSelection"));
var result = JsonConvert.DeserializeObject<ActionSelectionResult>(data);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(expectedActionName, result.ActionName);
}
[Theory]
[InlineData("GET", "api/Support/notActionParameterValue1/Test", "GetUsers")]
[InlineData("GET", "api/Support/notActionParameterValue2/Test/2", "GetUser")]
[InlineData("GET", "api/Support/notActionParameterValue1/Test?randomQueryVariable=val1", "GetUsers")]
[InlineData("GET", "api/Support/notActionParameterValue2/Test/2?randomQueryVariable=val2", "GetUser")]
public async Task LegacyActionSelection_ActionsThatHaveSubsetOfRouteParameters_AreConsideredForSelection(string httpMethod, string requestUrl, string expectedActionName)
{
// Arrange
var request = new HttpRequestMessage(new HttpMethod(httpMethod), "http://localhost/" + requestUrl);
// Act
var response = await Client.SendAsync(request);
// Assert
var data = Assert.Single(response.Headers.GetValues("ActionSelection"));
var result = JsonConvert.DeserializeObject<ActionSelectionResult>(data);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(expectedActionName, result.ActionName);
}
[Theory]
[InlineData("GET", "api/Admin/EnumParameterOverloads", "Get")]
[InlineData("GET", "api/Admin/EnumParameterOverloads?scope=global", "GetWithEnumParameter")]
[InlineData("GET", "api/Admin/EnumParameterOverloads?level=off&kind=trace", "GetWithTwoEnumParameters")]
[InlineData("GET", "api/Admin/EnumParameterOverloads?level=", "GetWithNullableEnumParameter")]
public async Task LegacyActionSelection_SelectAction_ReturnsActionDescriptor_ForEnumParameterOverloads(string httpMethod, string requestUrl, string expectedActionName)
{
// Arrange
var request = new HttpRequestMessage(new HttpMethod(httpMethod), "http://localhost/" + requestUrl);
// Act
var response = await Client.SendAsync(request);
// Assert
var data = Assert.Single(response.Headers.GetValues("ActionSelection"));
var result = JsonConvert.DeserializeObject<ActionSelectionResult>(data);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(expectedActionName, result.ActionName);
}
// Verify response has all the methods in its Allow header. values are unsorted.
private void AssertAllowedHeaders(HttpResponseMessage response, params HttpMethod[] allowedMethods)
{
foreach (var method in allowedMethods)
{
Assert.Contains(method.ToString(), response.Content.Headers.Allow);
}
Assert.Equal(allowedMethods.Length, response.Content.Headers.Allow.Count);
}
private class ActionSelectionResult
{
public string ActionName { get; set; }
public string ControllerName { get; set; }
}
}
}

View File

@ -1,469 +0,0 @@
// 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<MvcTestFixture<WebApiCompatShimWebSite.Startup>>
{
public WebApiCompatShimBasicTest(MvcTestFixture<WebApiCompatShimWebSite.Startup> fixture)
{
Client = fixture.CreateDefaultClient();
}
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,
typeof(FormUrlEncodedMediaTypeFormatter).FullName
};
// Act
var response = await Client.GetAsync("http://localhost/api/Blog/BasicApi/GetFormatters");
var content = await response.Content.ReadAsStringAsync();
var formatters = JsonConvert.DeserializeObject<string[]>(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<Dictionary<string, string>>(response);
Assert.Single(json);
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<Dictionary<string, string>>(response);
Assert.Single(json);
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<string> 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<string> 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<WebApiCompatShimWebSite.User>();
// 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<WebApiCompatShimWebSite.User>();
// 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<HttpError>();
// 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<WebApiCompatShimWebSite.User>();
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("Test User", user.Name);
Assert.Equal("text/json", response.Content.Headers.ContentType.MediaType);
}
// Use int for HttpStatusCode data because xUnit cannot serialize a GAC'd enum when running on .NET Framework.
[Theory]
[InlineData("http://localhost/Mvc/Index", (int)HttpStatusCode.OK)]
[InlineData("http://localhost/api/Blog/Mvc/Index", (int)HttpStatusCode.NotFound)]
public async Task WebApiRouting_AccessMvcController(string url, int expected)
{
// Arrange
var request = new HttpRequestMessage(HttpMethod.Get, url);
// Act
var response = await Client.SendAsync(request);
// Assert
Assert.Equal(expected, (int)response.StatusCode);
}
// Use int for HttpStatusCode data because xUnit cannot serialize a GAC'd enum when running on .NET Framework.
[Theory]
[InlineData("http://localhost/BasicApi/GenerateUrl", (int)HttpStatusCode.NotFound)]
[InlineData("http://localhost/api/Blog/BasicApi/GenerateUrl", (int)HttpStatusCode.OK)]
public async Task WebApiRouting_AccessWebApiController(string url, int expected)
{
// Arrange
var request = new HttpRequestMessage(HttpMethod.Get, url);
// Act
var response = await Client.SendAsync(request);
// Assert
Assert.Equal(expected, (int)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"));
}
}
}

View File

@ -1,199 +0,0 @@
// 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.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public class WebApiCompatShimParameterBindingTest : IClassFixture<MvcTestFixture<WebApiCompatShimWebSite.Startup>>
{
public WebApiCompatShimParameterBindingTest(MvcTestFixture<WebApiCompatShimWebSite.Startup> fixture)
{
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }
[Theory]
[InlineData("http://localhost/api/Blog/Employees/PostByIdDefault/5")]
[InlineData("http://localhost/api/Blog/Employees/PostByIdDefault?id=5")]
public async Task ApiController_SimpleParameter_Default_ReadsFromUrl(string url)
{
// Arrange
var request = new HttpRequestMessage(HttpMethod.Post, url);
// Act
var response = await Client.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("5", content);
}
[Fact]
public async Task ApiController_SimpleParameter_Default_DoesNotReadFormData()
{
// Arrange
var url = "http://localhost/api/Blog/Employees/PostByIdDefault";
var request = new HttpRequestMessage(HttpMethod.Post, url);
request.Content = new FormUrlEncodedContent(new Dictionary<string, string>()
{
{ "id", "5" },
});
// Act
var response = await Client.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("-1", content);
}
[Theory]
[InlineData("http://localhost/api/Blog/Employees/PostByIdModelBinder/5")]
[InlineData("http://localhost/api/Blog/Employees/PostByIdModelBinder?id=5")]
public async Task ApiController_SimpleParameter_ModelBinder_ReadsFromUrl(string url)
{
// Arrange
var request = new HttpRequestMessage(HttpMethod.Post, url);
// Act
var response = await Client.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("5", content);
}
[Fact]
public async Task ApiController_SimpleParameter_ModelBinder_ReadsFromFormData()
{
// Arrange
var url = "http://localhost/api/Blog/Employees/PostByIdModelBinder";
var request = new HttpRequestMessage(HttpMethod.Post, url);
request.Content = new FormUrlEncodedContent(new Dictionary<string, string>()
{
{ "id", "5" },
});
// Act
var response = await Client.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("5", content);
}
[Theory]
[InlineData("http://localhost/api/Blog/Employees/PostByIdFromQuery/5", "-1")]
[InlineData("http://localhost/api/Blog/Employees/PostByIdFromQuery?id=5", "5")]
public async Task ApiController_SimpleParameter_FromQuery_ReadsFromQueryNotRouteData(string url, string expected)
{
// Arrange
var request = new HttpRequestMessage(HttpMethod.Post, url);
// Act
var response = await Client.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(expected, content);
}
[Fact]
public async Task ApiController_SimpleParameter_FromQuery_DoesNotReadFormData()
{
// Arrange
var url = "http://localhost/api/Blog/Employees/PostByIdFromQuery";
var request = new HttpRequestMessage(HttpMethod.Post, url);
request.Content = new FormUrlEncodedContent(new Dictionary<string, string>()
{
{ "id", "5" },
});
// Act
var response = await Client.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("-1", content);
}
[Fact]
public async Task ApiController_ComplexParameter_Default_ReadsFromBody()
{
// Arrange
var url = "http://localhost/api/Blog/Employees/PutEmployeeDefault";
var request = new HttpRequestMessage(HttpMethod.Put, url);
request.Content = new StringContent(JsonConvert.SerializeObject(new
{
Id = 5,
Name = "Test Employee",
}));
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
// Act
var response = await Client.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("{\"id\":5,\"name\":\"Test Employee\"}", content);
}
[Fact]
public async Task ApiController_ComplexParameter_ModelBinder_ReadsFormAndUrl()
{
// Arrange
var url = "http://localhost/api/Blog/Employees/PutEmployeeModelBinder/5";
var request = new HttpRequestMessage(HttpMethod.Put, url);
request.Content = new FormUrlEncodedContent(new Dictionary<string, string>()
{
{ "name", "Test Employee" },
});
// Act
var response = await Client.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("{\"id\":5,\"name\":\"Test Employee\"}", content);
}
// name is read from the url - and the rest from the body (formatters)
[Fact]
public async Task ApiController_TwoParameters_DefaultSources()
{
// Arrange
var url = "http://localhost/api/Blog/Employees/PutEmployeeBothDefault?name=Name_Override";
var request = new HttpRequestMessage(HttpMethod.Put, url);
request.Content = new StringContent(JsonConvert.SerializeObject(new
{
Id = 5,
Name = "Test Employee",
}));
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
// Act
var response = await Client.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("{\"id\":5,\"name\":\"Name_Override\"}", content);
}
}
}

View File

@ -1,531 +0,0 @@
// 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.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.WebApiCompatShim;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;
namespace System.Web.Http
{
public class ApiControllerActionDiscoveryTest
{
[Fact]
public void GetActions_ApiControllerWithControllerSuffix_IsController()
{
// Arrange
var provider = CreateProvider();
// Act
var context = new ActionDescriptorProviderContext();
Invoke(provider, context);
var results = context.Results.Cast<ControllerActionDescriptor>();
// Assert
var controllerType = typeof(TestControllers.ProductsController).GetTypeInfo();
var actions = results.Where(ad => ad.ControllerTypeInfo == controllerType).ToArray();
Assert.NotEmpty(actions);
}
[Fact]
public void GetActions_ApiControllerWithoutControllerSuffix_IsNotController()
{
// Arrange
var provider = CreateProvider();
// Act
var context = new ActionDescriptorProviderContext();
Invoke(provider, context);
var results = context.Results.Cast<ControllerActionDescriptor>();
// Assert
var controllerType = typeof(TestControllers.Blog).GetTypeInfo();
var actions = results.Where(ad => ad.ControllerTypeInfo == controllerType);
Assert.Equal(2, actions.Count());
}
[Fact]
public void GetActions_CreatesNamedAndUnnamedAction()
{
// Arrange
var provider = CreateProvider();
// Act
var context = new ActionDescriptorProviderContext();
Invoke(provider, context);
var results = context.Results.Cast<ControllerActionDescriptor>();
// Assert
var controllerType = typeof(TestControllers.StoreController).GetTypeInfo();
var actions = results
.Where(ad => ad.ControllerTypeInfo == controllerType)
.Where(ad => ad.MethodInfo.Name == "GetAll")
.ToArray();
Assert.Equal(2, actions.Length);
var action = Assert.Single(
actions,
a => a.RouteValues.Any(rc => rc.Key == "action" && rc.Value == "GetAll"));
Assert.Equal(
new string[] { "GET" },
Assert.Single(action.ActionConstraints.OfType<HttpMethodActionConstraint>()).HttpMethods);
action = Assert.Single(
actions,
a => a.RouteValues.Any(rc => rc.Key == "action" && string.IsNullOrEmpty(rc.Value)));
Assert.Equal(
new string[] { "GET" },
Assert.Single(action.ActionConstraints.OfType<HttpMethodActionConstraint>()).HttpMethods);
}
[Fact]
public void GetActions_CreatesNamedAndUnnamedAction_DefaultVerbIsPost()
{
// Arrange
var provider = CreateProvider();
// Act
var context = new ActionDescriptorProviderContext();
Invoke(provider, context);
var results = context.Results.Cast<ControllerActionDescriptor>();
// Assert
var controllerType = typeof(TestControllers.StoreController).GetTypeInfo();
var actions = results
.Where(ad => ad.ControllerTypeInfo == controllerType)
.Where(ad => ad.MethodInfo.Name == "Edit")
.ToArray();
Assert.Equal(2, actions.Length);
var action = Assert.Single(
actions,
a => a.RouteValues.Any(rc => rc.Key == "action" && rc.Value == "Edit"));
Assert.Equal(
new string[] { "POST" },
Assert.Single(action.ActionConstraints.OfType<HttpMethodActionConstraint>()).HttpMethods);
action = Assert.Single(
actions,
a => a.RouteValues.Any(rc => rc.Key == "action" && string.IsNullOrEmpty(rc.Value)));
Assert.Equal(
new string[] { "POST" },
Assert.Single(action.ActionConstraints.OfType<HttpMethodActionConstraint>()).HttpMethods);
}
[Fact]
public void GetActions_CreatesNamedAndUnnamedAction_RespectsVerbAttribute()
{
// Arrange
var provider = CreateProvider();
// Act
var context = new ActionDescriptorProviderContext();
Invoke(provider, context);
var results = context.Results.Cast<ControllerActionDescriptor>();
// Assert
var controllerType = typeof(TestControllers.StoreController).GetTypeInfo();
var actions = results
.Where(ad => ad.ControllerTypeInfo == controllerType)
.Where(ad => ad.MethodInfo.Name == "Delete")
.ToArray();
Assert.Equal(2, actions.Length);
var action = Assert.Single(
actions,
a => a.RouteValues.Any(rc => rc.Key == "action" && rc.Value == "Delete"));
Assert.Equal(
new string[] { "PUT" },
Assert.Single(action.ActionConstraints.OfType<HttpMethodActionConstraint>()).HttpMethods);
action = Assert.Single(
actions,
a => a.RouteValues.Any(rc => rc.Key == "action" && string.IsNullOrEmpty(rc.Value)));
Assert.Equal(
new string[] { "PUT" },
Assert.Single(action.ActionConstraints.OfType<HttpMethodActionConstraint>()).HttpMethods);
}
// The method name is used to infer a verb, not the action name
[Fact]
public void GetActions_CreatesNamedAndUnnamedAction_VerbBasedOnMethodName()
{
// Arrange
var provider = CreateProvider();
// Act
var context = new ActionDescriptorProviderContext();
Invoke(provider, context);
var results = context.Results.Cast<ControllerActionDescriptor>();
// Assert
var controllerType = typeof(TestControllers.StoreController).GetTypeInfo();
var actions = results
.Where(ad => ad.ControllerTypeInfo == controllerType)
.Where(ad => ad.MethodInfo.Name == "Options")
.ToArray();
Assert.Equal(2, actions.Length);
var action = Assert.Single(
actions,
a => a.RouteValues.Any(rc => rc.Key == "action" && rc.Value == "GetOptions"));
Assert.Equal(
new string[] { "OPTIONS" },
Assert.Single(action.ActionConstraints.OfType<HttpMethodActionConstraint>()).HttpMethods);
action = Assert.Single(
actions,
a => a.RouteValues.Any(rc => rc.Key == "action" && string.IsNullOrEmpty(rc.Value)));
Assert.Equal(
new string[] { "OPTIONS" },
Assert.Single(action.ActionConstraints.OfType<HttpMethodActionConstraint>()).HttpMethods);
}
[Fact]
public void GetActions_AllWebApiActionsAreOverloaded()
{
// Arrange
var provider = CreateProvider();
// Act
var context = new ActionDescriptorProviderContext();
Invoke(provider, context);
var results = context.Results.Cast<ControllerActionDescriptor>();
// Assert
var controllerType = typeof(TestControllers.StoreController).GetTypeInfo();
var actions = results
.Where(ad => ad.ControllerTypeInfo == controllerType)
.ToArray();
Assert.NotEmpty(actions);
foreach (var action in actions)
{
Assert.Single(action.ActionConstraints, c => c is OverloadActionConstraint);
}
}
[Fact]
public void GetActions_AllWebApiActionsAreInWebApiArea()
{
// Arrange
var provider = CreateProvider();
// Act
var context = new ActionDescriptorProviderContext();
Invoke(provider, context);
var results = context.Results.Cast<ControllerActionDescriptor>();
// Assert
var controllerType = typeof(TestControllers.StoreController).GetTypeInfo();
var actions = results
.Where(ad => ad.ControllerTypeInfo == controllerType)
.ToArray();
Assert.NotEmpty(actions);
foreach (var action in actions)
{
Assert.Single(action.RouteValues, c => c.Key == "area" && c.Value== "api");
}
}
[Fact]
public void GetActions_Parameters_SimpleTypeFromUriByDefault()
{
// Arrange
var provider = CreateProvider();
// Act
var context = new ActionDescriptorProviderContext();
Invoke(provider, context);
var results = context.Results.Cast<ControllerActionDescriptor>();
// Assert
var controllerType = typeof(TestControllers.EmployeesController).GetTypeInfo();
var actions = results
.Where(ad => ad.ControllerTypeInfo == controllerType)
.Where(ad => ad.ActionName == "Get")
.ToArray();
Assert.NotEmpty(actions);
foreach (var action in actions)
{
var parameter = Assert.Single(action.Parameters);
Assert.Equal((new FromUriAttribute()).BindingSource, parameter.BindingInfo.BindingSource);
var optionalParameters = (HashSet<string>)action.Properties["OptionalParameters"];
Assert.DoesNotContain(parameter.Name, optionalParameters);
}
}
[Fact]
public void GetActions_Parameters_ComplexTypeFromBodyByDefault()
{
// Arrange
var provider = CreateProvider();
// Act
var context = new ActionDescriptorProviderContext();
Invoke(provider, context);
var results = context.Results.Cast<ControllerActionDescriptor>();
// Assert
var controllerType = typeof(TestControllers.EmployeesController).GetTypeInfo();
var actions = results
.Where(ad => ad.ControllerTypeInfo == controllerType)
.Where(ad => ad.ActionName == "Put")
.ToArray();
Assert.NotEmpty(actions);
foreach (var action in actions)
{
var parameter = Assert.Single(action.Parameters);
Assert.Equal(BindingSource.Body, parameter.BindingInfo.BindingSource);
}
}
[Fact]
public void GetActions_Parameters_WithBindingSource()
{
// Arrange
var provider = CreateProvider();
// Act
var context = new ActionDescriptorProviderContext();
Invoke(provider, context);
var results = context.Results.Cast<ControllerActionDescriptor>();
// Assert
var controllerType = typeof(TestControllers.EmployeesController).GetTypeInfo();
var actions = results
.Where(ad => ad.ControllerTypeInfo == controllerType)
.Where(ad => ad.ActionName == "Post")
.ToArray();
Assert.NotEmpty(actions);
foreach (var action in actions)
{
var parameter = Assert.Single(action.Parameters);
Assert.Null(parameter.BindingInfo.BindingSource);
}
}
[Theory]
[InlineData(nameof(TestControllers.EventsController.GetWithId))]
[InlineData(nameof(TestControllers.EventsController.GetWithEmployee))]
public void GetActions_Parameters_ImplicitOptional(string name)
{
// Arrange
var provider = CreateProvider();
// Act
var context = new ActionDescriptorProviderContext();
Invoke(provider, context);
var results = context.Results.Cast<ControllerActionDescriptor>();
// Assert
var controllerType = typeof(TestControllers.EventsController).GetTypeInfo();
var actions = results
.Where(ad => ad.ControllerTypeInfo == controllerType)
.Where(ad => ad.ActionName == name)
.ToArray();
Assert.NotEmpty(actions);
foreach (var action in actions)
{
var parameter = Assert.Single(action.Parameters);
Assert.Equal((new FromUriAttribute()).BindingSource, parameter.BindingInfo.BindingSource);
var optionalParameters = (HashSet<string>)action.Properties["OptionalParameters"];
Assert.Contains(parameter.Name, optionalParameters);
}
}
private ControllerActionDescriptorProvider CreateProvider()
{
var manager = GetApplicationManager(GetType().GetTypeInfo().Assembly.DefinedTypes.ToArray());
var options = new MvcOptions();
var setup = new WebApiCompatShimOptionsSetup();
setup.Configure(options);
var authorizationOptionsAccessor = new Mock<IOptions<AuthorizationOptions>>();
authorizationOptionsAccessor
.SetupGet(o => o.Value)
.Returns(new AuthorizationOptions());
var optionsAccessor = Options.Create(options);
var modelProvider = new DefaultApplicationModelProvider(optionsAccessor, TestModelMetadataProvider.CreateDefaultProvider());
var provider = new ControllerActionDescriptorProvider(
manager,
new[] { modelProvider },
optionsAccessor);
return provider;
}
private void Invoke(ControllerActionDescriptorProvider provider, ActionDescriptorProviderContext context)
{
provider.OnProvidersExecuting(context);
provider.OnProvidersExecuted(context);
}
private static ApplicationPartManager GetApplicationManager(params TypeInfo[] controllerTypes)
{
var manager = new ApplicationPartManager();
manager.ApplicationParts.Add(new TestPart(controllerTypes));
manager.FeatureProviders.Add(new TestProvider());
manager.FeatureProviders.Add(new NamespaceFilteredControllersFeatureProvider());
return manager;
}
private class TestPart : ApplicationPart, IApplicationPartTypeProvider
{
public TestPart(IEnumerable<TypeInfo> types)
{
Types = types;
}
public override string Name => "Test";
public IEnumerable<TypeInfo> Types { get; }
}
private class TestProvider : IApplicationFeatureProvider<ControllerFeature>
{
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
{
foreach (var type in parts.OfType<IApplicationPartTypeProvider>().SelectMany(t => t.Types))
{
feature.Controllers.Add(type);
}
}
}
private class NamespaceFilteredControllersFeatureProvider : IApplicationFeatureProvider<ControllerFeature>
{
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
{
var controllers = feature.Controllers.ToList();
foreach (var controller in controllers)
{
if (controller.Namespace != "System.Web.Http.TestControllers")
{
feature.Controllers.Remove(controller);
}
}
}
}
}
}
// These need to be public top-level classes to test discovery end-to-end. Don't reuse
// these outside of this test.
namespace System.Web.Http.TestControllers
{
public class ProductsController : ApiController
{
public IActionResult GetAll()
{
return null;
}
}
// Not a controller, because there's no controller suffix
public class Blog : ApiController
{
public IActionResult GetBlogPosts()
{
return null;
}
}
public class StoreController : ApiController
{
public IActionResult GetAll()
{
return null;
}
public IActionResult Edit(int id)
{
return null;
}
[HttpPut]
public IActionResult Delete(int id)
{
return null;
}
[ActionName("GetOptions")]
public IActionResult Options()
{
return null;
}
}
public class EmployeesController : ApiController
{
public IActionResult Get(int id)
{
return null;
}
public IActionResult Put(Employee employee)
{
return null;
}
public IActionResult Post([ModelBinder] Employee employee)
{
return null;
}
}
public class Employee
{
}
public class EventsController : ApiController
{
public IActionResult GetWithId(int id = 0)
{
return null;
}
public IActionResult GetWithEmployee([FromUri] Employee e = null)
{
return null;
}
}
}

View File

@ -1,416 +0,0 @@
// 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.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Security.Claims;
using System.Text;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Routing;
using Newtonsoft.Json;
using Xunit;
namespace System.Web.Http
{
public class ApiControllerTest
{
[Fact]
public void AccessDependentProperties()
{
// Arrange
var controller = new ConcreteApiController();
var httpContext = new DefaultHttpContext();
httpContext.User = new ClaimsPrincipal();
var actionContext = new ActionContext(httpContext, new RouteData(), new ControllerActionDescriptor());
// Act
controller.ControllerContext = new ControllerContext(actionContext);
// Assert
Assert.Same(httpContext, controller.Context);
Assert.Same(actionContext.ModelState, controller.ModelState);
Assert.Same(httpContext.User, controller.User);
}
[Fact]
public void AccessDependentProperties_UnsetContext()
{
// Arrange
var controller = new ConcreteApiController();
// Act & Assert
Assert.Null(controller.Context);
Assert.NotNull(controller.ModelState);
Assert.Null(controller.User);
}
[Fact]
public void ApiController_BadRequest()
{
// Arrange
var controller = new ConcreteApiController();
// Act
var result = controller.BadRequest();
// Assert
Assert.Equal(StatusCodes.Status400BadRequest, Assert.IsType<BadRequestResult>(result).StatusCode);
}
[Fact]
public void ApiController_BadRequest_Message()
{
// Arrange
var controller = new ConcreteApiController();
// Act
var result = controller.BadRequest("Error");
// Assert
var badRequest = Assert.IsType<BadRequestErrorMessageResult>(result);
Assert.Equal("Error", badRequest.Message);
var httpError = Assert.IsType<HttpError>(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<InvalidModelStateResult>(result);
var modelError = Assert.IsType<HttpError>(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<CreatedResult>(result);
Assert.Same(product, created.Value);
Assert.Equal(uri.OriginalString, 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<CreatedResult>(result);
Assert.Same(product, created.Value);
Assert.Equal(uri, created.Location);
}
[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<CreatedAtRouteResult>(result);
Assert.Same(product, created.Value);
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<CreatedAtRouteResult>(result);
Assert.Same(product, created.Value);
Assert.Equal("api_route", created.RouteName);
Assert.Equal("Products", created.RouteValues["controller"]);
Assert.Equal<KeyValuePair<string, object>>(values, created.RouteValues);
}
[Fact]
public void ApiController_Conflict()
{
// Arrange
var controller = new ConcreteApiController();
// Act
var result = controller.Conflict();
// Assert
Assert.Equal(StatusCodes.Status409Conflict, Assert.IsType<ConflictResult>(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<NegotiatedContentResult<Product>>(result);
Assert.Equal(StatusCodes.Status302Found, 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(StatusCodes.Status500InternalServerError, Assert.IsType<InternalServerErrorResult>(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<ExceptionResult>(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 jsonResult = Assert.IsType<JsonResult>(result);
Assert.Same(product, jsonResult.Value);
}
[Fact]
public void ApiController_Json_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 jsonResult = Assert.IsType<JsonResult>(result);
Assert.Same(product, jsonResult.Value);
Assert.Same(Encoding.UTF8, MediaType.GetEncoding(jsonResult.ContentType));
}
[Fact]
public void ApiController_NotFound()
{
// Arrange
var controller = new ConcreteApiController();
// Act
var result = controller.NotFound();
// Assert
Assert.Equal(404, Assert.IsType<NotFoundResult>(result).StatusCode);
}
[Fact]
public void ApiController_Ok()
{
// Arrange
var controller = new ConcreteApiController();
// Act
var result = controller.Ok();
// Assert
Assert.Equal(200, Assert.IsType<OkResult>(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<OkObjectResult>(result);
Assert.Same(product, okResult.Value);
}
[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<RedirectResult>(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<RedirectResult>(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<RedirectToRouteResult>(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<RedirectToRouteResult>(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<ResponseMessageResult>(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(StatusCodes.Status417ExpectationFailed, Assert.IsType<StatusCodeResult>(result).StatusCode);
}
private class Product
{
public string Name { get; set; }
public int Id { get; set; }
}
private class ConcreteApiController : ApiController
{
}
}
}

View File

@ -1,85 +0,0 @@
// 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.Buffers;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
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(httpContext, new RouteData(), new ActionDescriptor());
var result = new BadRequestErrorMessageResult("Error");
// Act
await result.ExecuteResultAsync(context);
// Assert
Assert.Equal(StatusCodes.Status400BadRequest, 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(httpContext, new RouteData(), 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 static IServiceProvider CreateServices()
{
var options = Options.Create(new MvcOptions());
options.Value.OutputFormatters.Add(new StringOutputFormatter());
options.Value.OutputFormatters.Add(new JsonOutputFormatter(
new JsonSerializerSettings(),
ArrayPool<char>.Shared));
var services = new ServiceCollection();
services.AddSingleton<IActionResultExecutor<ObjectResult>>(new ObjectResultExecutor(
new DefaultOutputFormatterSelector(options, NullLoggerFactory.Instance),
new TestHttpResponseStreamWriterFactory(),
NullLoggerFactory.Instance));
return services.BuildServiceProvider();
}
}
}

View File

@ -1,51 +0,0 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;
namespace System.Web.Http
{
public class ConflictResultTest
{
[Fact]
public async Task ConflictResult_SetsStatusCode()
{
// Arrange
var context = new ActionContext(GetHttpContext(), new RouteData(), new ActionDescriptor());
var result = new ConflictResult();
// Act
await result.ExecuteResultAsync(context);
// Assert
Assert.Equal(StatusCodes.Status409Conflict, context.HttpContext.Response.StatusCode);
}
private static IServiceCollection CreateServices()
{
var services = new ServiceCollection();
services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
return services;
}
private static HttpContext GetHttpContext()
{
var services = CreateServices();
var httpContext = new DefaultHttpContext();
httpContext.RequestServices = services.BuildServiceProvider();
return httpContext;
}
}
}

View File

@ -1,700 +0,0 @@
// 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.Collections.Generic;
using System.Linq;
using System.Net.Http.Formatting.Mocks;
using System.Net.Http.Headers;
using System.Text;
using Microsoft.AspNetCore.Testing;
using Microsoft.TestCommon;
using Newtonsoft.Json.Linq;
using Xunit;
namespace System.Net.Http.Formatting
{
public class DefaultContentNegotiatorTests
{
private readonly DefaultContentNegotiator _negotiator = new DefaultContentNegotiator();
private readonly HttpRequestMessage _request = new HttpRequestMessage();
public static TheoryData<string, string[], string> MatchRequestMediaTypeData
{
get
{
// string requestMediaType, string[] supportedMediaTypes, string expectedMediaType
return new TheoryData<string, string[], string>
{
{ "text/plain", new string[0], null },
{ "text/plain", new string[] { "text/xml", "application/xml" }, null },
{ "application/xml", new string[] { "application/xml", "text/xml" }, "application/xml" },
{ "APPLICATION/XML", new string[] { "text/xml", "application/xml" }, "application/xml" },
{ "application/xml; charset=utf-8", new string[] { "text/xml", "application/xml" }, "application/xml" },
{ "application/xml; charset=utf-8; parameter=value", new string[] { "text/xml", "application/xml" }, "application/xml" },
};
}
}
public static TheoryData<string[], string[], string, double, int> MatchAcceptHeaderData
{
get
{
// string[] acceptHeader, string[] supportedMediaTypes, string expectedMediaType, double matchQuality, int range
return new TheoryData<string[], string[], string, double, int>
{
{ new string[] { "text/plain" }, new string[0], null, 0.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral },
{ new string[] { "text/plain" }, new string[] { "text/xml", "application/xml" }, null, 0.0, (int)MediaTypeFormatterMatchRanking.None },
{ new string[] { "text/plain; q=0.5" }, new string[] { "text/xml", "application/xml" }, null, 0.0, (int)MediaTypeFormatterMatchRanking.None },
{ new string[] { "application/xml" }, new string[] { "application/xml", "text/xml" }, "application/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral },
{ new string[] { "APPLICATION/XML; q=0.5" }, new string[] { "text/xml", "application/xml" }, "application/xml", 0.5, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral },
{ new string[] { "text/xml; q=0.5", "APPLICATION/XML; q=0.7" }, new string[] { "text/xml", "application/xml" }, "application/xml", 0.7, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral },
{ new string[] { "application/xml; q=0.0" }, new string[] { "application/xml", "text/xml" }, null, 0.0, (int)MediaTypeFormatterMatchRanking.None },
{ new string[] { "APPLICATION/XML; q=0.0" }, new string[] { "text/xml", "application/xml" }, null, 0.0, (int)MediaTypeFormatterMatchRanking.None },
{ new string[] { "text/xml; q=0.0", "APPLICATION/XML; q=0.0" }, new string[] { "text/xml", "application/xml" }, null, 0.0, (int)MediaTypeFormatterMatchRanking.None },
{ new string[] { "text/*" }, new string[] { "text/xml", "application/xml" }, "text/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderSubtypeMediaRange },
{ new string[] { "text/*", "application/xml" }, new string[] { "text/xml", "application/xml" }, "application/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral },
{ new string[] { "text/*", "application/xml; q=0.5" }, new string[] { "text/xml", "application/xml" }, "text/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderSubtypeMediaRange },
{ new string[] { "text/*; q=0.5" }, new string[] { "text/xml", "application/xml" }, "text/xml", 0.5, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderSubtypeMediaRange },
{ new string[] { "text/*; q=0.5", "application/xml" }, new string[] { "text/xml", "application/xml" }, "application/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral },
{ new string[] { "text/*; q=0.0", "application/xml; q=0.0" }, new string[] { "text/xml", "application/xml" }, null, 0.0, (int)MediaTypeFormatterMatchRanking.None },
{ new string[] { "text/*; q=0.0", "application/xml" }, new string[] { "text/xml", "application/xml" }, "application/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral },
{ new string[] { "*/*; q=0.5" }, new string[] { "text/xml", "application/xml" }, "text/xml", 0.5, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderAllMediaRange },
{ new string[] { "*/*; q=0.0" }, new string[] { "text/xml", "application/xml" }, null, 0.0, (int)MediaTypeFormatterMatchRanking.None },
{ new string[] { "*/*; q=0.5", "application/xml" }, new string[] { "text/xml", "application/xml" }, "application/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral },
{ new string[] { "*/*; q=1.0", "application/xml; q=0.5" }, new string[] { "text/xml", "application/xml" }, "text/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderAllMediaRange },
{ new string[] { "*/*", "application/xml" }, new string[] { "text/xml", "application/xml" }, "application/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral },
{ new string[] { "text/*; q=0.5", "*/*; q=0.2", "application/xml; q=1.0" }, new string[] { "text/xml", "application/xml" }, "application/xml", 1.0, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral },
{ new string[] { "application/xml; q=0.5" }, new string[] { "text/xml", "application/xml" }, "application/xml", 0.5, (int)MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral },
};
}
}
public static TheoryData<bool, string[], bool> ShouldMatchOnTypeData
{
get
{
// bool excludeMatchOnType, string[] acceptHeaders, bool expectedResult
return new TheoryData<bool, string[], bool>
{
{ false, new string[0], true },
{ true, new string[0], true },
{ false, new string[] { "application/xml" }, true },
{ true, new string[] { "application/xml" }, false },
{ false, new string[] { "application/xml; q=1.0" }, true },
{ true, new string[] { "application/xml; q=1.0" }, false },
{ false, new string[] { "application/xml; q=0.0" }, true },
{ true, new string[] { "application/xml; q=0.0" }, false },
{ false, new string[] { "application/xml; q=0.0", "application/json" }, true },
{ true, new string[] { "application/xml; q=0.0", "application/json" }, false },
{ false, new string[] { "text/nomatch" }, true },
{ true, new string[] { "text/nomatch" }, false },
};
}
}
public static TheoryData<string[], string> MatchTypeData
{
get
{
// string[] supportedMediaTypes, string expectedMediaType
return new TheoryData<string[], string>
{
{ new string[0], "application/octet-stream" },
{ new string[] { "text/xml", "application/xml" }, "text/xml" },
{ new string[] { "application/xml", "text/xml" }, "application/xml" },
};
}
}
public static TheoryData<string[], string, string[], string> SelectResponseCharacterEncodingData
{
get
{
// string[] acceptEncodings, string requestEncoding, string[] supportedEncodings, string expectedEncoding
return new TheoryData<string[], string, string[], string>
{
{ new string[] { "utf-8" }, null, new string[0], null },
{ new string[0], "utf-8", new string[0], null },
{ new string[0], null, new string[] { "utf-8", "utf-16"}, "utf-8" },
{ new string[0], "utf-16", new string[] { "utf-8", "utf-16"}, "utf-16" },
{ new string[] { "utf-8" }, null, new string[] { "utf-8", "utf-16"}, "utf-8" },
{ new string[] { "utf-16" }, "utf-8", new string[] { "utf-8", "utf-16"}, "utf-16" },
{ new string[] { "utf-16; q=0.5" }, "utf-8", new string[] { "utf-8", "utf-16"}, "utf-16" },
{ new string[] { "utf-8; q=0.0" }, null, new string[] { "utf-8", "utf-16"}, "utf-8" },
{ new string[] { "utf-8; q=0.0" }, "utf-16", new string[] { "utf-8", "utf-16"}, "utf-16" },
{ new string[] { "utf-8; q=0.0", "utf-16; q=0.0" }, "utf-16", new string[] { "utf-8", "utf-16"}, "utf-16" },
{ new string[] { "utf-8; q=0.0", "utf-16; q=0.0" }, null, new string[] { "utf-8", "utf-16"}, "utf-8" },
{ new string[] { "*; q=0.0" }, null, new string[] { "utf-8", "utf-16"}, "utf-8" },
{ new string[] { "*; q=0.0" }, "utf-16", new string[] { "utf-8", "utf-16"}, "utf-16" },
};
}
}
public static TheoryData<ICollection<MediaTypeFormatterMatch>, MediaTypeFormatterMatch> SelectResponseMediaTypeData
{
get
{
MediaTypeFormatterMatch matchAccept10 = CreateMatch(1.0, MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral);
MediaTypeFormatterMatch matchAccept05 = CreateMatch(0.5, MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderLiteral);
MediaTypeFormatterMatch matchAcceptSubTypeRange10 = CreateMatch(1.0, MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderSubtypeMediaRange);
MediaTypeFormatterMatch matchAcceptSubTypeRange05 = CreateMatch(0.5, MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderSubtypeMediaRange);
MediaTypeFormatterMatch matchAcceptAllRange10 = CreateMatch(1.0, MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderAllMediaRange);
MediaTypeFormatterMatch matchAcceptAllRange05 = CreateMatch(0.5, MediaTypeFormatterMatchRanking.MatchOnRequestAcceptHeaderAllMediaRange);
MediaTypeFormatterMatch matchRequest10 = CreateMatch(1.0, MediaTypeFormatterMatchRanking.MatchOnRequestMediaType);
MediaTypeFormatterMatch matchType10 = CreateMatch(1.0, MediaTypeFormatterMatchRanking.MatchOnCanWriteType);
// ICollection<MediaTypeFormatterMatch> candidateMatches, MediaTypeFormatterMatch winner
return new TheoryData<ICollection<MediaTypeFormatterMatch>, MediaTypeFormatterMatch>
{
{ new List<MediaTypeFormatterMatch>(), null },
{ new List<MediaTypeFormatterMatch>() { matchType10 }, matchType10 },
{ new List<MediaTypeFormatterMatch>() { matchType10, matchRequest10 }, matchRequest10 },
{ new List<MediaTypeFormatterMatch>() { matchType10, matchRequest10, matchAcceptAllRange10 }, matchAcceptAllRange10 },
{ new List<MediaTypeFormatterMatch>() { matchType10, matchRequest10, matchAcceptAllRange10, matchAcceptSubTypeRange10 }, matchAcceptSubTypeRange10 },
{ new List<MediaTypeFormatterMatch>() { matchType10, matchRequest10, matchAcceptAllRange10, matchAcceptSubTypeRange10, matchAccept10 }, matchAccept10 },
{ new List<MediaTypeFormatterMatch>() { matchAccept05, matchAccept10 }, matchAccept10 },
{ new List<MediaTypeFormatterMatch>() { matchAccept10, matchAccept05 }, matchAccept10 },
{ new List<MediaTypeFormatterMatch>() { matchAcceptSubTypeRange05, matchAcceptSubTypeRange10 }, matchAcceptSubTypeRange10 },
{ new List<MediaTypeFormatterMatch>() { matchAcceptSubTypeRange10, matchAcceptSubTypeRange05 }, matchAcceptSubTypeRange10 },
{ new List<MediaTypeFormatterMatch>() { matchAcceptAllRange05, matchAcceptAllRange10 }, matchAcceptAllRange10 },
{ new List<MediaTypeFormatterMatch>() { matchAcceptAllRange10, matchAcceptAllRange05 }, matchAcceptAllRange10 },
};
}
}
public static TheoryData<MediaTypeFormatterMatch, MediaTypeFormatterMatch, bool> UpdateBestMatchData
{
get
{
MediaTypeFormatterMatch matchMapping10 = CreateMatch(1.0, MediaTypeFormatterMatchRanking.None);
MediaTypeFormatterMatch matchMapping05 = CreateMatch(0.5, MediaTypeFormatterMatchRanking.None);
// MediaTypeFormatterMatch current, MediaTypeFormatterMatch potentialReplacement, currentWins
return new TheoryData<MediaTypeFormatterMatch, MediaTypeFormatterMatch, bool>
{
{ null, matchMapping10, false },
{ null, matchMapping05, false },
{ matchMapping10, matchMapping10, true },
{ matchMapping10, matchMapping05, true },
{ matchMapping05, matchMapping10, false },
{ matchMapping05, matchMapping05, true },
};
}
}
private static MediaTypeFormatterMatch CreateMatch(double? quality, MediaTypeFormatterMatchRanking ranking)
{
MockMediaTypeFormatter formatter = new MockMediaTypeFormatter();
MediaTypeHeaderValue mediaType = new MediaTypeHeaderValue("text/test");
return new MediaTypeFormatterMatch(formatter, mediaType, quality, ranking);
}
[Fact]
public void TypeIsCorrect()
{
new TypeAssert().HasProperties(typeof(DefaultContentNegotiator), TypeAssert.TypeProperties.IsPublicVisibleClass);
}
[Fact]
public void Negotiate_ForEmptyFormatterCollection_ReturnsNull()
{
var result = _negotiator.Negotiate(typeof(string), _request, Enumerable.Empty<MediaTypeFormatter>());
Assert.Null(result);
}
[Fact]
public void Negotiate_ForRequestReturnsFirstMatchingFormatter()
{
MediaTypeHeaderValue mediaType = new MediaTypeHeaderValue("application/myMediaType");
MediaTypeFormatter formatter1 = new MockMediaTypeFormatter()
{
CanWriteTypeCallback = (Type t) => false
};
MediaTypeFormatter formatter2 = new MockMediaTypeFormatter()
{
CanWriteTypeCallback = (Type t) => true
};
formatter2.SupportedMediaTypes.Add(mediaType);
MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection(
new MediaTypeFormatter[]
{
formatter1,
formatter2
});
_request.Content = new StringContent("test", Encoding.UTF8, mediaType.MediaType);
var result = _negotiator.Negotiate(typeof(string), _request, collection);
Assert.Same(formatter2, result.Formatter);
new MediaTypeAssert().AreEqual(mediaType, result.MediaType, "Expected the formatter's media type to be returned.");
}
[Fact]
public void Negotiate_SelectsJsonAsDefaultFormatter()
{
// Arrange
_request.Content = new StringContent("test");
// Act
var result = _negotiator.Negotiate(typeof(string), _request, new MediaTypeFormatterCollection());
// Assert
Assert.IsType<JsonMediaTypeFormatter>(result.Formatter);
Assert.Equal(MediaTypeConstants.ApplicationJsonMediaType.MediaType, result.MediaType.MediaType);
}
[Fact]
public void Negotiate_SelectsXmlFormatter_ForXhrRequestThatAcceptsXml()
{
// Arrange
_request.Content = new StringContent("test");
_request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
_request.Headers.Add("x-requested-with", "XMLHttpRequest");
// Act
var result = _negotiator.Negotiate(typeof(string), _request, new MediaTypeFormatterCollection());
// Assert
Assert.Equal("application/xml", result.MediaType.MediaType);
Assert.IsType<XmlMediaTypeFormatter>(result.Formatter);
}
[Fact]
public void Negotiate_SelectsJsonFormatter_ForXhrRequestThatDoesNotSpecifyAcceptHeaders()
{
// Arrange
_request.Content = new StringContent("test");
_request.Headers.Add("x-requested-with", "XMLHttpRequest");
// Act
var result = _negotiator.Negotiate(typeof(string), _request, new MediaTypeFormatterCollection());
// Assert
Assert.Equal("application/json", result.MediaType.MediaType);
Assert.IsType<JsonMediaTypeFormatter>(result.Formatter);
}
[Fact]
public void Negotiate_SelectsJsonFormatter_ForXHRAndJsonValueResponse()
{
// Arrange
_request.Content = new StringContent("test");
_request.Headers.Add("x-requested-with", "XMLHttpRequest");
// Act
// Mono issue - https://github.com/aspnet/External/issues/27
var type = TestPlatformHelper.IsMono ? typeof(string) : typeof(JToken);
var result = _negotiator.Negotiate(type, _request, new MediaTypeFormatterCollection());
Assert.Equal("application/json", result.MediaType.MediaType);
Assert.IsType<JsonMediaTypeFormatter>(result.Formatter);
}
[Fact]
public void Negotiate_SelectsJsonFormatter_ForXHRAndMatchAllAcceptHeader()
{
// Accept
_request.Content = new StringContent("test");
_request.Headers.Add("x-requested-with", "XMLHttpRequest");
_request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
// Act
var result = _negotiator.Negotiate(typeof(string), _request, new MediaTypeFormatterCollection());
// Assert
Assert.Equal("application/json", result.MediaType.MediaType);
Assert.IsType<JsonMediaTypeFormatter>(result.Formatter);
}
[Fact]
public void Negotiate_UsesRequestedFormatterForXHRAndMatchAllPlusOtherAcceptHeader()
{
// Arrange
_request.Content = new StringContent("test");
_request.Headers.Add("x-requested-with", "XMLHttpRequest");
_request.Headers.Accept.ParseAdd("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); // XHR header sent by Firefox 3b5
// Act
var result = _negotiator.Negotiate(typeof(string), _request, new MediaTypeFormatterCollection());
// Assert
Assert.Equal("application/xml", result.MediaType.MediaType);
Assert.IsType<XmlMediaTypeFormatter>(result.Formatter);
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void Negotiate_ObservesExcludeMatchOnTypeOnly(bool excludeMatchOnTypeOnly)
{
// Arrange
MockContentNegotiator negotiator = new MockContentNegotiator(excludeMatchOnTypeOnly);
_request.Content = new StringContent("test");
_request.Headers.Accept.ParseAdd("text/html");
// Act
var result = negotiator.Negotiate(typeof(string), _request, new MediaTypeFormatterCollection());
// Assert
if (excludeMatchOnTypeOnly)
{
Assert.Null(result);
}
else
{
Assert.NotNull(result);
Assert.Equal("application/json", result.MediaType.MediaType);
}
}
[Theory]
[MemberData(nameof(MatchAcceptHeaderData))]
public void MatchAcceptHeader_ReturnsMatch(string[] acceptHeaders, string[] supportedMediaTypes, string expectedMediaType, double expectedQuality, int ranking)
{
// Arrange
MockContentNegotiator negotiator = new MockContentNegotiator();
List<MediaTypeWithQualityHeaderValue> unsortedAcceptHeaders = acceptHeaders.Select(a => MediaTypeWithQualityHeaderValue.Parse(a)).ToList();
IEnumerable<MediaTypeWithQualityHeaderValue> sortedAcceptHeaders = negotiator.SortMediaTypeWithQualityHeaderValuesByQFactor(unsortedAcceptHeaders);
MockMediaTypeFormatter formatter = new MockMediaTypeFormatter();
foreach (string supportedMediaType in supportedMediaTypes)
{
formatter.SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse(supportedMediaType));
}
// Act
MediaTypeFormatterMatch match = negotiator.MatchAcceptHeader(sortedAcceptHeaders, formatter);
// Assert
if (expectedMediaType == null)
{
Assert.Null(match);
}
else
{
Assert.Same(formatter, match.Formatter);
Assert.Equal(MediaTypeHeaderValue.Parse(expectedMediaType), match.MediaType);
Assert.Equal(expectedQuality, match.Quality);
Assert.Equal(ranking, (int)match.Ranking);
}
}
[Theory]
[MemberData(nameof(MatchRequestMediaTypeData))]
public void MatchRequestMediaType_ReturnsMatch(string requestMediaType, string[] supportedMediaTypes, string expectedMediaType)
{
// Arrange
MockContentNegotiator negotiator = new MockContentNegotiator();
HttpRequestMessage request = new HttpRequestMessage();
request.Content = new StringContent(String.Empty);
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(requestMediaType);
MockMediaTypeFormatter formatter = new MockMediaTypeFormatter();
foreach (string supportedMediaType in supportedMediaTypes)
{
formatter.SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse(supportedMediaType));
}
// Act
MediaTypeFormatterMatch match = negotiator.MatchRequestMediaType(request, formatter);
// Assert
if (expectedMediaType == null)
{
Assert.Null(match);
}
else
{
Assert.Same(formatter, match.Formatter);
Assert.Equal(MediaTypeHeaderValue.Parse(expectedMediaType), match.MediaType);
Assert.Equal(1.0, match.Quality);
Assert.Equal(MediaTypeFormatterMatchRanking.MatchOnRequestMediaType, match.Ranking);
}
}
[Theory]
[MemberData(nameof(ShouldMatchOnTypeData))]
public void ShouldMatchOnType_ReturnsExpectedResult(bool excludeMatchOnType, string[] acceptHeaders, bool expectedResult)
{
// Arrange
MockContentNegotiator negotiator = new MockContentNegotiator(excludeMatchOnType);
List<MediaTypeWithQualityHeaderValue> unsortedAcceptHeaders = acceptHeaders.Select(a => MediaTypeWithQualityHeaderValue.Parse(a)).ToList();
IEnumerable<MediaTypeWithQualityHeaderValue> sortedAcceptHeaders = negotiator.SortMediaTypeWithQualityHeaderValuesByQFactor(unsortedAcceptHeaders);
// Act
bool result = negotiator.ShouldMatchOnType(sortedAcceptHeaders);
// Assert
Assert.Equal(expectedResult, result);
}
[Theory]
[MemberData(nameof(MatchTypeData))]
public void MatchType_ReturnsMatch(string[] supportedMediaTypes, string expectedMediaType)
{
// Arrange
MockContentNegotiator negotiator = new MockContentNegotiator();
MockMediaTypeFormatter formatter = new MockMediaTypeFormatter();
foreach (string supportedMediaType in supportedMediaTypes)
{
formatter.SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse(supportedMediaType));
}
// Act
MediaTypeFormatterMatch match = negotiator.MatchType(typeof(object), formatter);
// Assert
Assert.Same(formatter, match.Formatter);
Assert.Equal(MediaTypeHeaderValue.Parse(expectedMediaType), match.MediaType);
Assert.Equal(1.0, match.Quality);
Assert.Equal(MediaTypeFormatterMatchRanking.MatchOnCanWriteType, match.Ranking);
}
[Theory]
[MemberData(nameof(SelectResponseMediaTypeData))]
public void SelectResponseMediaTypeFormatter_SelectsMediaType(ICollection<MediaTypeFormatterMatch> matches, MediaTypeFormatterMatch expectedWinner)
{
// Arrange
MockContentNegotiator negotiator = new MockContentNegotiator();
// Act
MediaTypeFormatterMatch actualWinner = negotiator.SelectResponseMediaTypeFormatter(matches);
// Assert
Assert.Same(expectedWinner, actualWinner);
}
[Theory]
[MemberData(nameof(SelectResponseCharacterEncodingData))]
public void SelectResponseCharacterEncoding_SelectsEncoding(string[] acceptCharsetHeaders, string requestEncoding, string[] supportedEncodings, string expectedEncoding)
{
// Arrange
MockContentNegotiator negotiator = new MockContentNegotiator();
HttpRequestMessage request = new HttpRequestMessage();
foreach (string acceptCharsetHeader in acceptCharsetHeaders)
{
request.Headers.AcceptCharset.Add(StringWithQualityHeaderValue.Parse(acceptCharsetHeader));
}
if (requestEncoding != null)
{
Encoding reqEncoding = Encoding.GetEncoding(requestEncoding);
StringContent content = new StringContent("", reqEncoding, "text/plain");
request.Content = content;
}
MockMediaTypeFormatter formatter = new MockMediaTypeFormatter() { CallBase = true };
foreach (string supportedEncoding in supportedEncodings)
{
formatter.SupportedEncodings.Add(Encoding.GetEncoding(supportedEncoding));
}
// Act
Encoding actualEncoding = negotiator.SelectResponseCharacterEncoding(request, formatter);
// Assert
if (expectedEncoding == null)
{
Assert.Null(actualEncoding);
}
else
{
Assert.Equal(Encoding.GetEncoding(expectedEncoding), actualEncoding);
}
}
[Theory]
[TestDataSet(typeof(DefaultContentNegotiatorTests), nameof(MediaTypeWithQualityHeaderValueComparerTestsBeforeAfterSortedValues))]
public void SortMediaTypeWithQualityHeaderValuesByQFactor_SortsCorrectly(IEnumerable<string> unsorted, IEnumerable<string> expectedSorted)
{
// Arrange
MockContentNegotiator negotiator = new MockContentNegotiator();
List<MediaTypeWithQualityHeaderValue> unsortedValues =
new List<MediaTypeWithQualityHeaderValue>(unsorted.Select(u => MediaTypeWithQualityHeaderValue.Parse(u)));
List<MediaTypeWithQualityHeaderValue> expectedSortedValues =
new List<MediaTypeWithQualityHeaderValue>(expectedSorted.Select(u => MediaTypeWithQualityHeaderValue.Parse(u)));
// Act
IEnumerable<MediaTypeWithQualityHeaderValue> actualSorted = negotiator.SortMediaTypeWithQualityHeaderValuesByQFactor(unsortedValues);
// Assert
Assert.True(expectedSortedValues.SequenceEqual(actualSorted));
}
public static TheoryData<string[], string[]> MediaTypeWithQualityHeaderValueComparerTestsBeforeAfterSortedValues
{
get
{
return new TheoryData<string[], string[]>
{
{
new string[]
{
"application/*",
"text/plain",
"text/plain;q=1.0",
"text/plain",
"text/plain;q=0",
"*/*;q=0.8",
"*/*;q=1",
"text/*;q=1",
"text/plain;q=0.8",
"text/*;q=0.8",
"text/*;q=0.6",
"text/*;q=1.0",
"*/*;q=0.4",
"text/plain;q=0.6",
"text/xml",
},
new string[]
{
"text/plain",
"text/plain;q=1.0",
"text/plain",
"text/xml",
"application/*",
"text/*;q=1",
"text/*;q=1.0",
"*/*;q=1",
"text/plain;q=0.8",
"text/*;q=0.8",
"*/*;q=0.8",
"text/plain;q=0.6",
"text/*;q=0.6",
"*/*;q=0.4",
"text/plain;q=0",
}
}
};
}
}
[Theory]
[TestDataSet(typeof(DefaultContentNegotiatorTests), nameof(StringWithQualityHeaderValueComparerTestsBeforeAfterSortedValues))]
public void SortStringWithQualityHeaderValuesByQFactor_SortsCorrectly(IEnumerable<string> unsorted, IEnumerable<string> expectedSorted)
{
// Arrange
MockContentNegotiator negotiator = new MockContentNegotiator();
List<StringWithQualityHeaderValue> unsortedValues =
new List<StringWithQualityHeaderValue>(unsorted.Select(u => StringWithQualityHeaderValue.Parse(u)));
List<StringWithQualityHeaderValue> expectedSortedValues =
new List<StringWithQualityHeaderValue>(expectedSorted.Select(u => StringWithQualityHeaderValue.Parse(u)));
// Act
IEnumerable<StringWithQualityHeaderValue> actualSorted = negotiator.SortStringWithQualityHeaderValuesByQFactor(unsortedValues);
// Assert
Assert.True(expectedSortedValues.SequenceEqual(actualSorted));
}
public static TheoryData<string[], string[]> StringWithQualityHeaderValueComparerTestsBeforeAfterSortedValues
{
get
{
return new TheoryData<string[], string[]>
{
{
new string[]
{
"text",
"text;q=1.0",
"text",
"text;q=0",
"*;q=0.8",
"*;q=1",
"text;q=0.8",
"*;q=0.6",
"text;q=1.0",
"*;q=0.4",
"text;q=0.6",
},
new string[]
{
"text",
"text;q=1.0",
"text",
"text;q=1.0",
"*;q=1",
"text;q=0.8",
"*;q=0.8",
"text;q=0.6",
"*;q=0.6",
"*;q=0.4",
"text;q=0",
}
}
};
}
}
[Theory]
[MemberData(nameof(UpdateBestMatchData))]
public void UpdateBestMatch_SelectsCorrectly(MediaTypeFormatterMatch current, MediaTypeFormatterMatch replacement, bool currentWins)
{
// Arrange
MockContentNegotiator negotiator = new MockContentNegotiator();
// Act
MediaTypeFormatterMatch actualResult = negotiator.UpdateBestMatch(current, replacement);
// Assert
if (currentWins)
{
Assert.Same(current, actualResult);
}
else
{
Assert.Same(replacement, actualResult);
}
}
private class PlainTextFormatter : MediaTypeFormatter
{
public override bool CanReadType(Type type)
{
return true;
}
public override bool CanWriteType(Type type)
{
return true;
}
}
}
}

View File

@ -1,85 +0,0 @@
// 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.Buffers;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
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(httpContext, new RouteData(), new ActionDescriptor());
var result = new ExceptionResult(new Exception("hello, world!"), includeErrorDetail: false);
// Act
await result.ExecuteResultAsync(context);
// Assert
Assert.Equal(StatusCodes.Status500InternalServerError, 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(httpContext, new RouteData(), 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 static IServiceProvider CreateServices()
{
var options = Options.Create(new MvcOptions());
options.Value.OutputFormatters.Add(new StringOutputFormatter());
options.Value.OutputFormatters.Add(new JsonOutputFormatter(
new JsonSerializerSettings(),
ArrayPool<char>.Shared));
var services = new ServiceCollection();
services.AddSingleton<IActionResultExecutor<ObjectResult>>(new ObjectResultExecutor(
new DefaultOutputFormatterSelector(options, NullLoggerFactory.Instance),
new TestHttpResponseStreamWriterFactory(),
NullLoggerFactory.Instance));
return services.BuildServiceProvider();
}
}
}

View File

@ -1,44 +0,0 @@
// 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.Linq;
using System.Net.Http.Formatting;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
public class FormDataCollectionExtensionsTest
{
[Theory]
[InlineData("", null)]
[InlineData("", "")] // empty
[InlineData("x", "x")] // normal key
[InlineData("", "[]")] // trim []
[InlineData("x", "x[]")] // trim []
[InlineData("x[234]", "x[234]")] // array index
[InlineData("x.y", "x[y]")] // field lookup
[InlineData("x.y.z", "x[y][z]")] // nested field lookup
[InlineData("x.y[234].x", "x[y][234][x]")] // compound
public void TestNormalize(string expectedMvc, string jqueryString)
{
Assert.Equal(expectedMvc, FormDataCollectionExtensions.NormalizeJQueryToMvc(jqueryString));
}
[Fact]
public void TestGetJQueryNameValuePairs()
{
// Arrange
var formData = new FormDataCollection("x.y=30&x[y]=70&x[z][20]=cool");
// Act
var actual = FormDataCollectionExtensions.GetJQueryNameValuePairs(formData).ToArray();
// Assert
var arraySetter = Assert.Single(actual, kvp => kvp.Key == "x.z[20]");
Assert.Equal("cool", arraySetter.Value);
Assert.Single(actual, kvp => kvp.Key == "x.y" && kvp.Value == "30");
Assert.Single(actual, kvp => kvp.Key == "x.y" && kvp.Value == "70");
}
}
}

View File

@ -1,304 +0,0 @@
// 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.Collections.Generic;
using System.IO;
using System.Net.Http.Formatting;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Testing.xunit;
using Newtonsoft.Json.Linq;
using Xunit;
namespace System.Web.Http.Dispatcher
{
public class HttpErrorTest
{
public static IEnumerable<object[]> ErrorKeyValue
{
get
{
var httpError = new HttpError();
yield return new object[] { httpError, (Func<string>)(() => httpError.Message), "Message", "Message_Value" };
yield return new object[] { httpError, (Func<string>)(() => httpError.MessageDetail), "MessageDetail", "MessageDetail_Value" };
yield return new object[] { httpError, (Func<string>)(() => httpError.ExceptionMessage), "ExceptionMessage", "ExceptionMessage_Value" };
yield return new object[] { httpError, (Func<string>)(() => httpError.ExceptionType), "ExceptionType", "ExceptionType_Value" };
yield return new object[] { httpError, (Func<string>)(() => httpError.StackTrace), "StackTrace", "StackTrace_Value" };
}
}
public static IEnumerable<object[]> HttpErrors
{
get
{
yield return new[] { new HttpError() };
yield return new[] { new HttpError("error") };
yield return new[] { new HttpError(new NotImplementedException(), true) };
var modelState = new ModelStateDictionary();
modelState.AddModelError("key", "error");
yield return new[] { new HttpError(modelState, true) };
}
}
[Fact]
public void StringConstructor_AddsCorrectDictionaryItems()
{
HttpError error = new HttpError("something bad happened");
Assert.Contains(new KeyValuePair<string, object>("Message", "something bad happened"), error);
}
[Fact]
public void ExceptionConstructorWithDetail_AddsCorrectDictionaryItems()
{
HttpError error = new HttpError(new ArgumentException("error", new Exception()), true);
Assert.Contains(new KeyValuePair<string, object>("Message", "An error has occurred."), error);
Assert.Contains(new KeyValuePair<string, object>("ExceptionMessage", "error"), error);
Assert.Contains(new KeyValuePair<string, object>("ExceptionType", "System.ArgumentException"), error);
Assert.True(error.ContainsKey("StackTrace"));
Assert.True(error.ContainsKey("InnerException"));
Assert.IsType<HttpError>(error["InnerException"]);
}
[Fact]
public void ModelStateConstructorWithDetail_AddsCorrectDictionaryItems()
{
// Arrange
ModelStateDictionary modelState = new ModelStateDictionary();
var provider = new EmptyModelMetadataProvider();
var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length));
modelState.AddModelError("[0].Name", "error1");
modelState.AddModelError("[0].Name", "error2");
modelState.AddModelError("[0].Address", "error");
modelState.AddModelError("[2].Name", new Exception("OH NO"), metadata);
// Act
HttpError error = new HttpError(modelState, true);
// Assert
HttpError modelStateError = error["ModelState"] as HttpError;
Assert.Contains(new KeyValuePair<string, object>("Message", "The request is invalid."), error);
Assert.Contains("error1", modelStateError["[0].Name"] as IEnumerable<string>);
Assert.Contains("error2", modelStateError["[0].Name"] as IEnumerable<string>);
Assert.Contains("error", modelStateError["[0].Address"] as IEnumerable<string>);
Assert.True(modelStateError.ContainsKey("[2].Name"));
Assert.Contains("OH NO", modelStateError["[2].Name"] as IEnumerable<string>);
}
[Fact]
public void ExceptionConstructorWithoutDetail_AddsCorrectDictionaryItems()
{
HttpError error = new HttpError(new ArgumentException("error", new Exception()), false);
Assert.Contains(new KeyValuePair<string, object>("Message", "An error has occurred."), error);
Assert.False(error.ContainsKey("ExceptionMessage"));
Assert.False(error.ContainsKey("ExceptionType"));
Assert.False(error.ContainsKey("StackTrace"));
Assert.False(error.ContainsKey("InnerException"));
}
[Fact]
public void ModelStateConstructorWithoutDetail_AddsCorrectDictionaryItems()
{
// Arrange
ModelStateDictionary modelState = new ModelStateDictionary();
var provider = new EmptyModelMetadataProvider();
var metadata = provider.GetMetadataForProperty(typeof(string), nameof(string.Length));
modelState.AddModelError("[0].Name", "error1");
modelState.AddModelError("[0].Name", "error2");
modelState.AddModelError("[0].Address", "error");
modelState.AddModelError("[2].Name", new Exception("OH NO"), metadata);
// Act
HttpError error = new HttpError(modelState, false);
// Assert
HttpError modelStateError = error["ModelState"] as HttpError;
Assert.Contains(new KeyValuePair<string, object>("Message", "The request is invalid."), error);
Assert.Contains("error1", modelStateError["[0].Name"] as IEnumerable<string>);
Assert.Contains("error2", modelStateError["[0].Name"] as IEnumerable<string>);
Assert.Contains("error", modelStateError["[0].Address"] as IEnumerable<string>);
Assert.True(modelStateError.ContainsKey("[2].Name"));
Assert.DoesNotContain("OH NO", modelStateError["[2].Name"] as IEnumerable<string>);
}
[Fact]
public void HttpError_Roundtrips_WithJsonFormatter()
{
HttpError error = new HttpError("error") { { "ErrorCode", 42 }, { "Data", new[] { "a", "b", "c" } } };
MediaTypeFormatter formatter = new JsonMediaTypeFormatter();
MemoryStream stream = new MemoryStream();
formatter.WriteToStreamAsync(typeof(HttpError), error, stream, content: null, transportContext: null).Wait();
stream.Position = 0;
HttpError roundtrippedError = formatter.ReadFromStreamAsync(typeof(HttpError), stream, content: null, formatterLogger: null).Result as HttpError;
Assert.NotNull(roundtrippedError);
Assert.Equal("error", roundtrippedError.Message);
Assert.Equal(42L, roundtrippedError["ErrorCode"]);
JArray data = roundtrippedError["Data"] as JArray;
Assert.Equal(3, data.Count);
Assert.Contains("a", data);
Assert.Contains("b", data);
Assert.Contains("c", data);
}
[ConditionalFact]
// Mono issue - https://github.com/aspnet/External/issues/25
[FrameworkSkipCondition(RuntimeFrameworks.Mono)]
public void HttpError_Roundtrips_WithXmlFormatter()
{
HttpError error = new HttpError("error") { { "ErrorCode", 42 }, { "Data", new[] { "a", "b", "c" } } };
MediaTypeFormatter formatter = new XmlMediaTypeFormatter();
MemoryStream stream = new MemoryStream();
formatter.WriteToStreamAsync(typeof(HttpError), error, stream, content: null, transportContext: null).Wait();
stream.Position = 0;
HttpError roundtrippedError = formatter.ReadFromStreamAsync(typeof(HttpError), stream, content: null, formatterLogger: null).Result as HttpError;
Assert.NotNull(roundtrippedError);
Assert.Equal("error", roundtrippedError.Message);
Assert.Equal("42", roundtrippedError["ErrorCode"]);
Assert.Equal("a b c", roundtrippedError["Data"]);
}
[Fact]
public void HttpErrorWithWhitespace_Roundtrips_WithXmlFormatter()
{
string message = " foo\n bar \n ";
HttpError error = new HttpError(message);
MediaTypeFormatter formatter = new XmlMediaTypeFormatter();
MemoryStream stream = new MemoryStream();
formatter.WriteToStreamAsync(typeof(HttpError), error, stream, content: null, transportContext: null).Wait();
stream.Position = 0;
HttpError roundtrippedError = formatter.ReadFromStreamAsync(typeof(HttpError), stream, content: null, formatterLogger: null).Result as HttpError;
Assert.NotNull(roundtrippedError);
Assert.Equal(message, roundtrippedError.Message);
}
[Fact]
public void HttpError_Roundtrips_WithXmlSerializer()
{
HttpError error = new HttpError("error") { { "ErrorCode", 42 }, { "Data", new[] { "a", "b", "c" } } };
MediaTypeFormatter formatter = new XmlMediaTypeFormatter() { UseXmlSerializer = true };
MemoryStream stream = new MemoryStream();
formatter.WriteToStreamAsync(typeof(HttpError), error, stream, content: null, transportContext: null).Wait();
stream.Position = 0;
HttpError roundtrippedError = formatter.ReadFromStreamAsync(typeof(HttpError), stream, content: null, formatterLogger: null).Result as HttpError;
Assert.NotNull(roundtrippedError);
Assert.Equal("error", roundtrippedError.Message);
Assert.Equal("42", roundtrippedError["ErrorCode"]);
Assert.Equal("a b c", roundtrippedError["Data"]);
}
[Fact]
public void HttpErrorForInnerException_Serializes_WithXmlSerializer()
{
HttpError error = new HttpError(new ArgumentException("error", new Exception("innerError")), includeErrorDetail: true);
MediaTypeFormatter formatter = new XmlMediaTypeFormatter() { UseXmlSerializer = true };
MemoryStream stream = new MemoryStream();
formatter.WriteToStreamAsync(typeof(HttpError), error, stream, content: null, transportContext: null).Wait();
stream.Position = 0;
string serializedError = new StreamReader(stream).ReadToEnd();
Assert.NotNull(serializedError);
Assert.Equal(
"<Error><Message>An error has occurred.</Message><ExceptionMessage>error</ExceptionMessage><ExceptionType>System.ArgumentException</ExceptionType><StackTrace /><InnerException><Message>An error has occurred.</Message><ExceptionMessage>innerError</ExceptionMessage><ExceptionType>System.Exception</ExceptionType><StackTrace /></InnerException></Error>",
serializedError);
}
[Fact]
public void GetPropertyValue_GetsValue_IfTypeMatches()
{
HttpError error = new HttpError();
error["key"] = "x";
Assert.Equal("x", error.GetPropertyValue<string>("key"));
Assert.Equal("x", error.GetPropertyValue<object>("key"));
}
[Fact]
public void GetPropertyValue_GetsDefault_IfTypeDoesNotMatch()
{
HttpError error = new HttpError();
error["key"] = "x";
Assert.Null(error.GetPropertyValue<Uri>("key"));
Assert.Equal(0, error.GetPropertyValue<int>("key"));
}
[Fact]
public void GetPropertyValue_GetsDefault_IfPropertyMissing()
{
HttpError error = new HttpError();
Assert.Null(error.GetPropertyValue<string>("key"));
Assert.Equal(0, error.GetPropertyValue<int>("key"));
}
[Theory]
[MemberData(nameof(ErrorKeyValue))]
public void HttpErrorStringProperties_UseCorrectHttpErrorKey(HttpError httpError, Func<string> productUnderTest, string key, string actualValue)
{
// Arrange
httpError[key] = actualValue;
// Act
string expectedValue = productUnderTest.Invoke();
// Assert
Assert.Equal(expectedValue, actualValue);
}
[Fact]
public void HttpErrorProperty_InnerException_UsesCorrectHttpErrorKey()
{
// Arrange
HttpError error = new HttpError(new ArgumentException("error", new Exception()), true);
// Act
HttpError innerException = error.InnerException;
// Assert
Assert.Same(error["InnerException"], innerException);
}
[Fact]
public void HttpErrorProperty_ModelState_UsesCorrectHttpErrorKey()
{
// Arrange
ModelStateDictionary modelState = new ModelStateDictionary();
modelState.AddModelError("[0].Name", "error1");
HttpError error = new HttpError(modelState, true);
// Act
HttpError actualModelStateError = error.ModelState;
// Assert
Assert.Same(error["ModelState"], actualModelStateError);
}
[Theory]
[MemberData(nameof(HttpErrors))]
public void HttpErrors_UseCaseInsensitiveComparer(HttpError httpError)
{
// Arrange
var lowercaseKey = "abcd";
var uppercaseKey = "ABCD";
httpError[lowercaseKey] = "error";
// Act & Assert
Assert.True(httpError.ContainsKey(lowercaseKey));
Assert.True(httpError.ContainsKey(uppercaseKey));
}
}
}

View File

@ -1,326 +0,0 @@
// 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.Collections.Generic;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.WebApiCompatShim;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;
namespace System.Net.Http
{
public class HttpRequestMessageExtensionsTest
{
[Fact]
public void CreateResponse_MatchingMediaType_WhenMediaTypeStringIsInvalidFormat_Throws()
{
HttpRequestMessage request = CreateRequest(new DefaultHttpContext());
var ex = Assert.Throws<FormatException>(
() => request.CreateResponse(HttpStatusCode.OK, CreateValue(), "foo/bar; param=value"));
Assert.Contains("foo/bar; param=value", ex.Message);
}
[Fact]
public void CreateResponse_MatchingMediaType_WhenRequestDoesNotHaveHttpContextThrows()
{
HttpRequestMessage request = CreateRequest(null);
// Arrange
// Act
var ex = Assert.Throws<InvalidOperationException>(
() => request.CreateResponse(HttpStatusCode.OK, CreateValue(), mediaType: "foo/bar"));
Assert.Equal(
"The HttpRequestMessage instance is not properly initialized. " +
"Use HttpRequestMessageHttpContextExtensions.GetHttpRequestMessage to create an HttpRequestMessage " +
"for the current request.",
ex.Message);
}
[Fact]
public void CreateResponse_DoingConneg_OnlyContent_RetrievesContentNegotiatorFromServices()
{
// Arrange
var context = new DefaultHttpContext();
var services = new Mock<IServiceProvider>();
services
.Setup(s => s.GetService(typeof(IContentNegotiator)))
.Returns(Mock.Of<IContentNegotiator>())
.Verifiable();
var options = new WebApiCompatShimOptions();
options.Formatters.AddRange(new MediaTypeFormatterCollection());
var optionsAccessor = new Mock<IOptions<WebApiCompatShimOptions>>();
optionsAccessor.SetupGet(o => o.Value).Returns(options);
services
.Setup(s => s.GetService(typeof(IOptions<WebApiCompatShimOptions>)))
.Returns(optionsAccessor.Object);
context.RequestServices = services.Object;
var request = CreateRequest(context);
// Act
request.CreateResponse(CreateValue());
// Assert
services.Verify();
}
[Fact]
public void CreateResponse_DoingConneg_RetrievesContentNegotiatorFromServices()
{
// Arrange
var context = new DefaultHttpContext();
var services = new Mock<IServiceProvider>();
services
.Setup(s => s.GetService(typeof(IContentNegotiator)))
.Returns(Mock.Of<IContentNegotiator>())
.Verifiable();
var options = new WebApiCompatShimOptions();
options.Formatters.AddRange(new MediaTypeFormatterCollection());
var optionsAccessor = new Mock<IOptions<WebApiCompatShimOptions>>();
optionsAccessor.SetupGet(o => o.Value).Returns(options);
services
.Setup(s => s.GetService(typeof(IOptions<WebApiCompatShimOptions>)))
.Returns(optionsAccessor.Object);
context.RequestServices = services.Object;
var request = CreateRequest(context);
// Act
request.CreateResponse(HttpStatusCode.OK, CreateValue());
// Assert
services.Verify();
}
[Fact]
public void CreateResponse_DoingConneg_PerformsContentNegotiationAndCreatesContentUsingResults()
{
// Arrange
var context = new DefaultHttpContext();
var formatter = new XmlMediaTypeFormatter();
var contentNegotiator = new Mock<IContentNegotiator>();
contentNegotiator
.Setup(c => c.Negotiate(It.IsAny<Type>(), It.IsAny<HttpRequestMessage>(), It.IsAny<IEnumerable<MediaTypeFormatter>>()))
.Returns(new ContentNegotiationResult(formatter, mediaType: null));
context.RequestServices = CreateServices(contentNegotiator.Object, formatter);
var request = CreateRequest(context);
// Act
var response = request.CreateResponse<string>(HttpStatusCode.NoContent, "42");
// Assert
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
Assert.Same(request, response.RequestMessage);
var objectContent = Assert.IsType<ObjectContent<string>>(response.Content);
Assert.Equal("42", objectContent.Value);
Assert.Same(formatter, objectContent.Formatter);
}
[Fact]
public void CreateResponse_MatchingMediaType_WhenMediaTypeDoesNotMatch_Throws()
{
// Arrange
var context = new DefaultHttpContext();
context.RequestServices = CreateServices(new DefaultContentNegotiator());
var request = CreateRequest(context);
// Act
var ex = Assert.Throws<InvalidOperationException>(
() => request.CreateResponse(HttpStatusCode.OK, CreateValue(), mediaType: "foo/bar"));
Assert.Equal(
"Could not find a formatter matching the media type 'foo/bar' that can write an instance of 'System.Object'.",
ex.Message);
}
[Fact]
public void CreateResponse_MatchingMediaType_FindsMatchingFormatterAndCreatesResponse()
{
// Arrange
var context = new DefaultHttpContext();
var formatter = new Mock<MediaTypeFormatter> { CallBase = true };
formatter.Setup(f => f.CanWriteType(typeof(object))).Returns(true).Verifiable();
formatter.Object.SupportedMediaTypes.Add(new MediaTypeHeaderValue("foo/bar"));
context.RequestServices = CreateServices(new DefaultContentNegotiator(), formatter.Object);
var expectedValue = CreateValue();
var request = CreateRequest(context);
// Act
var response = request.CreateResponse(HttpStatusCode.Gone, expectedValue, mediaType: "foo/bar");
// Assert
Assert.Equal(HttpStatusCode.Gone, response.StatusCode);
var content = Assert.IsType<ObjectContent<object>>(response.Content);
Assert.Same(expectedValue, content.Value);
Assert.Same(formatter.Object, content.Formatter);
Assert.Equal("foo/bar", content.Headers.ContentType.MediaType);
formatter.Verify();
}
[Fact]
public void CreateResponse_AcceptingFormatter_CreatesResponseWithDefaultMediaType()
{
// Arrange
var context = new DefaultHttpContext();
var formatter = new Mock<MediaTypeFormatter>() { CallBase = true };
formatter
.Setup(f => f.CanWriteType(typeof(object)))
.Returns(true)
.Verifiable();
formatter
.Setup(f => f.SetDefaultContentHeaders(typeof(object), It.IsAny<HttpContentHeaders>(), It.IsAny<MediaTypeHeaderValue>()))
.Callback<Type, HttpContentHeaders, MediaTypeHeaderValue>(SetMediaType)
.Verifiable();
formatter.Object.SupportedMediaTypes.Add(new MediaTypeHeaderValue("foo/bar"));
var expectedValue = CreateValue();
var request = CreateRequest(context);
// Act
var response = request.CreateResponse(
HttpStatusCode.MultipleChoices,
expectedValue,
formatter.Object,
mediaType: (string)null);
// Assert
Assert.Equal(HttpStatusCode.MultipleChoices, response.StatusCode);
var content = Assert.IsType<ObjectContent<object>>(response.Content);
Assert.Same(expectedValue, content.Value);
Assert.Same(formatter.Object, content.Formatter);
Assert.Equal("foo/bar", content.Headers.ContentType.MediaType);
formatter.Verify();
}
private static void SetMediaType(Type type, HttpContentHeaders headers, MediaTypeHeaderValue value)
{
headers.ContentType = new MediaTypeHeaderValue("foo/bar");
}
[Fact]
public void CreateResponse_AcceptingFormatter_WithOverridenMediaTypeString_CreatesResponse()
{
// Arrange
var context = new DefaultHttpContext();
var formatter = new Mock<MediaTypeFormatter> { CallBase = true };
formatter.Setup(f => f.CanWriteType(typeof(object))).Returns(true).Verifiable();
formatter.Object.SupportedMediaTypes.Add(new MediaTypeHeaderValue("foo/bar"));
var expectedValue = CreateValue();
var request = CreateRequest(context);
// Act
var response = request.CreateResponse(
HttpStatusCode.MultipleChoices,
CreateValue(),
formatter.Object,
mediaType: "bin/baz");
// Assert
Assert.Equal("bin/baz", response.Content.Headers.ContentType.MediaType);
}
[Fact]
public void CreateResponse_AcceptingFormatter_WithOverridenMediaTypeHeader_CreatesResponse()
{
// Arrange
var context = new DefaultHttpContext();
var formatter = new Mock<MediaTypeFormatter> { CallBase = true };
formatter.Setup(f => f.CanWriteType(typeof(object))).Returns(true).Verifiable();
formatter.Object.SupportedMediaTypes.Add(new MediaTypeHeaderValue("foo/bar"));
var expectedValue = CreateValue();
var request = CreateRequest(context);
// Act
var response = request.CreateResponse(
HttpStatusCode.MultipleChoices,
CreateValue(),
formatter.Object,
mediaType: new MediaTypeHeaderValue("bin/baz"));
// Assert
Assert.Equal("bin/baz", response.Content.Headers.ContentType.MediaType);
}
private static IServiceProvider CreateServices(
IContentNegotiator contentNegotiator = null,
MediaTypeFormatter formatter = null)
{
var options = new WebApiCompatShimOptions();
if (formatter == null)
{
options.Formatters.AddRange(new MediaTypeFormatterCollection());
}
else
{
options.Formatters.Add(formatter);
}
var optionsAccessor = new Mock<IOptions<WebApiCompatShimOptions>>();
optionsAccessor.SetupGet(o => o.Value).Returns(options);
var services = new Mock<IServiceProvider>(MockBehavior.Strict);
services
.Setup(s => s.GetService(typeof(IOptions<WebApiCompatShimOptions>)))
.Returns(optionsAccessor.Object);
if (contentNegotiator != null)
{
services
.Setup(s => s.GetService(typeof(IContentNegotiator)))
.Returns(contentNegotiator);
}
return services.Object;
}
private static object CreateValue()
{
return new object();
}
private static HttpRequestMessage CreateRequest(HttpContext context)
{
var request = new HttpRequestMessage();
request.Properties.Add(nameof(HttpContext), context);
return request;
}
}
}

View File

@ -1,136 +0,0 @@
// 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.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Testing.xunit;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
public class HttpRequestMessageFeatureTest
{
[Fact]
public void HttpRequestMessage_CombinesUri()
{
// Arrange
var context = new DefaultHttpContext();
var feature = new HttpRequestMessageFeature(context);
context.Request.Method = "GET";
context.Request.Scheme = "http";
context.Request.Host = new HostString("contoso.com");
context.Request.PathBase = new PathString("/app");
context.Request.Path = new PathString("/api/Products");
context.Request.QueryString = new QueryString("?orderId=3");
// Act
var request = feature.HttpRequestMessage;
// Assert
Assert.Equal("http://contoso.com/app/api/Products?orderId=3", request.RequestUri.AbsoluteUri);
}
[Fact]
public void HttpRequestMessage_CopiesRequestMethod()
{
// Arrange
var context = new DefaultHttpContext();
var feature = new HttpRequestMessageFeature(context);
context.Request.Method = "OPTIONS";
// Act
var request = feature.HttpRequestMessage;
// Assert
Assert.Equal(new HttpMethod("OPTIONS"), request.Method);
}
[ConditionalFact]
// Mono issue - https://github.com/aspnet/External/issues/24
[FrameworkSkipCondition(RuntimeFrameworks.Mono)]
public void HttpRequestMessage_CopiesHeader()
{
// Arrange
var context = new DefaultHttpContext();
var feature = new HttpRequestMessageFeature(context);
context.Request.Method = "OPTIONS";
context.Request.Headers.Add("Host", new string[] { "contoso.com" });
// Act
var request = feature.HttpRequestMessage;
// Assert
Assert.Equal("contoso.com", request.Headers.Host);
}
[Fact]
public void HttpRequestMessage_CopiesContentHeader()
{
// Arrange
var context = new DefaultHttpContext();
var feature = new HttpRequestMessageFeature(context);
context.Request.Method = "OPTIONS";
context.Request.Headers.Add("Content-Type", new string[] { "text/plain" });
// Act
var request = feature.HttpRequestMessage;
// Assert
Assert.Equal("text/plain", request.Content.Headers.ContentType.ToString());
}
[Fact]
public async Task HttpRequestMessage_WrapsBodyContent()
{
// Arrange
var context = new DefaultHttpContext();
var feature = new HttpRequestMessageFeature(context);
context.Request.Method = "OPTIONS";
var bytes = Encoding.UTF8.GetBytes("Hello, world!");
context.Request.Body = new MemoryStream(bytes);
context.Request.Body.Seek(0, SeekOrigin.Begin);
// Act
var request = feature.HttpRequestMessage;
// Assert
var streamContent = Assert.IsType<StreamContent>(request.Content);
var content = await request.Content.ReadAsStringAsync();
Assert.Equal("Hello, world!", content);
}
[Fact]
public void HttpRequestMessage_CachesMessage()
{
// Arrange
var context = new DefaultHttpContext();
var feature = new HttpRequestMessageFeature(context);
context.Request.Method = "GET";
context.Request.Scheme = "http";
context.Request.Host = new HostString("contoso.com");
// Act
var request1 = feature.HttpRequestMessage;
context.Request.Path = new PathString("/api/Products");
var request2 = feature.HttpRequestMessage;
// Assert
Assert.Same(request1, request2);
Assert.Equal("/", request2.RequestUri.AbsolutePath);
}
}
}

View File

@ -1,57 +0,0 @@
// 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.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
public class HttpRequestMessageModelBinderTest
{
[Fact]
public async Task BindModelAsync_BindsHttpRequestMessage()
{
// Arrange
var binder = new HttpRequestMessageModelBinder();
var bindingContext = GetBindingContext(typeof(HttpRequestMessage));
var expectedModel = bindingContext.HttpContext.GetHttpRequestMessage();
// Act
await binder.BindModelAsync(bindingContext);
// Assert
var result = bindingContext.Result;
Assert.True(result.IsModelSet);
Assert.Same(expectedModel, result.Model);
var entry = bindingContext.ValidationState[result.Model];
Assert.True(entry.SuppressValidation);
Assert.Null(entry.Key);
Assert.Null(entry.Metadata);
}
private static DefaultModelBindingContext GetBindingContext(Type modelType)
{
var metadataProvider = new EmptyModelMetadataProvider();
DefaultModelBindingContext bindingContext = new DefaultModelBindingContext
{
ActionContext = new ActionContext()
{
HttpContext = new DefaultHttpContext(),
},
ModelMetadata = metadataProvider.GetMetadataForType(modelType),
ModelName = "someName",
ValidationState = new ValidationStateDictionary(),
};
bindingContext.HttpContext.Request.Method = "GET";
return bindingContext;
}
}
}

View File

@ -1,87 +0,0 @@
// 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.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Routing;
using Moq;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
public class HttpResponseExceptionActionFilterTest
{
[Fact]
public void OrderIsSetToMaxValue()
{
// Arrange
var filter = new HttpResponseExceptionActionFilter();
var expectedFilterOrder = int.MaxValue - 10;
// Act & Assert
Assert.Equal(expectedFilterOrder, filter.Order);
}
[Fact]
public void OnActionExecuting_IsNoOp()
{
// Arrange
var filter = new HttpResponseExceptionActionFilter();
var actionContext = new ActionContext(
new DefaultHttpContext(),
new RouteData(),
Mock.Of<ActionDescriptor>());
var context = new ActionExecutingContext(
actionContext,
filters: new List<IFilterMetadata>(),
actionArguments: new Dictionary<string, object>(),
controller: new object());
// Act
filter.OnActionExecuting(context);
// Assert
Assert.Null(context.Result);
}
[Fact]
public void OnActionExecuted_HandlesExceptionAndReturnsObjectResult()
{
// Arrange
var filter = new HttpResponseExceptionActionFilter();
var httpContext = new DefaultHttpContext();
httpContext.Request.Method = "GET";
var actionContext = new ActionContext(
httpContext,
new RouteData(),
Mock.Of<ActionDescriptor>());
var context = new ActionExecutedContext(
actionContext,
filters: new List<IFilterMetadata>(),
controller: new object());
context.Exception = new HttpResponseException(HttpStatusCode.BadRequest);
// Act
filter.OnActionExecuted(context);
// Assert
Assert.True(context.ExceptionHandled);
var result = Assert.IsType<ObjectResult>(context.Result);
Assert.Equal(typeof(HttpResponseMessage), result.DeclaredType);
var response = Assert.IsType<HttpResponseMessage>(result.Value);
Assert.NotNull(response.RequestMessage);
Assert.Equal(context.HttpContext.GetHttpRequestMessage(), response.RequestMessage);
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
}
}

View File

@ -1,45 +0,0 @@
// 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.Net;
using System.Net.Http;
using System.Web.Http;
using Microsoft.AspNetCore.Testing;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
public class HttpResponseExceptionTest
{
[Fact]
[ReplaceCulture]
public void Constructor_SetsResponseProperty()
{
// Arrange and Act
var response = new HttpResponseMessage();
var exception = new HttpResponseException(response);
// Assert
Assert.Same(response, exception.Response);
Assert.Equal("Processing of the HTTP request resulted in an exception." +
" Please see the HTTP response returned by the 'Response' " +
"property of this exception for details.",
exception.Message);
}
[Fact]
[ReplaceCulture]
public void Constructor_SetsResponsePropertyWithGivenStatusCode()
{
// Arrange and Act
var exception = new HttpResponseException(HttpStatusCode.BadGateway);
// Assert
Assert.Equal(HttpStatusCode.BadGateway, exception.Response.StatusCode);
Assert.Equal("Processing of the HTTP request resulted in an exception." +
" Please see the HTTP response returned by the 'Response' " +
"property of this exception for details.",
exception.Message);
}
}
}

View File

@ -1,158 +0,0 @@
// 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.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.AspNetCore.Mvc.WebApiCompatShim;
using Microsoft.AspNetCore.Testing.xunit;
using Moq;
using Moq.Protected;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShimTest
{
public class HttpResponseMessageOutputFormatterTests
{
[Fact]
public async Task Disposed_CalledOn_HttpResponseMessage()
{
// Arrange
var formatter = new HttpResponseMessageOutputFormatter();
var streamContent = new Mock<StreamContent>(new MemoryStream());
streamContent.Protected().Setup("Dispose", true).Verifiable();
var httpResponseMessage = new HttpResponseMessage();
httpResponseMessage.Content = streamContent.Object;
var outputFormatterContext = GetOutputFormatterContext(
httpResponseMessage,
typeof(HttpResponseMessage),
new DefaultHttpContext());
// Act
await formatter.WriteAsync(outputFormatterContext);
// Assert
streamContent.Protected().Verify("Dispose", Times.Once(), true);
}
[ConditionalFact]
// Issue - https://github.com/aspnet/External/issues/20
[FrameworkSkipCondition(RuntimeFrameworks.Mono)]
public async Task ExplicitlySet_ChunkedEncodingFlag_IsIgnored()
{
// Arrange
var httpResponseMessage = new HttpResponseMessage();
httpResponseMessage.Content = new StreamContent(new MemoryStream(Encoding.UTF8.GetBytes("Hello, World")));
httpResponseMessage.Headers.TransferEncodingChunked = true;
var httpContext = new DefaultHttpContext();
var formatter = new HttpResponseMessageOutputFormatter();
var outputFormatterContext = GetOutputFormatterContext(
httpResponseMessage,
typeof(HttpResponseMessage),
httpContext);
// Act
await formatter.WriteAsync(outputFormatterContext);
// Assert
Assert.False(httpContext.Response.Headers.ContainsKey("Transfer-Encoding"));
Assert.NotNull(httpContext.Response.ContentLength);
}
[ConditionalFact]
// Issue - https://github.com/aspnet/External/issues/20
[FrameworkSkipCondition(RuntimeFrameworks.Mono)]
public async Task ExplicitlySet_ChunkedEncodingHeader_IsIgnored()
{
// Arrange
var transferEncodingHeaderKey = "Transfer-Encoding";
var httpResponseMessage = new HttpResponseMessage();
httpResponseMessage.Content = new StreamContent(new MemoryStream(Encoding.UTF8.GetBytes("Hello, World")));
httpResponseMessage.Headers.Add(transferEncodingHeaderKey, "chunked");
var httpContext = new DefaultHttpContext();
var formatter = new HttpResponseMessageOutputFormatter();
var outputFormatterContext = GetOutputFormatterContext(
httpResponseMessage,
typeof(HttpResponseMessage),
httpContext);
// Act
await formatter.WriteAsync(outputFormatterContext);
// Assert
Assert.False(httpContext.Response.Headers.ContainsKey(transferEncodingHeaderKey));
Assert.NotNull(httpContext.Response.ContentLength);
}
[ConditionalFact]
// Issue - https://github.com/aspnet/External/issues/20
[FrameworkSkipCondition(RuntimeFrameworks.Mono)]
public async Task ExplicitlySet_MultipleEncodings_ChunkedNotIgnored()
{
// Arrange
var transferEncodingHeaderKey = "Transfer-Encoding";
var httpResponseMessage = new HttpResponseMessage();
httpResponseMessage.Content = new StreamContent(new MemoryStream(Encoding.UTF8.GetBytes("Hello, World")));
httpResponseMessage.Headers.Add(transferEncodingHeaderKey, new[] { "identity", "chunked" });
var httpContext = new DefaultHttpContext();
var formatter = new HttpResponseMessageOutputFormatter();
var outputFormatterContext = GetOutputFormatterContext(
httpResponseMessage,
typeof(HttpResponseMessage),
httpContext);
// Act
await formatter.WriteAsync(outputFormatterContext);
// Assert
Assert.True(httpContext.Response.Headers.ContainsKey(transferEncodingHeaderKey));
Assert.Equal(new string[] { "identity", "chunked" },
httpContext.Response.Headers[transferEncodingHeaderKey]);
Assert.NotNull(httpContext.Response.ContentLength);
}
[ConditionalFact]
// Issue - https://github.com/aspnet/External/issues/20
[FrameworkSkipCondition(RuntimeFrameworks.Mono)]
public async Task ExplicitlySet_MultipleEncodingsUsingChunkedFlag_ChunkedNotIgnored()
{
// Arrange
var transferEncodingHeaderKey = "Transfer-Encoding";
var httpResponseMessage = new HttpResponseMessage();
httpResponseMessage.Content = new StreamContent(new MemoryStream(Encoding.UTF8.GetBytes("Hello, World")));
httpResponseMessage.Headers.Add(transferEncodingHeaderKey, new[] { "identity" });
httpResponseMessage.Headers.TransferEncodingChunked = true;
var httpContext = new DefaultHttpContext();
var formatter = new HttpResponseMessageOutputFormatter();
var outputFormatterContext = GetOutputFormatterContext(
httpResponseMessage,
typeof(HttpResponseMessage),
httpContext);
// Act
await formatter.WriteAsync(outputFormatterContext);
// Assert
Assert.True(httpContext.Response.Headers.ContainsKey(transferEncodingHeaderKey));
Assert.Equal(new string[] { "identity", "chunked" },
httpContext.Response.Headers[transferEncodingHeaderKey]);
Assert.NotNull(httpContext.Response.ContentLength);
}
private OutputFormatterWriteContext GetOutputFormatterContext(
object outputValue,
Type outputType,
HttpContext httpContext)
{
return new OutputFormatterWriteContext(
httpContext,
new TestHttpResponseStreamWriterFactory().CreateWriter,
outputType,
outputValue);
}
}
}

View File

@ -1,51 +0,0 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;
namespace System.Web.Http
{
public class InternalServerErrorResultTest
{
[Fact]
public async Task InternalServerErrorResult_SetsStatusCode()
{
// Arrange
var context = new ActionContext(GetHttpContext(), new RouteData(), new ActionDescriptor());
var result = new InternalServerErrorResult();
// Act
await result.ExecuteResultAsync(context);
// Assert
Assert.Equal(StatusCodes.Status500InternalServerError, context.HttpContext.Response.StatusCode);
}
private static IServiceCollection CreateServices()
{
var services = new ServiceCollection();
services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance);
return services;
}
private static HttpContext GetHttpContext()
{
var services = CreateServices();
var httpContext = new DefaultHttpContext();
httpContext.RequestServices = services.BuildServiceProvider();
return httpContext;
}
}
}

View File

@ -1,98 +0,0 @@
// 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.Buffers;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Xunit;
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(httpContext, new RouteData(), 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(StatusCodes.Status400BadRequest, 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(httpContext, new RouteData(), 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 static IServiceProvider CreateServices()
{
var options = Options.Create(new MvcOptions());
options.Value.OutputFormatters.Add(new StringOutputFormatter());
options.Value.OutputFormatters.Add(new JsonOutputFormatter(
new JsonSerializerSettings(),
ArrayPool<char>.Shared));
var services = new ServiceCollection();
services.AddSingleton<IActionResultExecutor<ObjectResult>>(new ObjectResultExecutor(
new DefaultOutputFormatterSelector(options, NullLoggerFactory.Instance),
new TestHttpResponseStreamWriterFactory(),
NullLoggerFactory.Instance));
return services.BuildServiceProvider();
}
}
}

View File

@ -1,18 +0,0 @@
<Project Sdk="Internal.AspNetCore.Sdk">
<PropertyGroup>
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Core.TestCommon\Microsoft.AspNetCore.Mvc.Core.TestCommon.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Mvc.WebApiCompatShim\Microsoft.AspNetCore.Mvc.WebApiCompatShim.csproj" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="$(MicrosoftAspNetCoreHttpPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
</ItemGroup>
</Project>

View File

@ -1,72 +0,0 @@
// 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.Collections.Generic;
using System.Collections.ObjectModel;
using System.Net.Http.Headers;
using System.Text;
namespace System.Net.Http.Formatting.Mocks
{
public class MockContentNegotiator : DefaultContentNegotiator
{
public MockContentNegotiator()
{
}
public MockContentNegotiator(bool excludeMatchOnTypeOnly)
: base(excludeMatchOnTypeOnly)
{
}
public new Collection<MediaTypeFormatterMatch> ComputeFormatterMatches(Type type, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters)
{
return base.ComputeFormatterMatches(type, request, formatters);
}
public new MediaTypeFormatterMatch SelectResponseMediaTypeFormatter(ICollection<MediaTypeFormatterMatch> matches)
{
return base.SelectResponseMediaTypeFormatter(matches);
}
public new Encoding SelectResponseCharacterEncoding(HttpRequestMessage request, MediaTypeFormatter formatter)
{
return base.SelectResponseCharacterEncoding(request, formatter);
}
public new MediaTypeFormatterMatch MatchAcceptHeader(IEnumerable<MediaTypeWithQualityHeaderValue> sortedAcceptValues, MediaTypeFormatter formatter)
{
return base.MatchAcceptHeader(sortedAcceptValues, formatter);
}
public new MediaTypeFormatterMatch MatchRequestMediaType(HttpRequestMessage request, MediaTypeFormatter formatter)
{
return base.MatchRequestMediaType(request, formatter);
}
public new bool ShouldMatchOnType(IEnumerable<MediaTypeWithQualityHeaderValue> sortedAcceptValues)
{
return base.ShouldMatchOnType(sortedAcceptValues);
}
public new MediaTypeFormatterMatch MatchType(Type type, MediaTypeFormatter formatter)
{
return base.MatchType(type, formatter);
}
public new IEnumerable<MediaTypeWithQualityHeaderValue> SortMediaTypeWithQualityHeaderValuesByQFactor(ICollection<MediaTypeWithQualityHeaderValue> headerValues)
{
return base.SortMediaTypeWithQualityHeaderValuesByQFactor(headerValues);
}
public new IEnumerable<StringWithQualityHeaderValue> SortStringWithQualityHeaderValuesByQFactor(ICollection<StringWithQualityHeaderValue> headerValues)
{
return base.SortStringWithQualityHeaderValuesByQFactor(headerValues);
}
public new MediaTypeFormatterMatch UpdateBestMatch(MediaTypeFormatterMatch current, MediaTypeFormatterMatch potentialReplacement)
{
return base.UpdateBestMatch(current, potentialReplacement);
}
}
}

View File

@ -1,47 +0,0 @@
// 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.Net.Http.Headers;
using System.Text;
namespace System.Net.Http.Formatting.Mocks
{
public class MockMediaTypeFormatter : MediaTypeFormatter
{
private bool _canWriteAnyTypes = true;
public bool CallBase { get; set; }
public Func<Type, bool> CanReadTypeCallback { get; set; }
public Func<Type, bool> CanWriteTypeCallback { get; set; }
public bool CanWriteAnyTypesReturn
{
get { return _canWriteAnyTypes; }
set { _canWriteAnyTypes = value; }
}
public override bool CanReadType(Type type)
{
if (!CallBase && CanReadTypeCallback == null)
{
throw new InvalidOperationException("CallBase or CanReadTypeCallback must be set first.");
}
return CanReadTypeCallback != null ? CanReadTypeCallback(type) : true;
}
public override bool CanWriteType(Type type)
{
if (!CallBase && CanWriteTypeCallback == null)
{
throw new InvalidOperationException("CallBase or CanWriteTypeCallback must be set first.");
}
return CanWriteTypeCallback != null ? CanWriteTypeCallback(type) : true;
}
public new Encoding SelectCharacterEncoding(HttpContentHeaders contentHeaders)
{
return base.SelectCharacterEncoding(contentHeaders);
}
}
}

View File

@ -1,93 +0,0 @@
// 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.Buffers;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
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(httpContext, new RouteData(), new ActionDescriptor());
var result = new NegotiatedContentResult<Product>(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(httpContext, new RouteData(), new ActionDescriptor());
var result = new NegotiatedContentResult<Product>(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 static IServiceProvider CreateServices()
{
var options = Options.Create(new MvcOptions());
options.Value.OutputFormatters.Add(new StringOutputFormatter());
options.Value.OutputFormatters.Add(new JsonOutputFormatter(
new JsonSerializerSettings(),
ArrayPool<char>.Shared));
var services = new ServiceCollection();
services.AddSingleton<IActionResultExecutor<ObjectResult>>(new ObjectResultExecutor(
new DefaultOutputFormatterSelector(options, NullLoggerFactory.Instance),
new TestHttpResponseStreamWriterFactory(),
NullLoggerFactory.Instance));
return services.BuildServiceProvider();
}
private class Product
{
public int Id { get; set; }
public string Name { get; set; }
};
}
}

View File

@ -1,624 +0,0 @@
// 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.Collections.Generic;
using System.Web.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Routing;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
{
public class OverloadActionConstraintTest
{
[Fact]
public void Accept_RejectsActionMatchWithMissingParameter()
{
// Arrange
var action = new ActionDescriptor();
action.Parameters = new List<ParameterDescriptor>()
{
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "id",
ParameterType = typeof(int),
},
};
var constraint = new OverloadActionConstraint();
var context = new ActionConstraintContext();
context.Candidates = new List<ActionSelectorCandidate>()
{
new ActionSelectorCandidate(action, new [] { constraint }),
};
context.CurrentCandidate = context.Candidates[0];
context.RouteContext = CreateRouteContext();
// Act & Assert
Assert.False(constraint.Accept(context));
}
[Fact]
public void Accept_AcceptsActionWithSatisfiedParameters()
{
// Arrange
var action = new ActionDescriptor();
action.Parameters = new List<ParameterDescriptor>()
{
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "id",
ParameterType = typeof(int),
},
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "quantity",
ParameterType = typeof(int),
},
};
var constraint = new OverloadActionConstraint();
var context = new ActionConstraintContext();
context.Candidates = new List<ActionSelectorCandidate>()
{
new ActionSelectorCandidate(action, new [] { constraint }),
};
context.CurrentCandidate = context.Candidates[0];
context.RouteContext = CreateRouteContext("?quantity=5", new { id = 17 });
// Act & Assert
Assert.True(constraint.Accept(context));
}
[Fact]
public void Accept_AcceptsActionWithSatisfiedParameters_QueryStringOnly()
{
// Arrange
var action = new ActionDescriptor();
action.Parameters = new List<ParameterDescriptor>()
{
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "id",
ParameterType = typeof(int),
},
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "quantity",
ParameterType = typeof(int),
},
};
var constraint = new OverloadActionConstraint();
var context = new ActionConstraintContext();
context.Candidates = new List<ActionSelectorCandidate>()
{
new ActionSelectorCandidate(action, new [] { constraint }),
};
context.CurrentCandidate = context.Candidates[0];
context.RouteContext = CreateRouteContext("?quantity=5&id=7", new { });
// Act & Assert
Assert.True(constraint.Accept(context));
}
[Fact]
public void Accept_AcceptsActionWithSatisfiedParameters_RouteDataOnly()
{
// Arrange
var action = new ActionDescriptor();
action.Parameters = new List<ParameterDescriptor>()
{
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "id",
ParameterType = typeof(int),
},
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "quantity",
ParameterType = typeof(int),
},
};
var constraint = new OverloadActionConstraint();
var context = new ActionConstraintContext();
context.Candidates = new List<ActionSelectorCandidate>()
{
new ActionSelectorCandidate(action, new [] { constraint }),
};
context.CurrentCandidate = context.Candidates[0];
context.RouteContext = CreateRouteContext("?", new { quantity = 9, id = 17 });
// Act & Assert
Assert.True(constraint.Accept(context));
}
[Fact]
public void Accept_AcceptsActionWithUnsatisfiedOptionalParameter()
{
// Arrange
var action = new ActionDescriptor();
action.Parameters = new List<ParameterDescriptor>()
{
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "id",
ParameterType = typeof(int),
},
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "quantity",
ParameterType = typeof(int),
},
};
var optionalParameters = new HashSet<string>();
optionalParameters.Add("quantity");
action.Properties.Add("OptionalParameters", optionalParameters);
var constraint = new OverloadActionConstraint();
var context = new ActionConstraintContext();
context.Candidates = new List<ActionSelectorCandidate>()
{
new ActionSelectorCandidate(action, new [] { constraint }),
};
context.CurrentCandidate = context.Candidates[0];
context.RouteContext = CreateRouteContext("?store=5", new { id = 17 });
// Act & Assert
Assert.True(constraint.Accept(context));
}
[Fact]
public void Accept_AcceptsOneAndRejectsAnother()
{
// Arrange
var action1 = new ActionDescriptor();
action1.Parameters = new List<ParameterDescriptor>()
{
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "id",
ParameterType = typeof(int),
},
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "quantity",
ParameterType = typeof(int),
},
};
var action2 = new ActionDescriptor();
action2.Parameters = new List<ParameterDescriptor>()
{
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "id",
ParameterType = typeof(int),
},
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "quantity_ordered",
ParameterType = typeof(int),
},
};
var constraint = new OverloadActionConstraint();
var context = new ActionConstraintContext();
context.Candidates = new List<ActionSelectorCandidate>()
{
new ActionSelectorCandidate(action1, new [] { constraint }),
new ActionSelectorCandidate(action2, new [] { constraint }),
};
context.CurrentCandidate = context.Candidates[0];
context.RouteContext = CreateRouteContext("?quantity=5", new { id = 17 });
// Act & Assert
Assert.True(constraint.Accept(context));
context.CurrentCandidate = context.Candidates[1];
Assert.False(constraint.Accept(context));
}
[Fact]
public void Accept_RejectsWorseMatch()
{
// Arrange
var action1 = new ActionDescriptor();
action1.Parameters = new List<ParameterDescriptor>()
{
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "id",
ParameterType = typeof(int),
},
};
var action2 = new ActionDescriptor();
action2.Parameters = new List<ParameterDescriptor>()
{
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "id",
ParameterType = typeof(int),
},
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "quantity",
ParameterType = typeof(int),
},
};
var constraint = new OverloadActionConstraint();
var context = new ActionConstraintContext();
context.Candidates = new List<ActionSelectorCandidate>()
{
new ActionSelectorCandidate(action1, new [] { constraint }),
new ActionSelectorCandidate(action2, new [] { constraint }),
};
context.CurrentCandidate = context.Candidates[0];
context.RouteContext = CreateRouteContext("?quantity=5", new { id = 17 });
// Act & Assert
Assert.False(constraint.Accept(context));
}
[Fact]
public void Accept_RejectsWorseMatch_OptionalParameter()
{
// Arrange
var action1 = new ActionDescriptor();
action1.Parameters = new List<ParameterDescriptor>()
{
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "id",
ParameterType = typeof(int),
},
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "quantity",
ParameterType = typeof(int),
},
};
var optionalParameters = new HashSet<string>();
optionalParameters.Add("quantity");
action1.Properties.Add("OptionalParameters", optionalParameters);
var action2 = new ActionDescriptor();
action2.Parameters = new List<ParameterDescriptor>()
{
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "id",
ParameterType = typeof(int),
},
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "quantity",
ParameterType = typeof(int),
},
};
var constraint = new OverloadActionConstraint();
var context = new ActionConstraintContext();
context.Candidates = new List<ActionSelectorCandidate>()
{
new ActionSelectorCandidate(action1, new [] { constraint }),
new ActionSelectorCandidate(action2, new [] { constraint }),
};
context.CurrentCandidate = context.Candidates[0];
context.RouteContext = CreateRouteContext("?quantity=5", new { id = 17 });
// Act & Assert
Assert.False(constraint.Accept(context));
}
[Fact]
public void Accept_AcceptsActionsOnSameTier()
{
// Arrange
var action1 = new ActionDescriptor();
action1.Parameters = new List<ParameterDescriptor>()
{
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "id",
ParameterType = typeof(int),
},
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "quantity",
ParameterType = typeof(int),
},
};
var action2 = new ActionDescriptor();
action2.Parameters = new List<ParameterDescriptor>()
{
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "id",
ParameterType = typeof(int),
},
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "price",
ParameterType = typeof(decimal),
},
};
var constraint = new OverloadActionConstraint();
var context = new ActionConstraintContext();
context.Candidates = new List<ActionSelectorCandidate>()
{
new ActionSelectorCandidate(action1, new [] { constraint }),
new ActionSelectorCandidate(action2, new [] { constraint }),
};
context.CurrentCandidate = context.Candidates[0];
context.RouteContext = CreateRouteContext("?quantity=5&price=5.99", new { id = 17 });
// Act & Assert
Assert.True(constraint.Accept(context));
context.CurrentCandidate = context.Candidates[1];
Assert.True(constraint.Accept(context));
}
[Fact]
public void Accept_AcceptsAction_WithFewerParameters_WhenOtherIsNotOverloaded()
{
// Arrange
var action1 = new ActionDescriptor();
action1.Parameters = new List<ParameterDescriptor>()
{
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "id",
ParameterType = typeof(int),
},
};
var action2 = new ActionDescriptor();
action2.Parameters = new List<ParameterDescriptor>()
{
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "id",
ParameterType = typeof(int),
},
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "quantity",
ParameterType = typeof(int),
},
};
var optionalParameters = new HashSet<string>();
optionalParameters.Add("quantity");
action2.Properties.Add("OptionalParameters", optionalParameters);
var constraint = new OverloadActionConstraint();
var context = new ActionConstraintContext();
context.Candidates = new List<ActionSelectorCandidate>()
{
new ActionSelectorCandidate(action1, new [] { constraint }),
new ActionSelectorCandidate(action2, new IActionConstraint[] { }),
};
context.CurrentCandidate = context.Candidates[0];
context.RouteContext = CreateRouteContext("?quantity=5", new { id = 17 });
// Act & Assert
Assert.True(constraint.Accept(context));
}
[Fact]
public void Accept_AcceptsAction_WithFewerParameters_WhenOtherIsNotOverloaded_FromBodyAttribute()
{
// Arrange
var action1 = new ActionDescriptor();
action1.Parameters = new List<ParameterDescriptor>()
{
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "id",
ParameterType = typeof(int),
},
};
var action2 = new ActionDescriptor();
action2.Parameters = new List<ParameterDescriptor>()
{
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromUriAttribute()).BindingSource,
},
Name = "id",
ParameterType = typeof(int),
},
new ParameterDescriptor()
{
BindingInfo = new BindingInfo()
{
BindingSource = (new FromBodyAttribute()).BindingSource,
},
Name = "quantity",
ParameterType = typeof(int),
},
};
var constraint = new OverloadActionConstraint();
var context = new ActionConstraintContext();
context.Candidates = new List<ActionSelectorCandidate>()
{
new ActionSelectorCandidate(action1, new [] { constraint }),
new ActionSelectorCandidate(action2, new IActionConstraint[] { }),
};
context.CurrentCandidate = context.Candidates[0];
context.RouteContext = CreateRouteContext("?quantity=5", new { id = 17 });
// Act & Assert
Assert.True(constraint.Accept(context));
}
private static RouteContext CreateRouteContext(string queryString = null, object routeValues = null)
{
var httpContext = new DefaultHttpContext();
if (queryString != null)
{
httpContext.Request.QueryString = new QueryString(queryString);
}
var routeContext = new RouteContext(httpContext);
routeContext.RouteData = new RouteData();
foreach (var kvp in new RouteValueDictionary(routeValues))
{
routeContext.RouteData.Values.Add(kvp.Key, kvp.Value);
}
return routeContext;
}
}
}

View File

@ -1,15 +0,0 @@
// 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;
namespace Microsoft.TestCommon.Types
{
[Flags]
public enum FlagsEnum
{
One = 0x1,
Two = 0x2,
Four = 0x4
}
}

View File

@ -1,13 +0,0 @@
// 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.
namespace Microsoft.TestCommon.Types
{
public enum LongEnum : long
{
FirstLong,
SecondLong,
ThirdLong,
FourthLong
}
}

View File

@ -1,56 +0,0 @@
// 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.Net.Http.Headers;
using Xunit;
namespace Microsoft.TestCommon
{
public class MediaTypeAssert
{
private static readonly MediaTypeAssert singleton = new MediaTypeAssert();
public static MediaTypeAssert Singleton { get { return singleton; } }
public void AreEqual(MediaTypeHeaderValue expected, MediaTypeHeaderValue actual, string errorMessage)
{
if (expected != null || actual != null)
{
Assert.NotNull(expected);
Assert.Equal(0, new MediaTypeHeaderValueComparer().Compare(expected, actual));
}
}
public void AreEqual(MediaTypeHeaderValue expected, string actual, string errorMessage)
{
if (expected != null || !String.IsNullOrEmpty(actual))
{
MediaTypeHeaderValue actualMediaType = new MediaTypeHeaderValue(actual);
Assert.NotNull(expected);
Assert.Equal(0, new MediaTypeHeaderValueComparer().Compare(expected, actualMediaType));
}
}
public void AreEqual(string expected, string actual, string errorMessage)
{
if (!String.IsNullOrEmpty(expected) || !String.IsNullOrEmpty(actual))
{
Assert.NotNull(expected);
MediaTypeHeaderValue expectedMediaType = new MediaTypeHeaderValue(expected);
MediaTypeHeaderValue actualMediaType = new MediaTypeHeaderValue(actual);
Assert.Equal(0, new MediaTypeHeaderValueComparer().Compare(expectedMediaType, actualMediaType));
}
}
public void AreEqual(string expected, MediaTypeHeaderValue actual, string errorMessage)
{
if (!String.IsNullOrEmpty(expected) || actual != null)
{
Assert.NotNull(expected);
MediaTypeHeaderValue expectedMediaType = new MediaTypeHeaderValue(expected);
Assert.Equal(0, new MediaTypeHeaderValueComparer().Compare(expectedMediaType, actual));
}
}
}
}

View File

@ -1,109 +0,0 @@
// 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.Net.Http.Headers;
namespace System.Net.Http.Formatting
{
/// <summary>
/// Constants related to media types.
/// </summary>
internal static class MediaTypeConstants
{
private static readonly MediaTypeHeaderValue _defaultApplicationXmlMediaType =
new MediaTypeHeaderValue("application/xml");
private static readonly MediaTypeHeaderValue _defaultTextXmlMediaType =
new MediaTypeHeaderValue("text/xml");
private static readonly MediaTypeHeaderValue _defaultApplicationJsonMediaType =
new MediaTypeHeaderValue("application/json");
private static readonly MediaTypeHeaderValue _defaultTextJsonMediaType =
new MediaTypeHeaderValue("text/json");
private static readonly MediaTypeHeaderValue _defaultApplicationOctetStreamMediaType =
new MediaTypeHeaderValue("application/octet-stream");
private static readonly MediaTypeHeaderValue _defaultApplicationFormUrlEncodedMediaType =
new MediaTypeHeaderValue("application/x-www-form-urlencoded");
private static readonly MediaTypeHeaderValue _defaultApplicationBsonMediaType =
new MediaTypeHeaderValue("application/bson");
/// <summary>
/// Gets a <see cref="MediaTypeHeaderValue"/> instance representing <c>application/octet-stream</c>.
/// </summary>
/// <value>
/// A new <see cref="MediaTypeHeaderValue"/> instance representing <c>application/octet-stream</c>.
/// </value>
public static MediaTypeHeaderValue ApplicationOctetStreamMediaType
{
get { return _defaultApplicationOctetStreamMediaType; }
}
/// <summary>
/// Gets a <see cref="MediaTypeHeaderValue"/> instance representing <c>application/xml</c>.
/// </summary>
/// <value>
/// A new <see cref="MediaTypeHeaderValue"/> instance representing <c>application/xml</c>.
/// </value>
public static MediaTypeHeaderValue ApplicationXmlMediaType
{
get { return _defaultApplicationXmlMediaType; }
}
/// <summary>
/// Gets a <see cref="MediaTypeHeaderValue"/> instance representing <c>application/json</c>.
/// </summary>
/// <value>
/// A new <see cref="MediaTypeHeaderValue"/> instance representing <c>application/json</c>.
/// </value>
public static MediaTypeHeaderValue ApplicationJsonMediaType
{
get { return _defaultApplicationJsonMediaType; }
}
/// <summary>
/// Gets a <see cref="MediaTypeHeaderValue"/> instance representing <c>text/xml</c>.
/// </summary>
/// <value>
/// A new <see cref="MediaTypeHeaderValue"/> instance representing <c>text/xml</c>.
/// </value>
public static MediaTypeHeaderValue TextXmlMediaType
{
get { return _defaultTextXmlMediaType; }
}
/// <summary>
/// Gets a <see cref="MediaTypeHeaderValue"/> instance representing <c>text/json</c>.
/// </summary>
/// <value>
/// A new <see cref="MediaTypeHeaderValue"/> instance representing <c>text/json</c>.
/// </value>
public static MediaTypeHeaderValue TextJsonMediaType
{
get { return _defaultTextJsonMediaType; }
}
/// <summary>
/// Gets a <see cref="MediaTypeHeaderValue"/> instance representing <c>application/x-www-form-urlencoded</c>.
/// </summary>
/// <value>
/// A new <see cref="MediaTypeHeaderValue"/> instance representing <c>application/x-www-form-urlencoded</c>.
/// </value>
public static MediaTypeHeaderValue ApplicationFormUrlEncodedMediaType
{
get { return _defaultApplicationFormUrlEncodedMediaType; }
}
/// <summary>
/// Gets a <see cref="MediaTypeHeaderValue"/> instance representing <c>application/bson</c>.
/// </summary>
/// <value>
/// A new <see cref="MediaTypeHeaderValue"/> instance representing <c>application/bson</c>.
/// </value>
/// <remarks>
/// Not yet a standard. In particular this media type is not currently listed at
/// https://www.iana.org/assignments/media-types/application.
/// </remarks>
public static MediaTypeHeaderValue ApplicationBsonMediaType
{
get { return _defaultApplicationBsonMediaType; }
}
}
}

View File

@ -1,195 +0,0 @@
// 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.Net.Http.Headers;
namespace Microsoft.TestCommon
{
public class MediaTypeHeaderValueComparer : IComparer<MediaTypeHeaderValue>
{
private static readonly MediaTypeHeaderValueComparer mediaTypeComparer = new MediaTypeHeaderValueComparer();
public MediaTypeHeaderValueComparer()
{
}
public static MediaTypeHeaderValueComparer Comparer
{
get
{
return mediaTypeComparer;
}
}
public int Compare(MediaTypeHeaderValue mediaType1, MediaTypeHeaderValue mediaType2)
{
ParsedMediaTypeHeaderValue parsedMediaType1 = new ParsedMediaTypeHeaderValue(mediaType1);
ParsedMediaTypeHeaderValue parsedMediaType2 = new ParsedMediaTypeHeaderValue(mediaType2);
int returnValue = CompareBasedOnQualityFactor(parsedMediaType1, parsedMediaType2);
if (returnValue == 0)
{
if (!String.Equals(parsedMediaType1.Type, parsedMediaType2.Type, StringComparison.OrdinalIgnoreCase))
{
if (parsedMediaType1.IsAllMediaRange)
{
return 1;
}
else if (parsedMediaType2.IsAllMediaRange)
{
return -1;
}
}
else if (!String.Equals(parsedMediaType1.SubType, parsedMediaType2.SubType, StringComparison.OrdinalIgnoreCase))
{
if (parsedMediaType1.IsSubTypeMediaRange)
{
return 1;
}
else if (parsedMediaType2.IsSubTypeMediaRange)
{
return -1;
}
}
else
{
if (!parsedMediaType1.HasNonQualityFactorParameter)
{
if (parsedMediaType2.HasNonQualityFactorParameter)
{
return 1;
}
}
else if (!parsedMediaType2.HasNonQualityFactorParameter)
{
return -1;
}
}
}
return returnValue;
}
private static int CompareBasedOnQualityFactor(ParsedMediaTypeHeaderValue parsedMediaType1, ParsedMediaTypeHeaderValue parsedMediaType2)
{
double qualityDifference = parsedMediaType1.QualityFactor - parsedMediaType2.QualityFactor;
if (qualityDifference < 0)
{
return 1;
}
else if (qualityDifference > 0)
{
return -1;
}
return 0;
}
internal class ParsedMediaTypeHeaderValue
{
private const string MediaRangeAsterisk = "*";
private const char MediaTypeSubTypeDelimiter = '/';
private const string QualityFactorParameterName = "q";
private const double DefaultQualityFactor = 1.0;
private MediaTypeHeaderValue mediaType;
private string type;
private string subType;
private bool? hasNonQualityFactorParameter;
private double? qualityFactor;
public ParsedMediaTypeHeaderValue(MediaTypeHeaderValue mediaType)
{
this.mediaType = mediaType;
string[] splitMediaType = mediaType.MediaType.Split(MediaTypeSubTypeDelimiter);
this.type = splitMediaType[0];
this.subType = splitMediaType[1];
}
public string Type
{
get
{
return this.type;
}
}
public string SubType
{
get
{
return this.subType;
}
}
public bool IsAllMediaRange
{
get
{
return this.IsSubTypeMediaRange && String.Equals(MediaRangeAsterisk, this.Type, StringComparison.Ordinal);
}
}
public bool IsSubTypeMediaRange
{
get
{
return String.Equals(MediaRangeAsterisk, this.SubType, StringComparison.Ordinal);
}
}
public bool HasNonQualityFactorParameter
{
get
{
if (!this.hasNonQualityFactorParameter.HasValue)
{
this.hasNonQualityFactorParameter = false;
foreach (NameValueHeaderValue param in this.mediaType.Parameters)
{
if (!String.Equals(QualityFactorParameterName, param.Name, StringComparison.Ordinal))
{
this.hasNonQualityFactorParameter = true;
}
}
}
return this.hasNonQualityFactorParameter.Value;
}
}
public string CharSet
{
get
{
return this.mediaType.CharSet;
}
}
public double QualityFactor
{
get
{
if (!this.qualityFactor.HasValue)
{
MediaTypeWithQualityHeaderValue mediaTypeWithQuality = this.mediaType as MediaTypeWithQualityHeaderValue;
if (mediaTypeWithQuality != null)
{
this.qualityFactor = mediaTypeWithQuality.Quality;
}
if (!this.qualityFactor.HasValue)
{
this.qualityFactor = DefaultQualityFactor;
}
}
return this.qualityFactor.Value;
}
}
}
}
}

View File

@ -1,76 +0,0 @@
// 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.Linq;
namespace Microsoft.TestCommon
{
public class RefTypeTestData<T> : TestData<T> where T : class
{
private Func<IEnumerable<T>> testDataProvider;
private Func<IEnumerable<T>> derivedTypeTestDataProvider;
private Func<IEnumerable<T>> knownTypeTestDataProvider;
public RefTypeTestData(Func<IEnumerable<T>> testDataProvider)
{
if (testDataProvider == null)
{
throw new ArgumentNullException(nameof(testDataProvider));
}
this.testDataProvider = testDataProvider;
this.RegisterTestDataVariation(TestDataVariations.WithNull, this.Type, GetNullTestData);
}
public RefTypeTestData(
Func<IEnumerable<T>> testDataProvider,
Func<IEnumerable<T>> derivedTypeTestDataProvider,
Func<IEnumerable<T>> knownTypeTestDataProvider)
: this(testDataProvider)
{
this.derivedTypeTestDataProvider = derivedTypeTestDataProvider;
if (this.derivedTypeTestDataProvider != null)
{
this.RegisterTestDataVariation(TestDataVariations.AsDerivedType, this.Type, this.GetTestDataAsDerivedType);
}
this.knownTypeTestDataProvider = knownTypeTestDataProvider;
if (this.knownTypeTestDataProvider != null)
{
this.RegisterTestDataVariation(TestDataVariations.AsKnownType, this.Type, this.GetTestDataAsDerivedKnownType);
}
}
public T GetNullTestData()
{
return null;
}
public IEnumerable<T> GetTestDataAsDerivedType()
{
if (this.derivedTypeTestDataProvider != null)
{
return this.derivedTypeTestDataProvider();
}
return Enumerable.Empty<T>();
}
public IEnumerable<T> GetTestDataAsDerivedKnownType()
{
if (this.knownTypeTestDataProvider != null)
{
return this.knownTypeTestDataProvider();
}
return Enumerable.Empty<T>();
}
protected override IEnumerable<T> GetTypedTestData()
{
return this.testDataProvider();
}
}
}

View File

@ -1,13 +0,0 @@
// 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.
namespace Microsoft.TestCommon.Types
{
public enum SimpleEnum
{
First,
Second,
Third,
Fourth
}
}

View File

@ -1,441 +0,0 @@
// 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;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Microsoft.TestCommon.Types;
namespace Microsoft.TestCommon
{
/// <summary>
/// A base class for test data. A <see cref="TestData"/> instance is associated with a given type, and the <see cref="TestData"/> instance can
/// provide instances of the given type to use as data in tests. The same <see cref="TestData"/> instance can also provide instances
/// of types related to the given type, such as a <see cref="List<>"/> of the type. See the <see cref="TestDataVariations"/> enum for all the
/// variations of test data that a <see cref="TestData"/> instance can provide.
/// </summary>
public abstract class TestData
{
/// <summary>
/// Common <see cref="TestData"/> for a <see cref="char"/>.
/// </summary>
public static readonly ValueTypeTestData<char> CharTestData = new ValueTypeTestData<char>('a', Char.MinValue, Char.MaxValue);
/// <summary>
/// Common <see cref="TestData"/> for a <see cref="int"/>.
/// </summary>
public static readonly ValueTypeTestData<int> IntTestData = new ValueTypeTestData<int>(-1, 0, 1, Int32.MinValue, Int32.MaxValue);
/// <summary>
/// Common <see cref="TestData"/> for a <see cref="uint"/>.
/// </summary>
public static readonly ValueTypeTestData<uint> UintTestData = new ValueTypeTestData<uint>(0, 1, UInt32.MinValue, UInt32.MaxValue);
/// <summary>
/// Common <see cref="TestData"/> for a <see cref="short"/>.
/// </summary>
public static readonly ValueTypeTestData<short> ShortTestData = new ValueTypeTestData<short>(-1, 0, 1, Int16.MinValue, Int16.MaxValue);
/// <summary>
/// Common <see cref="TestData"/> for a <see cref="ushort"/>.
/// </summary>
public static readonly ValueTypeTestData<ushort> UshortTestData = new ValueTypeTestData<ushort>(0, 1, UInt16.MinValue, UInt16.MaxValue);
/// <summary>
/// Common <see cref="TestData"/> for a <see cref="long"/>.
/// </summary>
public static readonly ValueTypeTestData<long> LongTestData = new ValueTypeTestData<long>(-1, 0, 1, Int64.MinValue, Int64.MaxValue);
/// <summary>
/// Common <see cref="TestData"/> for a <see cref="ulong"/>.
/// </summary>
public static readonly ValueTypeTestData<ulong> UlongTestData = new ValueTypeTestData<ulong>(0, 1, UInt64.MinValue, UInt64.MaxValue);
/// <summary>
/// Common <see cref="TestData"/> for a <see cref="byte"/>.
/// </summary>
public static readonly ValueTypeTestData<byte> ByteTestData = new ValueTypeTestData<byte>(0, 1, Byte.MinValue, Byte.MaxValue);
/// <summary>
/// Common <see cref="TestData"/> for a <see cref="sbyte"/>.
/// </summary>
public static readonly ValueTypeTestData<sbyte> SByteTestData = new ValueTypeTestData<sbyte>(-1, 0, 1, SByte.MinValue, SByte.MaxValue);
/// <summary>
/// Common <see cref="TestData"/> for a <see cref="bool"/>.
/// </summary>
public static readonly ValueTypeTestData<bool> BoolTestData = new ValueTypeTestData<bool>(true, false);
/// <summary>
/// Common <see cref="TestData"/> for a <see cref="double"/>.
/// </summary>
public static readonly ValueTypeTestData<double> DoubleTestData = new ValueTypeTestData<double>(
-1.0,
0.0,
1.0,
double.MinValue,
double.MaxValue,
double.PositiveInfinity,
double.NegativeInfinity);
/// <summary>
/// Common <see cref="TestData"/> for a <see cref="float"/>.
/// </summary>
public static readonly ValueTypeTestData<float> FloatTestData = new ValueTypeTestData<float>(
-1.0f,
0.0f,
1.0f,
float.MinValue,
float.MaxValue,
float.PositiveInfinity,
float.NegativeInfinity);
/// <summary>
/// Common <see cref="TestData"/> for a <see cref="decimal"/>.
/// </summary>
public static readonly ValueTypeTestData<decimal> DecimalTestData = new ValueTypeTestData<decimal>(
-1M,
0M,
1M,
decimal.MinValue,
decimal.MaxValue);
/// <summary>
/// Common <see cref="TestData"/> for a <see cref="DateTime"/>.
/// </summary>
public static readonly ValueTypeTestData<DateTime> DateTimeTestData = new ValueTypeTestData<DateTime>(
DateTime.Now,
DateTime.UtcNow,
DateTime.MaxValue,
DateTime.MinValue);
/// <summary>
/// Common <see cref="TestData"/> for a <see cref="TimeSpan"/>.
/// </summary>
public static readonly ValueTypeTestData<TimeSpan> TimeSpanTestData = new ValueTypeTestData<TimeSpan>(
TimeSpan.MinValue,
TimeSpan.MaxValue);
/// <summary>
/// Common <see cref="TestData"/> for a <see cref="Guid"/>.
/// </summary>
public static readonly ValueTypeTestData<Guid> GuidTestData = new ValueTypeTestData<Guid>(
Guid.NewGuid(),
Guid.Empty);
/// <summary>
/// Common <see cref="TestData"/> for a <see cref="DateTimeOffset"/>.
/// </summary>
public static readonly ValueTypeTestData<DateTimeOffset> DateTimeOffsetTestData = new ValueTypeTestData<DateTimeOffset>(
DateTimeOffset.MaxValue,
DateTimeOffset.MinValue,
new DateTimeOffset(DateTime.Now));
/// <summary>
/// Common <see cref="TestData"/> for an <c>enum</c>.
/// </summary>
public static readonly ValueTypeTestData<SimpleEnum> SimpleEnumTestData = new ValueTypeTestData<SimpleEnum>(
SimpleEnum.First,
SimpleEnum.Second,
SimpleEnum.Third);
/// <summary>
/// Common <see cref="TestData"/> for an <c>enum</c> implemented with a <see cref="long"/>.
/// </summary>
public static readonly ValueTypeTestData<LongEnum> LongEnumTestData = new ValueTypeTestData<LongEnum>(
LongEnum.FirstLong,
LongEnum.SecondLong,
LongEnum.ThirdLong);
/// <summary>
/// Common <see cref="TestData"/> for an <c>enum</c> decorated with a <see cref="FlagsAttribtute"/>.
/// </summary>
public static readonly ValueTypeTestData<FlagsEnum> FlagsEnumTestData = new ValueTypeTestData<FlagsEnum>(
FlagsEnum.One,
FlagsEnum.Two,
FlagsEnum.Four);
/// <summary>
/// Expected permutations of non supported file paths.
/// </summary>
public static readonly TestData<string> NotSupportedFilePaths = new RefTypeTestData<string>(() => new List<string>() {
"cc:\\a\\b",
});
/// <summary>
/// Expected permutations of invalid file paths.
/// </summary>
public static readonly TestData<string> InvalidNonNullFilePaths = new RefTypeTestData<string>(() => new List<string>() {
String.Empty,
"",
" ",
" ",
"\t\t \n ",
"c:\\a<b",
"c:\\a>b",
"c:\\a\"b",
"c:\\a\tb",
"c:\\a|b",
"c:\\a\bb",
"c:\\a\0b",
});
/// <summary>
/// All expected permutations of an empty string.
/// </summary>
public static readonly TestData<string> NonNullEmptyStrings = new RefTypeTestData<string>(() => new List<string>() { String.Empty, "", " ", "\t\r\n" });
/// <summary>
/// All expected permutations of an empty string.
/// </summary>
public static readonly TestData<string> EmptyStrings = new RefTypeTestData<string>(() => new List<string>() { null, String.Empty, "", " ", "\t\r\n" });
/// <summary>
/// Common <see cref="TestData"/> for a <see cref="string"/>.
/// </summary>
public static readonly RefTypeTestData<string> StringTestData = new RefTypeTestData<string>(() => new List<string>() {
"",
" ", // one space
" ", // multiple spaces
" data ", // leading and trailing whitespace
"\t\t \n ",
"Some String!"});
/// <summary>
/// A read-only collection of value type test data.
/// </summary>
public static readonly ReadOnlyCollection<TestData> ValueTypeTestDataCollection = new ReadOnlyCollection<TestData>(new TestData[] {
CharTestData,
IntTestData,
UintTestData,
ShortTestData,
UshortTestData,
LongTestData,
UlongTestData,
ByteTestData,
SByteTestData,
BoolTestData,
DoubleTestData,
FloatTestData,
DecimalTestData,
TimeSpanTestData,
GuidTestData,
DateTimeOffsetTestData,
SimpleEnumTestData,
LongEnumTestData,
FlagsEnumTestData});
/// <summary>
/// A read-only collection of representative values and reference type test data.
/// Uses where exhaustive coverage is not required.
/// </summary>
public static readonly ReadOnlyCollection<TestData> RepresentativeValueAndRefTypeTestDataCollection = new ReadOnlyCollection<TestData>(new TestData[] {
IntTestData,
BoolTestData,
SimpleEnumTestData,
StringTestData,
});
private Dictionary<TestDataVariations, TestDataVariationProvider> registeredTestDataVariations;
/// <summary>
/// Initializes a new instance of the <see cref="TestData"/> class.
/// </summary>
/// <param name="type">The type associated with the <see cref="TestData"/> instance.</param>
protected TestData(Type type)
{
this.Type = type;
this.registeredTestDataVariations = new Dictionary<TestDataVariations, TestDataVariationProvider>();
}
/// <summary>
/// Gets the type associated with the <see cref="TestData"/> instance.
/// </summary>
public Type Type { get; private set; }
/// <summary>
/// Gets the supported test data variations.
/// </summary>
/// <returns></returns>
public IEnumerable<TestDataVariations> GetSupportedTestDataVariations()
{
return this.registeredTestDataVariations.Keys;
}
/// <summary>
/// Gets the related type for the given test data variation or returns null if the <see cref="TestData"/> instance
/// doesn't support the given variation.
/// </summary>
/// <param name="variation">The test data variation with which to create the related <see cref="Type"/>.</param>
/// <returns>The related <see cref="Type"/> for the <see cref="TestData.Type"/> as given by the test data variation.</returns>
/// <example>
/// For example, if the given <see cref="TestData"/> was created for <see cref="string"/> test data and the variation parameter
/// was <see cref="TestDataVariations.AsList"/> then the returned type would be <see cref="List<string>"/>.
/// </example>
public Type GetAsTypeOrNull(TestDataVariations variation)
{
TestDataVariationProvider testDataVariation = null;
if (this.registeredTestDataVariations.TryGetValue(variation, out testDataVariation))
{
return testDataVariation.Type;
}
return null;
}
/// <summary>
/// Gets test data for the given test data variation or returns null if the <see cref="TestData"/> instance
/// doesn't support the given variation.
/// </summary>
/// <param name="variation">The test data variation with which to create the related test data.</param>
/// <returns>Test data of the type specified by the <see cref="TestData.GetAsTypeOrNull"/> method.</returns>
public object GetAsTestDataOrNull(TestDataVariations variation)
{
TestDataVariationProvider testDataVariation = null;
if (this.registeredTestDataVariations.TryGetValue(variation, out testDataVariation))
{
return testDataVariation.TestDataProvider();
}
return null;
}
/// <summary>
/// Allows derived classes to register a <paramref name="testDataProvider "/> <see cref="Func<>"/> that will
/// provide test data for a given variation.
/// </summary>
/// <param name="variation">The variation with which to register the <paramref name="testDataProvider "/>r.</param>
/// <param name="type">The type of the test data created by the <paramref name="testDataProvider "/></param>
/// <param name="testDataProvider">A <see cref="Func<>"/> that will provide test data.</param>
protected void RegisterTestDataVariation(TestDataVariations variation, Type type, Func<object> testDataProvider)
{
this.registeredTestDataVariations.Add(variation, new TestDataVariationProvider(type, testDataProvider));
}
private class TestDataVariationProvider
{
public TestDataVariationProvider(Type type, Func<object> testDataProvider)
{
this.Type = type;
this.TestDataProvider = testDataProvider;
}
public Func<object> TestDataProvider { get; private set; }
public Type Type { get; private set; }
}
}
/// <summary>
/// A generic base class for test data.
/// </summary>
/// <typeparam name="T">The type associated with the test data.</typeparam>
public abstract class TestData<T> : TestData, IEnumerable<T>
{
private static readonly Type OpenIEnumerableType = typeof(IEnumerable<>);
private static readonly Type OpenListType = typeof(List<>);
private static readonly Type OpenIQueryableType = typeof(IQueryable<>);
private static readonly Type OpenDictionaryType = typeof(Dictionary<,>);
private static readonly Type OpenTestDataHolderType = typeof(TestDataHolder<>);
private int dictionaryKey;
/// <summary>
/// Initializes a new instance of the <see cref="TestData&lt;T&gt;"/> class.
/// </summary>
protected TestData()
: base(typeof(T))
{
Type[] typeParams = new Type[] { this.Type };
Type[] dictionaryTypeParams = new Type[] { typeof(string), this.Type };
Type arrayType = this.Type.MakeArrayType();
Type listType = OpenListType.MakeGenericType(typeParams);
Type iEnumerableType = OpenIEnumerableType.MakeGenericType(typeParams);
Type iQueryableType = OpenIQueryableType.MakeGenericType(typeParams);
Type dictionaryType = OpenDictionaryType.MakeGenericType(dictionaryTypeParams);
Type testDataHolderType = OpenTestDataHolderType.MakeGenericType(typeParams);
this.RegisterTestDataVariation(TestDataVariations.AsInstance, this.Type, () => GetTypedTestData());
this.RegisterTestDataVariation(TestDataVariations.AsArray, arrayType, GetTestDataAsArray);
this.RegisterTestDataVariation(TestDataVariations.AsIEnumerable, iEnumerableType, GetTestDataAsIEnumerable);
this.RegisterTestDataVariation(TestDataVariations.AsIQueryable, iQueryableType, GetTestDataAsIQueryable);
this.RegisterTestDataVariation(TestDataVariations.AsList, listType, GetTestDataAsList);
this.RegisterTestDataVariation(TestDataVariations.AsDictionary, dictionaryType, GetTestDataAsDictionary);
this.RegisterTestDataVariation(TestDataVariations.AsClassMember, testDataHolderType, GetTestDataInHolder);
}
public IEnumerator<T> GetEnumerator()
{
return (IEnumerator<T>)this.GetTypedTestData().ToList().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return (IEnumerator)this.GetTypedTestData().ToList().GetEnumerator();
}
/// <summary>
/// Gets the test data as an array.
/// </summary>
/// <returns>An array of test data of the given type.</returns>
public T[] GetTestDataAsArray()
{
return this.GetTypedTestData().ToArray();
}
/// <summary>
/// Gets the test data as a <see cref="List<>"/>.
/// </summary>
/// <returns>A <see cref="List<>"/> of test data of the given type.</returns>
public List<T> GetTestDataAsList()
{
return this.GetTypedTestData().ToList();
}
/// <summary>
/// Gets the test data as an <see cref="IEnumerable<>"/>.
/// </summary>
/// <returns>An <see cref="IEnumerable<>"/> of test data of the given type.</returns>
public IEnumerable<T> GetTestDataAsIEnumerable()
{
return this.GetTypedTestData().AsEnumerable();
}
/// <summary>
/// Gets the test data as an <see cref="IQueryable<>"/>.
/// </summary>
/// <returns>An <see cref="IQueryable<>"/> of test data of the given type.</returns>
public IQueryable<T> GetTestDataAsIQueryable()
{
//return this.GetTypedTestData().AsQueryable();
return null;
}
public Dictionary<string, T> GetTestDataAsDictionary()
{
// Some TestData collections contain duplicates e.g. UintTestData contains both 0 and UInt32.MinValue.
// Therefore use dictionaryKey, not _unused.ToString(). Reset key to keep dictionaries consistent if used
// multiple times.
dictionaryKey = 0;
return this.GetTypedTestData().ToDictionary(_unused => (dictionaryKey++).ToString());
}
public IEnumerable<TestDataHolder<T>> GetTestDataInHolder()
{
return this.GetTypedTestData().Select(value => new TestDataHolder<T> { V1 = value, });
}
/// <summary>
/// Must be implemented by derived types to return test data of the given type.
/// </summary>
/// <returns>Test data of the given type.</returns>
protected abstract IEnumerable<T> GetTypedTestData();
}
}

View File

@ -1,51 +0,0 @@
// 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.Globalization;
using System.Reflection;
namespace Microsoft.TestCommon
{
/// <summary>
/// Equatable class wrapping a single instance of type <paramref name="T"/>. Equatable to ease test assertions.
/// </summary>
/// <typeparam name="T">The <see cref="Type"/> to wrap.</typeparam>
public class TestDataHolder<T> : IEquatable<TestDataHolder<T>>
{
public T V1 { get; set; }
bool IEquatable<TestDataHolder<T>>.Equals(TestDataHolder<T> other)
{
if (other == null)
{
return false;
}
return Object.Equals(V1, other.V1);
}
public override bool Equals(object obj)
{
TestDataHolder<T> that = obj as TestDataHolder<T>;
return ((IEquatable<TestDataHolder<T>>)this).Equals(that);
}
public override int GetHashCode()
{
if (typeof(ValueType).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo()) || V1 != null)
{
return V1.GetHashCode();
}
else
{
return 0;
}
}
public override string ToString()
{
return String.Format(CultureInfo.InvariantCulture, "{{ V1: '{0}' }}", V1);
}
}
}

View File

@ -1,223 +0,0 @@
// 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;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Xunit.Sdk;
namespace Microsoft.TestCommon
{
public class TestDataSetAttribute : DataAttribute
{
public Type DeclaringType { get; set; }
public string PropertyName { get; set; }
public TestDataVariations TestDataVariations { get; set; }
private IEnumerable<Tuple<Type, string>> ExtraDataSets { get; set; }
public TestDataSetAttribute(Type declaringType, string propertyName, TestDataVariations testDataVariations = TestCommon.TestDataVariations.All)
{
DeclaringType = declaringType;
PropertyName = propertyName;
TestDataVariations = testDataVariations;
ExtraDataSets = new List<Tuple<Type, string>>();
}
public TestDataSetAttribute(
Type declaringType,
string propertyName,
Type declaringType1,
string propertyName1,
TestDataVariations testDataVariations = TestCommon.TestDataVariations.All)
: this(declaringType, propertyName, testDataVariations)
{
ExtraDataSets = new List<Tuple<Type, string>> { Tuple.Create(declaringType1, propertyName1) };
}
public TestDataSetAttribute(
Type declaringType,
string propertyName,
Type declaringType1,
string propertyName1,
Type declaringType2,
string propertyName2,
TestDataVariations testDataVariations = TestCommon.TestDataVariations.All)
: this(declaringType, propertyName, testDataVariations)
{
ExtraDataSets = new List<Tuple<Type, string>> { Tuple.Create(declaringType1, propertyName1), Tuple.Create(declaringType2, propertyName2) };
}
public TestDataSetAttribute(
Type declaringType,
string propertyName,
Type declaringType1,
string propertyName1,
Type declaringType2,
string propertyName2,
Type declaringType3,
string propertyName3,
TestDataVariations testDataVariations = TestCommon.TestDataVariations.All)
: this(declaringType, propertyName, testDataVariations)
{
ExtraDataSets = new List<Tuple<Type, string>> { Tuple.Create(declaringType1, propertyName1), Tuple.Create(declaringType2, propertyName2), Tuple.Create(declaringType3, propertyName3) };
}
public TestDataSetAttribute(
Type declaringType,
string propertyName,
Type declaringType1,
string propertyName1,
Type declaringType2,
string propertyName2,
Type declaringType3,
string propertyName3,
Type declaringType4,
string propertyName4,
TestDataVariations testDataVariations = TestCommon.TestDataVariations.All)
: this(declaringType, propertyName, testDataVariations)
{
ExtraDataSets = new List<Tuple<Type, string>>
{
Tuple.Create(declaringType1, propertyName1), Tuple.Create(declaringType2, propertyName2),
Tuple.Create(declaringType3, propertyName3), Tuple.Create(declaringType4, propertyName4)
};
}
public override IEnumerable<object[]> GetData(MethodInfo testMethod)
{
IEnumerable<object[]> baseDataSet = GetBaseDataSet(DeclaringType, PropertyName, TestDataVariations);
IEnumerable<IEnumerable<object[]>> extraDataSets = GetExtraDataSets();
IEnumerable<IEnumerable<object[]>> finalDataSets = (new[] { baseDataSet }).Concat(extraDataSets);
var datasets = CrossProduct(finalDataSets);
return datasets;
}
private static IEnumerable<object[]> CrossProduct(IEnumerable<IEnumerable<object[]>> datasets)
{
if (datasets.Count() == 1)
{
foreach (var dataset in datasets.First())
{
yield return dataset;
}
}
else
{
IEnumerable<object[]> datasetLeft = datasets.First();
IEnumerable<object[]> datasetRight = CrossProduct(datasets.Skip(1));
foreach (var dataLeft in datasetLeft)
{
foreach (var dataRight in datasetRight)
{
yield return dataLeft.Concat(dataRight).ToArray();
}
}
}
}
// The base data set(first one) can either be a TestDataSet or a TestDataSetCollection
private static IEnumerable<object[]> GetBaseDataSet(Type declaringType, string propertyName, TestDataVariations variations)
{
return TryGetDataSetFromTestDataCollection(declaringType, propertyName, variations) ?? GetDataSet(declaringType, propertyName);
}
private IEnumerable<IEnumerable<object[]>> GetExtraDataSets()
{
foreach (var tuple in ExtraDataSets)
{
yield return GetDataSet(tuple.Item1, tuple.Item2);
}
}
private static object GetTestDataPropertyValue(Type declaringType, string propertyName)
{
PropertyInfo property = declaringType.GetProperty(propertyName, BindingFlags.Static | BindingFlags.Public);
if (property == null)
{
throw new ArgumentException(String.Format("Could not find public static property {0} on {1}", propertyName, declaringType.FullName));
}
else
{
return property.GetValue(null, null);
}
}
private static IEnumerable<object[]> GetDataSet(Type declaringType, string propertyName)
{
object propertyValue = GetTestDataPropertyValue(declaringType, propertyName);
// box the dataset items if the property is not a RefTypeTestData
IEnumerable<object> value = (propertyValue as IEnumerable<object>) ?? (propertyValue as IEnumerable).Cast<object>();
if (value == null)
{
throw new InvalidOperationException(String.Format("{0}.{1} is either null or does not implement IEnumerable", declaringType.FullName, propertyName));
}
IEnumerable<object[]> dataset = value as IEnumerable<object[]>;
if (dataset != null)
{
return dataset;
}
else
{
return value.Select((data) => new object[] { data });
}
}
private static IEnumerable<object[]> TryGetDataSetFromTestDataCollection(Type declaringType, string propertyName, TestDataVariations variations)
{
object propertyValue = GetTestDataPropertyValue(declaringType, propertyName);
IEnumerable<TestData> testDataCollection = propertyValue as IEnumerable<TestData>;
return testDataCollection == null ? null : GetDataSetFromTestDataCollection(testDataCollection, variations);
}
private static IEnumerable<object[]> GetDataSetFromTestDataCollection(IEnumerable<TestData> testDataCollection, TestDataVariations variations)
{
foreach (TestData testDataInstance in testDataCollection)
{
foreach (TestDataVariations variation in testDataInstance.GetSupportedTestDataVariations())
{
if ((variation & variations) == variation)
{
Type variationType = testDataInstance.GetAsTypeOrNull(variation);
object testData = testDataInstance.GetAsTestDataOrNull(variation);
if (AsSingleInstances(variation))
{
foreach (object obj in (IEnumerable)testData)
{
yield return new object[] { variationType, obj };
}
}
else
{
yield return new object[] { variationType, testData };
}
}
}
}
}
private static bool AsSingleInstances(TestDataVariations variation)
{
return variation == TestDataVariations.AsInstance ||
variation == TestDataVariations.AsNullable ||
variation == TestDataVariations.AsDerivedType ||
variation == TestDataVariations.AsKnownType ||
variation == TestDataVariations.AsDataMember ||
variation == TestDataVariations.AsClassMember ||
variation == TestDataVariations.AsXmlElementProperty;
}
}
}

View File

@ -1,116 +0,0 @@
// 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;
namespace Microsoft.TestCommon
{
/// <summary>
/// An flags enum that can be used to indicate different variations of a given
/// <see cref="TestData"/> instance.
/// </summary>
[Flags]
public enum TestDataVariations
{
/// <summary>
/// An individual instance of a given <see cref="TestData"/> type.
/// </summary>
AsInstance = 0x1,
/// <summary>
/// An individual instance of a type that derives from a given <see cref="TestData"/> type.
/// </summary>
AsDerivedType = 0x2,
/// <summary>
/// An individual instance of a given <see cref="TestData"/> type that has a property value
/// that is a known type of the declared property type.
/// </summary>
AsKnownType = 0x4,
/// <summary>
/// A <see cref="Nullable<>"/> instance of a given <see cref="TestData"/> type. Only applies to
/// instances of <see cref="ValueTypeTestData"/>.
/// </summary>
AsNullable = 0x8,
/// <summary>
/// An instance of a <see cref="System.Collections.Generic.List<>"/> of a given <see cref="TestData"/> type.
/// </summary>
AsList = 0x10,
/// <summary>
/// An instance of a array of the <see cref="TestData"/> type.
/// </summary>
AsArray = 0x20,
/// <summary>
/// An instance of an <see cref="System.Collections.Generic.IEnumerable<>"/> of a given <see cref="TestData"/> type.
/// </summary>
AsIEnumerable = 0x40,
/// <summary>
/// An instance of an <see cref="System.Linq.IQueryable<>"/> of a given <see cref="TestData"/> type.
/// </summary>
AsIQueryable = 0x80,
/// <summary>
/// An instance of a DataContract type in which a given <see cref="TestData"/> type is a member.
/// </summary>
AsDataMember = 0x100,
/// <summary>
/// An instance of a type in which a given <see cref="TestData"/> type is decorated with a
/// <see cref="System.Xml.Serialization.XmlElementAttribute"/>.
/// </summary>
AsXmlElementProperty = 0x200,
/// <summary>
/// An instance of a <see cref="System.Collections.Generic.IDictionary{string,TValue}"/> of a given
/// <see cref="TestData"/> type.
/// </summary>
AsDictionary = 0x400,
/// <summary>
/// Add a <c>null</c> instance of the given <see cref="TestData"/> type to the data set. This variation is
/// not included in <see cref="All"/> or other variation masks.
/// </summary>
WithNull = 0x800,
/// <summary>
/// Individual instances of <see cref="TestDataHolder{T}"/> containing the given <see cref="TestData"/>. This
/// variation is not included in <see cref="All"/> or other variation masks.
/// </summary>
AsClassMember = 0x1000,
/// <summary>
/// All of the flags for single instance variations of a given <see cref="TestData"/> type.
/// </summary>
AllSingleInstances = AsInstance | AsDerivedType | AsKnownType | AsNullable,
/// <summary>
/// All of the flags for collection variations of a given <see cref="TestData"/> type.
/// </summary>
AllCollections = AsList | AsArray | AsIEnumerable | AsIQueryable | AsDictionary,
/// <summary>
/// All of the flags for variations in which a given <see cref="TestData"/> type is a property on another type.
/// </summary>
AllProperties = AsDataMember | AsXmlElementProperty,
/// <summary>
/// All of the flags for interface collection variations of a given <see cref="TestData"/> type.
/// </summary>
AllInterfaces = AsIEnumerable | AsIQueryable,
/// <summary>
/// All of the flags except for the interface collection variations of a given <see cref="TestData"/> type.
/// </summary>
AllNonInterfaces = All & ~AllInterfaces,
/// <summary>
/// All of the flags for all of the variations of a given <see cref="TestData"/> type.
/// </summary>
All = AllSingleInstances | AllCollections | AllProperties
}
}

View File

@ -1,165 +0,0 @@
// 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.Reflection;
using Xunit;
namespace Microsoft.TestCommon
{
/// <summary>
/// MSTest utility for testing that a given type has the expected properties such as being public, sealed, etc.
/// </summary>
public class TypeAssert
{
/// <summary>
/// Specifies a set of type properties to test for using the <see cref="CheckProperty"/> method.
/// This enumeration has a <see cref="FlagsAttribute"/> attribute that allows a bitwise combination of its member values.
/// </summary>
[Flags]
public enum TypeProperties
{
/// <summary>
/// Indicates that the type must be abstract.
/// </summary>
IsAbstract = 0x1,
/// <summary>
/// Indicates that the type must be a class.
/// </summary>
IsClass = 0x2,
/// <summary>
/// Indicates that the type must be a COM object.
/// </summary>
IsComObject = 0x4,
/// <summary>
/// Indicates that the type must be disposable.
/// </summary>
IsDisposable = 0x8,
/// <summary>
/// Indicates that the type must be an enum.
/// </summary>
IsEnum = 0x10,
/// <summary>
/// Indicates that the type must be a generic type.
/// </summary>
IsGenericType = 0x20,
/// <summary>
/// Indicates that the type must be a generic type definition.
/// </summary>
IsGenericTypeDefinition = 0x40,
/// <summary>
/// Indicates that the type must be an interface.
/// </summary>
IsInterface = 0x80,
/// <summary>
/// Indicates that the type must be nested and declared private.
/// </summary>
IsNestedPrivate = 0x100,
/// <summary>
/// Indicates that the type must be nested and declared public.
/// </summary>
IsNestedPublic = 0x200,
/// <summary>
/// Indicates that the type must be public.
/// </summary>
IsPublic = 0x400,
/// <summary>
/// Indicates that the type must be sealed.
/// </summary>
IsSealed = 0x800,
/// <summary>
/// Indicates that the type must be visible outside the assembly.
/// </summary>
IsVisible = 0x1000,
/// <summary>
/// Indicates that the type must be static.
/// </summary>
IsStatic = TypeAssert.TypeProperties.IsAbstract | TypeAssert.TypeProperties.IsSealed,
/// <summary>
/// Indicates that the type must be a public, visible class.
/// </summary>
IsPublicVisibleClass = TypeAssert.TypeProperties.IsClass | TypeAssert.TypeProperties.IsPublic | TypeAssert.TypeProperties.IsVisible
}
private static void CheckProperty(Type type, bool expected, bool actual, string property)
{
Assert.NotNull(type);
Assert.True(expected == actual, String.Format("Type '{0}' should{1} be {2}.", type.FullName, expected ? "" : " NOT", property));
}
/// <summary>
/// Determines whether the specified type has a given set of properties such as being public, sealed, etc.
/// The method asserts if one or more of the properties are not satisfied.
/// </summary>
/// <typeparam name="T">The type to test for properties.</typeparam>
/// <param name="typeProperties">The set of type properties to test for.</param>
public void HasProperties<T>(TypeProperties typeProperties)
{
HasProperties(typeof(T), typeProperties);
}
/// <summary>
/// Determines whether the specified type has a given set of properties such as being public, sealed, etc.
/// The method asserts if one or more of the properties are not satisfied.
/// </summary>
/// <typeparam name="T">The type to test for properties.</typeparam>
/// <typeparam name="TIsAssignableFrom">Verify that the type to test is assignable from this type.</typeparam>
/// <param name="typeProperties">The set of type properties to test for.</param>
public void HasProperties<T, TIsAssignableFrom>(TypeProperties typeProperties)
{
HasProperties(typeof(T), typeProperties, typeof(TIsAssignableFrom));
}
/// <summary>
/// Determines whether the specified type has a given set of properties such as being public, sealed, etc.
/// The method asserts if one or more of the properties are not satisfied.
/// </summary>
/// <param name="type">The type to test for properties.</param>
/// <param name="typeProperties">The set of type properties to test for.</param>
public void HasProperties(Type type, TypeProperties typeProperties)
{
HasProperties(type, typeProperties, null);
}
/// <summary>
/// Determines whether the specified type has a given set of properties such as being public, sealed, etc.
/// The method asserts if one or more of the properties are not satisfied.
/// </summary>
/// <param name="type">The type to test for properties.</param>
/// <param name="typeProperties">The set of type properties to test for.</param>
/// <param name="isAssignableFrom">Verify that the type to test is assignable from this type.</param>
public void HasProperties(Type type, TypeProperties typeProperties, Type isAssignableFrom)
{
TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsAbstract) > 0, type.GetTypeInfo().IsAbstract, "abstract");
TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsClass) > 0, type.GetTypeInfo().IsClass, "a class");
TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsDisposable) > 0, typeof(IDisposable).IsAssignableFrom(type), "disposable");
TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsEnum) > 0, type.GetTypeInfo().IsEnum, "an enum");
TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsGenericType) > 0, type.GetTypeInfo().IsGenericType, "a generic type");
TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsGenericTypeDefinition) > 0, type.GetTypeInfo().IsGenericTypeDefinition, "a generic type definition");
TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsInterface) > 0, type.GetTypeInfo().IsInterface, "an interface");
TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsNestedPrivate) > 0, type.GetTypeInfo().IsNestedPrivate, "nested private");
TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsNestedPublic) > 0, type.GetTypeInfo().IsNestedPublic, "nested public");
TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsPublic) > 0, type.GetTypeInfo().IsPublic, "public");
TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsSealed) > 0, type.GetTypeInfo().IsSealed, "sealed");
TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsVisible) > 0, type.GetTypeInfo().IsVisible, "visible");
if (isAssignableFrom != null)
{
TypeAssert.CheckProperty(type, true, isAssignableFrom.GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()), String.Format("assignable from {0}", isAssignableFrom.FullName));
}
}
}
}

View File

@ -1,40 +0,0 @@
// 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.Linq;
namespace Microsoft.TestCommon
{
public class ValueTypeTestData<T> : TestData<T> where T : struct
{
private static readonly Type OpenNullableType = typeof(Nullable<>);
private T[] testData;
public ValueTypeTestData(params T[] testData)
: base()
{
this.testData = testData;
Type[] typeParams = new Type[] { this.Type };
this.RegisterTestDataVariation(TestDataVariations.WithNull, OpenNullableType.MakeGenericType(typeParams), GetNullTestData);
this.RegisterTestDataVariation(TestDataVariations.AsNullable, OpenNullableType.MakeGenericType(typeParams), GetTestDataAsNullable);
}
public object GetNullTestData()
{
return null;
}
public IEnumerable<Nullable<T>> GetTestDataAsNullable()
{
return this.GetTypedTestData().Select(d => new Nullable<T>(d));
}
protected override IEnumerable<T> GetTypedTestData()
{
return this.testData;
}
}
}