Remove WebApiCompatShim
This commit is contained in:
parent
b18526cdc8
commit
ecb7edadc8
|
|
@ -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
39
Mvc.sln
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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")]
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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" }));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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))]
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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)" />
|
||||
|
||||
<!--
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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<T>"/> 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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue