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
|
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}"
|
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
|
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}"
|
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
|
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}"
|
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.ActiveCfg = Release|Any CPU
|
||||||
{5F945B82-FE5F-425C-956C-8BC2F2020254}.Release|Mixed Platforms.Build.0 = 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
|
{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.ActiveCfg = Debug|Any CPU
|
||||||
{B2347320-308E-4D2B-AEC8-005DFA68B0C9}.Debug|Any CPU.Build.0 = 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
|
{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}
|
{3F6E355E-4869-41D9-943B-D54771221A7F} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
|
||||||
{A8AA326E-8EE8-4F11-B750-23028E0949D7} = {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}
|
{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}
|
{B2347320-308E-4D2B-AEC8-005DFA68B0C9} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
|
||||||
{860119ED-3DB1-424D-8D0A-30132A8A7D96} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
|
{860119ED-3DB1-424D-8D0A-30132A8A7D96} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
|
||||||
{F504357E-C2E1-4818-BA5C-9A2EAC25FEE5} = {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
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApplicationModelWebSite", "test\WebSites\ApplicationModelWebSite\ApplicationModelWebSite.csproj", "{CAE52CB7-0FAC-4B5B-8251-B0FF837DB657}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApplicationModelWebSite", "test\WebSites\ApplicationModelWebSite\ApplicationModelWebSite.csproj", "{CAE52CB7-0FAC-4B5B-8251-B0FF837DB657}"
|
||||||
EndProject
|
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}"
|
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
|
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}"
|
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.ActiveCfg = Release|Any CPU
|
||||||
{CAE52CB7-0FAC-4B5B-8251-B0FF837DB657}.Release|Mixed Platforms.Build.0 = 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
|
{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.ActiveCfg = Debug|Any CPU
|
||||||
{B2347320-308E-4D2B-AEC8-005DFA68B0C9}.Debug|Any CPU.Build.0 = 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
|
{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}
|
{6DB9B8D0-80F7-4E70-BBB0-0B4C04D79A47} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
||||||
{0EF9860B-10D7-452F-B0F4-A405B88BEBB3} = {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}
|
{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}
|
{B2347320-308E-4D2B-AEC8-005DFA68B0C9} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
|
||||||
{860119ED-3DB1-424D-8D0A-30132A8A7D96} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
|
{860119ED-3DB1-424D-8D0A-30132A8A7D96} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
|
||||||
{920F8A0E-6F7D-4BBE-84FF-840B89099BE6} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
{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.TagHelpers, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Testing, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Testing, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.ViewFeatures, 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.Abstractions.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.ApiExplorer.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.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.ViewFeatures.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.Views.TestCommon, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.WebApiCompatShimTest, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
|
||||||
|
|
||||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Performance, 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\SimpleWebSite\SimpleWebSite.csproj" />
|
||||||
<ProjectReference Include="..\WebSites\TagHelpersWebSite\TagHelpersWebSite.csproj" />
|
<ProjectReference Include="..\WebSites\TagHelpersWebSite\TagHelpersWebSite.csproj" />
|
||||||
<ProjectReference Include="..\WebSites\VersioningWebSite\VersioningWebSite.csproj" />
|
<ProjectReference Include="..\WebSites\VersioningWebSite\VersioningWebSite.csproj" />
|
||||||
<ProjectReference Include="..\WebSites\WebApiCompatShimWebSite\WebApiCompatShimWebSite.csproj" />
|
|
||||||
<ProjectReference Include="..\WebSites\XmlFormattersWebSite\XmlFormattersWebSite.csproj" />
|
<ProjectReference Include="..\WebSites\XmlFormattersWebSite\XmlFormattersWebSite.csproj" />
|
||||||
|
|
||||||
<PackageReference Include="AngleSharp" Version="$(AngleSharpPackageVersion)" />
|
<PackageReference Include="AngleSharp" Version="$(AngleSharpPackageVersion)" />
|
||||||
|
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="$(MicrosoftAspNetWebApiClientPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.ChunkingCookieManager.Sources" PrivateAssets="All" Version="$(MicrosoftAspNetCoreChunkingCookieManagerSourcesPackageVersion)" />
|
<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.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
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