├── .gitattributes ├── .gitignore ├── .nuget └── NuGet.Config ├── DynamicRest.UnitTests ├── DynamicRest.UnitTests.csproj ├── Fluent │ └── RestClientBuilderTests.cs ├── Json │ └── JsonResponsesTest.cs ├── RestClients │ ├── HttpVerbRequestBuilderTests.cs │ └── TemplatedUriRequestBuilderTests.cs ├── Simulators │ └── ResponseSimulatorTests.cs ├── TestDoubles │ ├── FakeHttpRequestFactory.cs │ ├── FakeHttpWebRequestWrapper.cs │ ├── FakeHttpWebResponseWrapper.cs │ └── FakeWebProxy.cs └── Xml │ └── XmlResponsesTests.cs ├── DynamicRest.sln ├── DynamicRest ├── BinaryResponseProcessor.cs ├── DynamicParsingException.cs ├── DynamicRest.csproj ├── Fluent │ ├── IRestClientBuilder.cs │ └── RestClientBuilder.cs ├── HTTPInterfaces │ ├── HttpVerb.cs │ ├── HttpWebRequestWrappers │ │ ├── HttpWebRequestFactory.cs │ │ ├── HttpWebRequestWrapper.cs │ │ └── HttpWebResponseWrapper.cs │ ├── IHttpRequest.cs │ ├── IHttpRequestFactory.cs │ ├── IHttpResponse.cs │ └── WebWrappers │ │ ├── HttpWebRequestWrapper.cs │ │ ├── HttpWebResponseWrapper.cs │ │ └── RequestFactory.cs ├── Helpers │ └── HttpVerbHelpers.cs ├── HttpVerbRequestBuilder.cs ├── IBuildDynamicResults.cs ├── IBuildRequests.cs ├── IProcessResponses.cs ├── IRestUriTransformer.cs ├── Json │ ├── JsonArray.cs │ ├── JsonObject.cs │ ├── JsonReader.cs │ └── JsonWriter.cs ├── ParametersStore.cs ├── ResponseProcessor.cs ├── RestCallback.cs ├── RestClient.cs ├── RestOperation.cs ├── RestService.cs ├── StandardResultBuilder.cs ├── TemplatedUriBuilder.cs ├── TemplatedUriRequestBuilder.cs └── Xml │ ├── XmlNode.cs │ ├── XmlNodeList.cs │ └── XmlString.cs ├── License.txt ├── README.md └── Samples ├── AmazonSample.cs ├── AmazonUriSigner.cs ├── BingSearchSample.cs ├── GoogleSearchSample.cs ├── JsonSample.cs ├── Program.cs ├── Samples.csproj └── Services.cs /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opensdf 80 | *.sdf 81 | *.cachefile 82 | 83 | # Visual Studio profiler 84 | *.psess 85 | *.vsp 86 | *.vspx 87 | *.sap 88 | 89 | # TFS 2012 Local Workspace 90 | $tf/ 91 | 92 | # Guidance Automation Toolkit 93 | *.gpState 94 | 95 | # ReSharper is a .NET coding add-in 96 | _ReSharper*/ 97 | *.[Rr]e[Ss]harper 98 | *.DotSettings.user 99 | 100 | # JustCode is a .NET coding add-in 101 | .JustCode 102 | 103 | # TeamCity is a build add-in 104 | _TeamCity* 105 | 106 | # DotCover is a Code Coverage Tool 107 | *.dotCover 108 | 109 | # NCrunch 110 | _NCrunch_* 111 | .*crunch*.local.xml 112 | nCrunchTemp_* 113 | 114 | # MightyMoose 115 | *.mm.* 116 | AutoTest.Net/ 117 | 118 | # Web workbench (sass) 119 | .sass-cache/ 120 | 121 | # Installshield output folder 122 | [Ee]xpress/ 123 | 124 | # DocProject is a documentation generator add-in 125 | DocProject/buildhelp/ 126 | DocProject/Help/*.HxT 127 | DocProject/Help/*.HxC 128 | DocProject/Help/*.hhc 129 | DocProject/Help/*.hhk 130 | DocProject/Help/*.hhp 131 | DocProject/Help/Html2 132 | DocProject/Help/html 133 | 134 | # Click-Once directory 135 | publish/ 136 | 137 | # Publish Web Output 138 | *.[Pp]ublish.xml 139 | *.azurePubxml 140 | # TODO: Comment the next line if you want to checkin your web deploy settings 141 | # but database connection strings (with potential passwords) will be unencrypted 142 | *.pubxml 143 | *.publishproj 144 | 145 | # NuGet Packages 146 | *.nupkg 147 | # The packages folder can be ignored because of Package Restore 148 | **/packages/* 149 | # except build/, which is used as an MSBuild target. 150 | !**/packages/build/ 151 | # Uncomment if necessary however generally it will be regenerated when needed 152 | #!**/packages/repositories.config 153 | 154 | # Windows Azure Build Output 155 | csx/ 156 | *.build.csdef 157 | 158 | # Windows Azure Emulator 159 | efc/ 160 | rfc/ 161 | 162 | # Windows Store app package directory 163 | AppPackages/ 164 | 165 | # Visual Studio cache files 166 | # files ending in .cache can be ignored 167 | *.[Cc]ache 168 | # but keep track of directories ending in .cache 169 | !*.[Cc]ache/ 170 | 171 | # Others 172 | ClientBin/ 173 | [Ss]tyle[Cc]op.* 174 | ~$* 175 | *~ 176 | *.dbmdl 177 | *.dbproj.schemaview 178 | *.pfx 179 | *.publishsettings 180 | node_modules/ 181 | orleans.codegen.cs 182 | 183 | # RIA/Silverlight projects 184 | Generated_Code/ 185 | 186 | # Backup & report files from converting an old project file 187 | # to a newer Visual Studio version. Backup files are not needed, 188 | # because we have git ;-) 189 | _UpgradeReport_Files/ 190 | Backup*/ 191 | UpgradeLog*.XML 192 | UpgradeLog*.htm 193 | 194 | # SQL Server files 195 | *.mdf 196 | *.ldf 197 | 198 | # Business Intelligence projects 199 | *.rdl.data 200 | *.bim.layout 201 | *.bim_*.settings 202 | 203 | # Microsoft Fakes 204 | FakesAssemblies/ 205 | 206 | # GhostDoc plugin setting file 207 | *.GhostDoc.xml 208 | 209 | # Node.js Tools for Visual Studio 210 | .ntvs_analysis.dat 211 | 212 | # Visual Studio 6 build log 213 | *.plg 214 | 215 | # Visual Studio 6 workspace options file 216 | *.opt 217 | 218 | # Visual Studio LightSwitch build output 219 | **/*.HTMLClient/GeneratedArtifacts 220 | **/*.DesktopClient/GeneratedArtifacts 221 | **/*.DesktopClient/ModelManifest.xml 222 | **/*.Server/GeneratedArtifacts 223 | **/*.Server/ModelManifest.xml 224 | _Pvt_Extensions 225 | 226 | # Paket dependency manager 227 | .paket/paket.exe 228 | 229 | # FAKE - F# Make 230 | .fake/ -------------------------------------------------------------------------------- /.nuget/NuGet.Config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /DynamicRest.UnitTests/DynamicRest.UnitTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net452 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /DynamicRest.UnitTests/Fluent/RestClientBuilderTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using DynamicRest.Fluent; 4 | using DynamicRest.UnitTests.TestDoubles; 5 | using Machine.Specifications; 6 | 7 | namespace DynamicRest.UnitTests.Fluent { 8 | [Subject(typeof(RestClientBuilder))] 9 | public class When_a_rest_client_is_created_with_a_rest_client_builder_using_default_values { 10 | static IRestClientBuilder restClientBuilder; 11 | static dynamic builtClient; 12 | static FakeHttpRequestFactory fakeHttpRequestFactory; 13 | 14 | Establish context = () => 15 | { 16 | restClientBuilder = new RestClientBuilder(); 17 | }; 18 | 19 | Because the_rest_client_is_built_and_executed = () => 20 | { 21 | fakeHttpRequestFactory = new FakeHttpRequestFactory(); 22 | builtClient = restClientBuilder 23 | .WithRequestBuilder(new HttpVerbRequestBuilder(fakeHttpRequestFactory)) 24 | .WithOAuth2Token("token") 25 | .WithUri("http://www.google.com") 26 | .WithBody("My body") 27 | .WithUserAgent("useragent") 28 | .Build(); 29 | 30 | builtClient.Post(); 31 | }; 32 | 33 | It should_have_xml_as_content_type = () => 34 | fakeHttpRequestFactory.CreatedRequest.ContentType.ShouldEqual("application/xml"); 35 | It should_have_xml_as_accept = () => 36 | fakeHttpRequestFactory.CreatedRequest.Accept.ShouldEqual("application/xml"); 37 | It should_have_the_allow_auto_redirect_flag_set_to_false = () => 38 | fakeHttpRequestFactory.CreatedRequest.AllowAutoRedirect.ShouldEqual(false); 39 | It should_have_the_expected_user_agent = () => 40 | fakeHttpRequestFactory.CreatedRequest.UserAgent.ShouldEqual("useragent"); 41 | } 42 | 43 | [Subject(typeof(RestClientBuilder))] 44 | public class When_a_rest_client_is_created_with_a_rest_client_builder_without_OAuth2_token 45 | { 46 | static IRestClientBuilder restClientBuilder; 47 | static dynamic builtClient; 48 | 49 | static FakeHttpRequestFactory fakeHttpRequestFactory; 50 | 51 | Establish context = () => 52 | { 53 | restClientBuilder = new RestClientBuilder(); 54 | }; 55 | 56 | Because the_rest_client_is_built_and_executed = () => 57 | { 58 | fakeHttpRequestFactory = new FakeHttpRequestFactory(); 59 | builtClient = restClientBuilder 60 | .WithRequestBuilder(new HttpVerbRequestBuilder(fakeHttpRequestFactory)) 61 | .WithUri("http://www.google.com") 62 | .WithBody("My body") 63 | .Build(); 64 | 65 | builtClient.Post(); 66 | }; 67 | 68 | It should_have_xml_as_content_type = () => 69 | fakeHttpRequestFactory.CreatedRequest.ContentType.ShouldEqual("application/xml"); 70 | It should_have_xml_as_accept = () => 71 | fakeHttpRequestFactory.CreatedRequest.Accept.ShouldEqual("application/xml"); 72 | It should_have_the_allow_auto_redirect_flag_set_to_false = () => 73 | fakeHttpRequestFactory.CreatedRequest.AllowAutoRedirect.ShouldEqual(false); 74 | It should_have_no_OAuth_header = () => 75 | fakeHttpRequestFactory.CreatedRequest.Headers.Keys.ShouldNotContain(HttpRequestHeader.Authorization.ToString()); 76 | } 77 | 78 | [Subject(typeof(RestClientBuilder))] 79 | public class When_a_rest_client_is_created_with_a_rest_client_builder { 80 | 81 | static IRestClientBuilder restClientBuilder; 82 | static dynamic builtClient; 83 | 84 | static FakeHttpRequestFactory fakeHttpRequestFactory; 85 | 86 | Establish context = () => { 87 | restClientBuilder = new RestClientBuilder(); 88 | }; 89 | 90 | Because the_rest_client_is_built_and_executed = () => { 91 | fakeHttpRequestFactory = new FakeHttpRequestFactory(); 92 | builtClient = restClientBuilder 93 | .WithRequestBuilder(new HttpVerbRequestBuilder(fakeHttpRequestFactory)) 94 | .WithOAuth2Token("token") 95 | .WithContentType("application/vnd.data+xml") 96 | .WithUri("http://www.google.com") 97 | .WithBody("My body") 98 | .WithAcceptHeader("application/xml") 99 | .WithResponseProcessor(new ResponseProcessor(new StandardResultBuilder(RestService.Xml))) 100 | .WithAutoRedirect(true) 101 | .WithAcceptEncodingHeader("gzip") 102 | .Build(); 103 | 104 | builtClient.Post(); 105 | }; 106 | 107 | It should_have_the_correct_content_type_header = () => 108 | fakeHttpRequestFactory.CreatedRequest.ContentType.ShouldEqual("application/vnd.data+xml"); 109 | It should_have_the_correct_uri = () => 110 | fakeHttpRequestFactory.CreatedRequest.RequestURI.ShouldEqual(new Uri("http://www.google.com")); 111 | It should_have_the_correct_body = () => 112 | fakeHttpRequestFactory.CreatedRequest.RequestBody.ShouldEqual("My body"); 113 | It should_have_the_correct_accept_header = () => 114 | fakeHttpRequestFactory.CreatedRequest.Accept.ShouldEqual("application/xml"); 115 | It should_have_the_correct_token = () => 116 | fakeHttpRequestFactory.CreatedRequest.Headers[HttpRequestHeader.Authorization].ShouldEqual("Bearer token"); 117 | It should_have_the_allow_auto_redirect_flag_set_to_false = () => 118 | fakeHttpRequestFactory.CreatedRequest.AllowAutoRedirect.ShouldEqual(true); 119 | It should_have_the_accept_encode_header = () => 120 | fakeHttpRequestFactory.CreatedRequest.Headers[HttpRequestHeader.AcceptEncoding].ShouldEqual("gzip"); 121 | } 122 | 123 | [Subject(typeof(RestClientBuilder))] 124 | public class When_a_rest_client_is_created_with_a_rest_client_builder_with_timeout 125 | { 126 | static IRestClientBuilder restClientBuilder; 127 | static dynamic builtClient; 128 | 129 | static FakeHttpRequestFactory fakeHttpRequestFactory; 130 | 131 | Establish context = () => 132 | { 133 | restClientBuilder = new RestClientBuilder(); 134 | }; 135 | 136 | Because the_rest_client_is_built_and_executed = () => 137 | { 138 | fakeHttpRequestFactory = new FakeHttpRequestFactory(); 139 | builtClient = restClientBuilder 140 | .WithRequestBuilder(new HttpVerbRequestBuilder(fakeHttpRequestFactory)) 141 | .WithUri("http://www.google.com") 142 | .WithTimeout(timeout) 143 | .Build(); 144 | 145 | builtClient.Post(); 146 | }; 147 | 148 | It should_have_the_correct_timeout = () => 149 | fakeHttpRequestFactory.CreatedRequest.Timeout.ShouldEqual(timeout); 150 | 151 | static int timeout = 100; 152 | } 153 | 154 | [Subject(typeof(RestClientBuilder))] 155 | public class When_a_rest_client_is_created_with_a_rest_client_builder_with_proxy_settings 156 | { 157 | static IRestClientBuilder restClientBuilder; 158 | static dynamic builtClient; 159 | static FakeHttpRequestFactory fakeHttpRequestFactory; 160 | static Uri faekUriToGetProxyFor = new Uri("http://www.google.com"); 161 | static int timeout = 100; 162 | static FakeWebProxy fakeWebProxy = new FakeWebProxy(new Uri("http://proxy.com:5647")); 163 | 164 | Establish context = () => 165 | { 166 | restClientBuilder = new RestClientBuilder(); 167 | }; 168 | 169 | Because the_rest_client_is_built_and_executed = () => 170 | { 171 | fakeHttpRequestFactory = new FakeHttpRequestFactory(); 172 | builtClient = restClientBuilder 173 | .WithRequestBuilder(new HttpVerbRequestBuilder(fakeHttpRequestFactory)) 174 | .WithUri(faekUriToGetProxyFor.AbsoluteUri) 175 | .WithProxy(fakeWebProxy) 176 | .Build(); 177 | 178 | builtClient.Post(); 179 | }; 180 | 181 | It should_have_the_correct_proxy_uri = () => 182 | fakeHttpRequestFactory.CreatedRequest.Proxy.GetProxy(faekUriToGetProxyFor).AbsoluteUri.ShouldEqual(fakeWebProxy.GetProxy(faekUriToGetProxyFor).AbsoluteUri); 183 | It should_have_the_correct_proxy_host = () => 184 | fakeHttpRequestFactory.CreatedRequest.Proxy.GetProxy(faekUriToGetProxyFor).Host.ShouldEqual(fakeWebProxy.GetProxy(faekUriToGetProxyFor).Host); 185 | It should_have_the_correct_proxy_port = () => 186 | fakeHttpRequestFactory.CreatedRequest.Proxy.GetProxy(faekUriToGetProxyFor).Port.ShouldEqual(fakeWebProxy.GetProxy(faekUriToGetProxyFor).Port); 187 | } 188 | } -------------------------------------------------------------------------------- /DynamicRest.UnitTests/Json/JsonResponsesTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using DynamicRest.Json; 3 | using Machine.Specifications; 4 | 5 | namespace DynamicRest.UnitTests.Json { 6 | 7 | [Subject(typeof(StandardResultBuilder))] 8 | public class When_a_response_contains_a_collection { 9 | 10 | static StandardResultBuilder _resultBuilder; 11 | static dynamic _response; 12 | 13 | Establish context = () => 14 | { 15 | _resultBuilder = new StandardResultBuilder(RestService.Json); 16 | }; 17 | 18 | Because the_response_is_created = () => { _response = _resultBuilder.CreateResult(_json); }; 19 | 20 | It should_contain_the_media_0_url = () => (_response.item.media[0].url as string).ShouldEqual("http://media0url"); 21 | It should_contain_the_media_1_url = () => (_response.item.media[1].url as string).ShouldEqual("http://media1url"); 22 | It should_contain_the_image_0_url = () => (_response.item.images.image[0].src as string).ShouldEqual("http://image0url"); 23 | It should_contain_the_image_1_url = () => (_response.item.images.image[1].src as string).ShouldEqual("http://image1url"); 24 | It should_contain_the_link_using_array_access = () => (_response.item.link[0] as string).ShouldEqual("http://www.bbc.co.uk/go/rss/int/news/-/news/world-africa-12673956"); 25 | 26 | static string _json = @"{ 27 | item: { 28 | title:'Gaddafi renews attack on rebels', 29 | description:'Forces loyal to Libyan leader Col Muammar Gaddafi launch fresh air strikes on the rebel-held Ras Lanuf, as they try to retake the oil-rich town.', 30 | link:[ 31 | 'http://www.bbc.co.uk/go/rss/int/news/-/news/world-africa-12673956' 32 | ], 33 | guid:'http://www.bbc.co.uk/news/world-africa-12673956', 34 | pubdate:'Tue, 08 Mar 2011 11:21:16 GMT', 35 | media:[ 36 | { url:'http://media0url' , src: 'dfsdfdfsfsd'}, 37 | { url:'http://media1url' , src: 'dfsdfdfsfsd'} 38 | ], 39 | images:{ 40 | image:[ 41 | { src:'http://image0url' }, 42 | { src:'http://image1url' } 43 | ] 44 | } 45 | } 46 | }"; 47 | } 48 | 49 | [Subject(typeof(JsonObject))] 50 | public class When_accessing_a_non_existing_property_on_a_json_property { 51 | 52 | static StandardResultBuilder _resultBuilder; 53 | static dynamic _response; 54 | 55 | Establish context = () => 56 | { 57 | _resultBuilder = new StandardResultBuilder(RestService.Json); 58 | _response = _resultBuilder.CreateResult(_json); 59 | }; 60 | 61 | Because the_response_is_created = () => { _thrownException = Catch.Exception(() => { var junk = _response.item.desc; }); }; 62 | 63 | It should_give_an_exception_with_a_sensible_error_message_from_json_object = () => 64 | _thrownException.Message.ShouldEqual("No member named 'desc' found in the response."); 65 | 66 | static string _json = @"{ 67 | item: { 68 | title:'Gaddafi renews attack on rebels', 69 | description:'Forces loyal to Libyan leader Col Muammar Gaddafi launch fresh air strikes on the rebel-held Ras Lanuf, as they try to retake the oil-rich town.', 70 | link:['http://www.bbc.co.uk/go/rss/int/news/-/news/world-africa-12673956'], 71 | guid:'http://www.bbc.co.uk/news/world-africa-12673956', 72 | pubdate:'Tue, 08 Mar 2011 11:21:16 GMT', 73 | media:[ 74 | { url:'http://media0url' , src: 'dfsdfdfsfsd'}, 75 | { url:'http://media1url' , src: 'dfsdfdfsfsd'} 76 | ], 77 | images:{ 78 | image:[ 79 | { src:'http://image0url' }, 80 | { src:'http://image1url' } 81 | ] 82 | } 83 | } 84 | }"; 85 | 86 | private static Exception _thrownException; 87 | } 88 | 89 | 90 | [Subject(typeof(JsonObject))] 91 | public class When_accessing_a_non_existing_property_on_a_json_array { 92 | 93 | static StandardResultBuilder _resultBuilder; 94 | static Exception _thrownException; 95 | static dynamic _response; 96 | static string _json; 97 | 98 | Establish context = () => 99 | { 100 | _json = @"{ 101 | item:{ 102 | images:[ 103 | { src:'http://image0url' }, 104 | { src:'http://image1url' } 105 | ] 106 | } 107 | }"; 108 | 109 | _resultBuilder = new StandardResultBuilder(RestService.Json); 110 | _response = _resultBuilder.CreateResult(_json); 111 | }; 112 | 113 | Because the_response_is_created = () => { _thrownException = Catch.Exception(() => { var junk = _response.item.images.doesntexist; }); }; 114 | 115 | It should_give_an_exception_with_a_sensible_error_message_from_json_object = () => 116 | _thrownException.Message.ShouldEqual("No member named 'doesntexist' found in the response."); 117 | } 118 | 119 | [Subject(typeof(StandardResultBuilder))] 120 | public class When_a_response_is_empty 121 | { 122 | 123 | static StandardResultBuilder _resultBuilder; 124 | static dynamic _response; 125 | 126 | Establish context = () => 127 | { 128 | _resultBuilder = new StandardResultBuilder(RestService.Json); 129 | }; 130 | 131 | Because the_response_is_created = () => { _response = _resultBuilder.CreateResult(_json); }; 132 | 133 | It should_return_empty_json_object = () => (_response as JsonObject).ShouldEqual(new JsonObject()); 134 | 135 | static string _json = @""; 136 | } 137 | } -------------------------------------------------------------------------------- /DynamicRest.UnitTests/RestClients/HttpVerbRequestBuilderTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using DynamicRest.HTTPInterfaces; 4 | using DynamicRest.UnitTests.TestDoubles; 5 | using Machine.Specifications; 6 | 7 | namespace DynamicRest.UnitTests.RestClients { 8 | 9 | [Subject(typeof(HttpVerbRequestBuilder))] 10 | public class When_i_use_the_client_to_put_a_request { 11 | private const string TestUri = "http://api.huddle.local/v2/tasks/123456"; 12 | private static dynamic client; 13 | private static FakeHttpRequestFactory requestFactory; 14 | private static string oAuth2Token = "{my_token}"; 15 | 16 | Establish context = () => 17 | { 18 | requestFactory = new FakeHttpRequestFactory(); 19 | 20 | var httpVerbRequestBuilder = new HttpVerbRequestBuilder(requestFactory) { Uri = TestUri }; 21 | httpVerbRequestBuilder.SetOAuth2AuthorizationHeader(oAuth2Token); 22 | client = new RestClient(httpVerbRequestBuilder, new ResponseProcessor(new StandardResultBuilder(RestService.Xml))); 23 | }; 24 | 25 | Because we_make_get_call_to_an_api_via_rest_client = () => client.Post(); 26 | 27 | It should_build_the_expected_uri = () => TestUri.ShouldEqual(requestFactory.CreatedRequest.RequestURI.ToString()); 28 | It should_set_the_correct_http_verb_on_the_request = () => requestFactory.CreatedRequest.HttpVerb.ShouldEqual(HttpVerb.POST); 29 | It should_set_the_correct_authorization_header_on_the_request = () => requestFactory.CreatedRequest.Headers[HttpRequestHeader.Authorization].ShouldEqual( 30 | $"Bearer {oAuth2Token}"); 31 | } 32 | 33 | [Subject(typeof(HttpVerbRequestBuilder))] 34 | public class When_i_use_the_client_to_get_a_request { 35 | private const string TestUri = "http://api.huddle.local/v2/tasks/123456"; 36 | private static dynamic client; 37 | private static FakeHttpRequestFactory requestFactory; 38 | 39 | Establish context = () => 40 | { 41 | requestFactory = new FakeHttpRequestFactory(); 42 | 43 | var httpVerbRequestBuilder = new HttpVerbRequestBuilder(requestFactory) { Uri = TestUri }; 44 | client = new RestClient(httpVerbRequestBuilder, new ResponseProcessor(new StandardResultBuilder(RestService.Xml))); 45 | }; 46 | 47 | Because we_make_get_call_to_an_api_via_rest_client = () => client.Get(); 48 | 49 | It should_build_the_expected_uri = () => TestUri.ShouldEqual(requestFactory.CreatedRequest.RequestURI.ToString()); 50 | It should_set_the_correct_http_verb_on_the_request = () => requestFactory.CreatedRequest.HttpVerb.ShouldEqual(HttpVerb.GET); 51 | } 52 | 53 | [Subject(typeof(HttpVerbRequestBuilder))] 54 | public class When_i_set_an_invalid_operation_on_the_client { 55 | private const string TestUri = "http://api.huddle.local/v2/tasks/123456"; 56 | private static dynamic client; 57 | private static dynamic exception; 58 | private static FakeHttpRequestFactory requestFactory; 59 | 60 | Establish context = () => 61 | { 62 | requestFactory = new FakeHttpRequestFactory(); 63 | 64 | var httpVerbRequestBuilder = new HttpVerbRequestBuilder(requestFactory) { Uri = TestUri }; 65 | client = new RestClient(httpVerbRequestBuilder, new ResponseProcessor(new StandardResultBuilder(RestService.Xml))); 66 | }; 67 | 68 | Because we_make_get_call_to_an_api_via_rest_client = () => exception = Catch.Exception(() => client.NotHttp()); 69 | 70 | It should_throw_an_exception = () => ((Exception) exception).ShouldBeAssignableTo(); 71 | } 72 | 73 | [Subject(typeof(HttpVerbRequestBuilder))] 74 | public class When_i_set_an_xml_body_on_the_request { 75 | private const string ContentType = @"application\xml+"; 76 | private static FakeHttpRequestFactory requestFactory; 77 | private static dynamic client; 78 | private static string requestBody = ""; 79 | 80 | Establish context = () => 81 | { 82 | requestFactory = new FakeHttpRequestFactory(); 83 | 84 | var httpVerbRequestBuilder = new HttpVerbRequestBuilder(requestFactory) { Body = requestBody, ContentType = ContentType, Uri = "http://api.huddle.com" }; 85 | client = new RestClient(httpVerbRequestBuilder, new ResponseProcessor(new StandardResultBuilder(RestService.Xml))); 86 | }; 87 | 88 | Because we_set_an_xml_body_on_the_test = () => client.Post(); 89 | 90 | It should_set_the_body_of_the_request = () => requestFactory.CreatedRequest.RequestBody.ShouldEqual(requestBody); 91 | It should_set_the_content_type_of_the_request = () => requestFactory.CreatedRequest.ContentType.ShouldEqual(ContentType); 92 | } 93 | 94 | [Subject(typeof(HttpVerbRequestBuilder))] 95 | public class When_the_autofollow_option_is_set_to_true { 96 | private const string TestUri = "http://api.huddle.local/v2/tasks/123456"; 97 | private static dynamic client; 98 | private static FakeHttpRequestFactory requestFactory; 99 | 100 | Establish context = () => 101 | { 102 | requestFactory = new FakeHttpRequestFactory(); 103 | 104 | var httpVerbRequestBuilder = new HttpVerbRequestBuilder(requestFactory) { Uri = TestUri, AllowAutoRedirect = true }; 105 | client = new RestClient(httpVerbRequestBuilder, new ResponseProcessor(new StandardResultBuilder(RestService.Xml))); 106 | }; 107 | 108 | Because we_make_get_call_to_an_api_via_rest_client = () => client.Get(); 109 | 110 | It should_set_the_auto_follow_option_to_true_on_the_request = () => requestFactory.CreatedRequest.AllowAutoRedirect.ShouldEqual(true); 111 | } 112 | 113 | [Subject(typeof(HttpVerbRequestBuilder))] 114 | public class When_the_autofollow_option_is_set_to_false { 115 | private const string TestUri = "http://api.huddle.local/v2/tasks/123456"; 116 | private static dynamic client; 117 | private static FakeHttpRequestFactory requestFactory; 118 | 119 | Establish context = () => 120 | { 121 | requestFactory = new FakeHttpRequestFactory(); 122 | 123 | var httpVerbRequestBuilder = new HttpVerbRequestBuilder(requestFactory) { Uri = TestUri, AllowAutoRedirect = false }; 124 | client = new RestClient(httpVerbRequestBuilder, new ResponseProcessor(new StandardResultBuilder(RestService.Xml))); 125 | }; 126 | 127 | Because we_make_get_call_to_an_api_via_rest_client = () => client.Get(); 128 | 129 | It should_set_the_auto_follow_option_to_false_on_the_request = () => requestFactory.CreatedRequest.AllowAutoRedirect.ShouldEqual(false); 130 | } 131 | 132 | [Subject(typeof(HttpVerbRequestBuilder))] 133 | public class When_the_autofollow_option_is_not_explicitly_set { 134 | private const string TestUri = "http://api.huddle.local/v2/tasks/123456"; 135 | private static dynamic client; 136 | private static FakeHttpRequestFactory requestFactory; 137 | 138 | Establish context = () => 139 | { 140 | requestFactory = new FakeHttpRequestFactory(); 141 | 142 | var httpVerbRequestBuilder = new HttpVerbRequestBuilder(requestFactory) { Uri = TestUri }; 143 | client = new RestClient(httpVerbRequestBuilder, new ResponseProcessor(new StandardResultBuilder(RestService.Xml))); 144 | }; 145 | 146 | Because we_make_get_call_to_an_api_via_rest_client = () => client.Get(); 147 | 148 | It should_default_the_auto_follow_option_to_false_on_the_request = () => requestFactory.CreatedRequest.AllowAutoRedirect.ShouldEqual(false); 149 | } 150 | 151 | [Subject(typeof(HttpVerbRequestBuilder))] 152 | public class When_i_use_the_client_to_delete_a_request 153 | { 154 | private const string TestUri = "http://api.huddle.local/v2/tasks/123456"; 155 | private static dynamic client; 156 | private static FakeHttpRequestFactory requestFactory; 157 | private static string oAuth2Token = "{my_token}"; 158 | 159 | Establish context = () => 160 | { 161 | requestFactory = new FakeHttpRequestFactory(); 162 | 163 | var httpVerbRequestBuilder = new HttpVerbRequestBuilder(requestFactory) { Uri = TestUri }; 164 | httpVerbRequestBuilder.SetOAuth2AuthorizationHeader(oAuth2Token); 165 | client = new RestClient(httpVerbRequestBuilder, new ResponseProcessor(new StandardResultBuilder(RestService.Xml))); 166 | }; 167 | 168 | Because we_make_get_call_to_an_api_via_rest_client = () => client.Delete(); 169 | 170 | It should_build_the_expected_uri = () => TestUri.ShouldEqual(requestFactory.CreatedRequest.RequestURI.ToString()); 171 | It should_set_the_correct_http_verb_on_the_request = () => requestFactory.CreatedRequest.HttpVerb.ShouldEqual(HttpVerb.DELETE); 172 | It should_set_the_correct_authorization_header_on_the_request = () => requestFactory.CreatedRequest.Headers[HttpRequestHeader.Authorization].ShouldEqual(string.Format("Bearer {0}", oAuth2Token)); 173 | } 174 | 175 | } -------------------------------------------------------------------------------- /DynamicRest.UnitTests/RestClients/TemplatedUriRequestBuilderTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using DynamicRest.Json; 3 | using DynamicRest.UnitTests.TestDoubles; 4 | using Machine.Specifications; 5 | 6 | namespace DynamicRest.UnitTests.RestClients { 7 | 8 | [Subject(typeof(TemplatedUriRequestBuilder))] 9 | public class When_using_a_templated_uri_with_an_operation { 10 | private const string AmazonUriTemplate = "http://ecs.amazonaws.com/onca/xml?Service=AWSECommerceService&Version=2009-03-31&Operation={operation}&AssociateTag=myamzn-20"; 11 | private const string ExpectedUri = "http://ecs.amazonaws.com/onca/xml?Service=AWSECommerceService&Version=2009-03-31&Operation=ItemSearch&AssociateTag=myamzn-20"; 12 | private static dynamic client; 13 | private static FakeHttpRequestFactory requestFactory; 14 | 15 | Establish context = () => 16 | { 17 | requestFactory = new FakeHttpRequestFactory(); 18 | var templatedUriRequestBuilder = new TemplatedUriRequestBuilder(requestFactory) { Uri = AmazonUriTemplate }; 19 | client = new RestClient(templatedUriRequestBuilder, new ResponseProcessor(new StandardResultBuilder(RestService.Xml))); 20 | }; 21 | 22 | Because we_make_get_call_to_an_api_via_rest_client = () => client.ItemSearch(); 23 | 24 | It should_merge_operationname_parameters_into_the_uri_template = () => ExpectedUri.ShouldEqual(requestFactory.CreatedRequest.RequestURI.ToString()); 25 | } 26 | 27 | [Subject(typeof(TemplatedUriRequestBuilder))] 28 | public class When_using_a_templated_uri_with_an_operation_and_options { 29 | private const string AmazonUriTemplate = "http://ecs.amazonaws.com/onca/xml?Service=AWSECommerceService&Version=2009-03-31&Operation={operation}&AssociateTag=myamzn-20"; 30 | private const string ExpectedUri = "http://ecs.amazonaws.com/onca/xml?Service=AWSECommerceService&Version=2009-03-31&Operation=ItemSearch&AssociateTag=myamzn-20&SearchIndex=Books&Keywords=Dynamic+Programming"; 31 | private static dynamic client; 32 | private static dynamic searchOptions; 33 | private static FakeHttpRequestFactory requestFactory; 34 | 35 | Establish context = () => 36 | { 37 | requestFactory = new FakeHttpRequestFactory(); 38 | var templatedUriRequestBuilder = new TemplatedUriRequestBuilder(requestFactory) { Uri = AmazonUriTemplate }; 39 | client = new RestClient(templatedUriRequestBuilder, new ResponseProcessor(new StandardResultBuilder(RestService.Xml))); 40 | 41 | searchOptions = new JsonObject(); 42 | searchOptions.SearchIndex = "Books"; 43 | searchOptions.Keywords = "Dynamic Programming"; 44 | }; 45 | 46 | Because we_make_get_call_to_an_api_via_rest_client = () => client.ItemSearch(searchOptions); 47 | 48 | It should_merge_operation_and_option_parameters_into_the_uri_template = () => ExpectedUri.ShouldEqual(requestFactory.CreatedRequest.RequestURI.ToString()); 49 | } 50 | 51 | [Subject(typeof(TemplatedUriRequestBuilder))] 52 | public class When_using_a_templated_uri_with_ids_in_uri { 53 | private const string BingSearchUri = "http://api.bing.net/json.aspx?AppId={appID}&Version=2.2&Market=en-US"; 54 | private const string ExpectedUri = "http://api.bing.net/json.aspx?AppId=12345&Version=2.2&Market=en-US"; 55 | private const string BingApiKey = "12345"; 56 | private static FakeHttpRequestFactory requestFactory; 57 | private static dynamic client; 58 | 59 | Establish context = () => 60 | { 61 | requestFactory = new FakeHttpRequestFactory(); 62 | var templatedUriRequestBuilder = new TemplatedUriRequestBuilder(requestFactory) { Uri = BingSearchUri }; 63 | client = new RestClient(templatedUriRequestBuilder, new ResponseProcessor(new StandardResultBuilder(RestService.Json))); 64 | client.appID = BingApiKey; 65 | }; 66 | 67 | Because we_make_a_call_to_an_api_via_rest_client = () => client.Invoke(); 68 | 69 | It should_merge_properties_into_the_uri_template = () => ExpectedUri.ShouldEqual(requestFactory.CreatedRequest.RequestURI.ToString()); 70 | } 71 | 72 | [Subject(typeof(TemplatedUriRequestBuilder))] 73 | public class When_using_a_templated_uri_with_ids_and_missing_ids { 74 | private static FakeHttpRequestFactory requestFactory; 75 | private static dynamic bing; 76 | private const string BingSearchUri = "http://api.bing.net/json.aspx?AppId={appID}&Version=2.2&Market=en-US"; 77 | private const string ExpectedUri = "http://api.bing.net/json.aspx?AppId=12345&Version=2.2&Market=en-US"; 78 | private const string BingApiKey = "12345"; 79 | private static Exception exception; 80 | 81 | Establish context = () => 82 | { 83 | requestFactory = new FakeHttpRequestFactory(); 84 | var templatedUriRequestBuilder = new TemplatedUriRequestBuilder(requestFactory) { Uri = BingSearchUri }; 85 | bing = new RestClient(templatedUriRequestBuilder, new ResponseProcessor(new StandardResultBuilder(RestService.Json))); 86 | }; 87 | 88 | Because we_make_a_call_to_an_api_via_rest_client = () => exception = Catch.Exception(() => bing.Invoke()); 89 | 90 | It should_throw_an_exception = () => exception.ShouldBeAssignableTo(typeof (ArgumentException)); 91 | 92 | It should_contain_helpful_error_message = () => exception.Message.ShouldEqual("You are missing one or more expected template parameters in the uri: http://api.bing.net/json.aspx?AppId={appID}&Version=2.2&Market=en-US"); 93 | } 94 | 95 | [Subject(typeof(TemplatedUriRequestBuilder))] 96 | public class When_using_a_templated_uri_with_ids_in_uri_and_options { 97 | private static FakeHttpRequestFactory requestFactory; 98 | private static dynamic bing; 99 | private static dynamic searchOptions; 100 | private const string BingSearchUri = "http://api.bing.net/json.aspx?AppId={appID}&Version=2.2&Market=en-US"; 101 | private const string ExpectedUri = "http://api.bing.net/json.aspx?AppId=12345&Version=2.2&Market=en-US&Query=seattle&Sources=Web+Image&Web.Count=4&Image.Count=2"; 102 | private const string BingApiKey = "12345"; 103 | 104 | Establish context = () => 105 | { 106 | requestFactory = new FakeHttpRequestFactory(); 107 | var templatedUriRequestBuilder = new TemplatedUriRequestBuilder(requestFactory) { Uri = BingSearchUri }; 108 | bing = new RestClient(templatedUriRequestBuilder, new ResponseProcessor(new StandardResultBuilder(RestService.Json))); 109 | bing.appID = BingApiKey; 110 | 111 | searchOptions = new JsonObject(); 112 | searchOptions.Query = "seattle"; 113 | searchOptions.Sources = new[] {"Web", "Image"}; 114 | searchOptions.Web = new JsonObject("Count", 4); 115 | searchOptions.Image = new JsonObject("Count", 2); 116 | }; 117 | 118 | Because we_make_a_call_to_an_api_via_rest_client = () => bing.Invoke(searchOptions); 119 | 120 | It should_merge_properties_into_the_uri_template = () => ExpectedUri.ShouldEqual(requestFactory.CreatedRequest.RequestURI.ToString()); 121 | } 122 | 123 | [Subject(typeof(TemplatedUriRequestBuilder))] 124 | public class When_using_a_templated_uri_with_ids_in_uri_and_operation_and_options { 125 | private static dynamic flickr; 126 | private static dynamic searchOptions; 127 | private static FakeHttpRequestFactory requestFactory; 128 | private const string FlickrSearchApi = "http://api.flickr.com/services/rest/?method=flickr.{operation}&api_key={apiKey}&format=json&nojsoncallback=1"; 129 | private const string ExpectedUri = "http://api.flickr.com/services/rest/?method=flickr.Photos.Search&api_key=123456&format=json&nojsoncallback=1"; 130 | private const string FlickrApiKey = "123456"; 131 | 132 | private Establish context = () => 133 | { 134 | requestFactory = new FakeHttpRequestFactory(); 135 | var templatedUriRequestBuilder = new TemplatedUriRequestBuilder(requestFactory) { Uri = FlickrSearchApi }; 136 | flickr = new RestClient(templatedUriRequestBuilder, new ResponseProcessor(new StandardResultBuilder(RestService.Json))); 137 | flickr.apiKey = FlickrApiKey; 138 | 139 | dynamic searchOptions = new JsonObject(); 140 | searchOptions.tags = "seattle"; 141 | searchOptions.per_page = 4; 142 | }; 143 | 144 | Because we_make_a_call_to_an_api_via_the_rest_client = () => flickr.Photos.Search(searchOptions); 145 | 146 | It should_merge_properties_into_the_uri_template = () => ExpectedUri.ShouldEqual(requestFactory.CreatedRequest.RequestURI.ToString()); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /DynamicRest.UnitTests/Simulators/ResponseSimulatorTests.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Runtime.Serialization; 3 | using DynamicRest.Fluent; 4 | using DynamicRest.UnitTests.TestDoubles; 5 | using Machine.Specifications; 6 | 7 | namespace DynamicRest.UnitTests.Simulators 8 | { 9 | [Subject(typeof(RestClientBuilder))] 10 | public class When_a_rest_client_is_created_with_a_rest_client_builder_passing_in_an_expected_response 11 | { 12 | static IRestClientBuilder restClientBuilder; 13 | private static RestOperation _response; 14 | static dynamic builtClient; 15 | 16 | static FakeHttpRequestFactory fakeHttpRequestFactory; 17 | 18 | Establish context = () => 19 | { 20 | restClientBuilder = new RestClientBuilder(); 21 | }; 22 | 23 | Because the_rest_client_is_built_and_executed = () => 24 | { 25 | fakeHttpRequestFactory = new FakeHttpRequestFactory(); 26 | var responseContent = new FakeHttpWebResponseWrapper.ResponseContent("application/vnd.data+xml"); 27 | resultAsString = "Test"; 28 | responseContent.SetContent(resultAsString); 29 | fakeHttpRequestFactory.SetResponse(new FakeHttpWebResponseWrapper 30 | { 31 | Headers = new WebHeaderCollection(), 32 | StatusCode = HttpStatusCode.OK, 33 | Content = responseContent 34 | 35 | }); 36 | 37 | _response = restClientBuilder 38 | .WithRequestBuilder(new HttpVerbRequestBuilder(fakeHttpRequestFactory)) 39 | .WithUri("http://www.google.com") 40 | .Build() 41 | .Get(); 42 | }; 43 | 44 | It should_return_a_value_built_from_interpreting_the_response = () =>ShouldExtensionMethods.ShouldEqual(_response.Result.message.Value, "Test"); 45 | It should_return_the_original_string_in_the_response = () => ShouldExtensionMethods.ShouldEqual(_response.ResponseText, resultAsString); 46 | 47 | static string resultAsString; 48 | } 49 | 50 | 51 | [Subject(typeof(RestClientBuilder))] 52 | public class When_a_rest_client_is_created_with_a_rest_client_builder_passing_in_a_templated_response 53 | { 54 | static IRestClientBuilder restClientBuilder; 55 | private static RestOperation _response; 56 | static dynamic builtClient; 57 | 58 | static FakeHttpRequestFactory fakeHttpRequestFactory; 59 | 60 | Establish context = () => 61 | { 62 | restClientBuilder = new RestClientBuilder(); 63 | }; 64 | 65 | Because the_rest_client_is_built_and_executed = () => 66 | { 67 | fakeHttpRequestFactory = new FakeHttpRequestFactory(); 68 | var responseContent = new FakeHttpWebResponseWrapper.ResponseContent("application/vnd.data+xml"); 69 | responseContent.SetContent(new Result { Message = "Test" }); 70 | fakeHttpRequestFactory.SetResponse(new FakeHttpWebResponseWrapper 71 | { 72 | Headers = new WebHeaderCollection(), 73 | StatusCode = HttpStatusCode.OK, 74 | Content = responseContent 75 | }); 76 | 77 | _response = restClientBuilder 78 | .WithRequestBuilder(new HttpVerbRequestBuilder(fakeHttpRequestFactory)) 79 | .WithUri("http://www.google.com") 80 | .Build() 81 | .Get(); 82 | }; 83 | 84 | It should_return_a_value_built_from_interpreting_the_response = () => ShouldExtensionMethods.ShouldEqual(_response.Result.Message.Value, "Test"); 85 | } 86 | public class Result 87 | { 88 | public string Message { get; set; } 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /DynamicRest.UnitTests/TestDoubles/FakeHttpRequestFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using DynamicRest.HTTPInterfaces; 3 | 4 | namespace DynamicRest.UnitTests.TestDoubles { 5 | 6 | internal class FakeHttpRequestFactory : IHttpRequestFactory { 7 | private IHttpResponse _httpResponse; 8 | 9 | internal FakeHttpWebRequestWrapper CreatedRequest {get; set; } 10 | 11 | public IHttpRequest Create(Uri uri) { 12 | CreatedRequest = new FakeHttpWebRequestWrapper(uri); 13 | if (_httpResponse != null) 14 | { 15 | CreatedRequest.Response = _httpResponse; 16 | } 17 | return CreatedRequest; 18 | } 19 | 20 | public void SetResponse(IHttpResponse httpResponse) 21 | { 22 | _httpResponse = httpResponse; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /DynamicRest.UnitTests/TestDoubles/FakeHttpWebRequestWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | using DynamicRest.HTTPInterfaces; 5 | 6 | namespace DynamicRest.UnitTests.TestDoubles 7 | { 8 | public class FakeHttpWebRequestWrapper : IHttpRequest { 9 | 10 | private readonly Uri _uri; 11 | private WebHeaderCollection _headers; 12 | private IHttpResponse _response; 13 | 14 | public FakeHttpWebRequestWrapper(Uri uri) { 15 | _uri = uri; 16 | } 17 | 18 | public Uri RequestURI { 19 | get { return _uri; } 20 | } 21 | 22 | public string Accept { get; set; } 23 | public string ContentType { get; private set; } 24 | public bool AllowAutoRedirect { get; set; } 25 | public string UserAgent { get; set; } 26 | public int Timeout { get; set; } 27 | public IWebProxy Proxy { get; set; } 28 | public HttpVerb HttpVerb { get; set; } 29 | 30 | public WebHeaderCollection Headers { 31 | get { 32 | return _headers; 33 | } 34 | } 35 | 36 | public string RequestBody { get; private set; } 37 | 38 | public IHttpResponse Response 39 | { 40 | set { 41 | _response = value; 42 | } 43 | } 44 | 45 | public void AddCredentials(ICredentials credentials) { 46 | 47 | } 48 | 49 | public void AddHeaders(WebHeaderCollection headers) { 50 | _headers = headers; 51 | } 52 | 53 | public void AddRequestBody(string contentType, string content) { 54 | RequestBody = content; 55 | ContentType = contentType; 56 | } 57 | 58 | public void BeginGetResponse(Action action, object asyncRequest) { 59 | 60 | } 61 | 62 | public IHttpResponse EndGetResponse(object asyncRequest) { 63 | return _response ?? new FakeHttpWebResponseWrapper(); 64 | } 65 | 66 | public IHttpResponse GetResponse() { 67 | return _response ?? new FakeHttpWebResponseWrapper(); 68 | } 69 | 70 | public IHttpResponse GetResponse(int timeout) { 71 | return _response ?? new FakeHttpWebResponseWrapper(); 72 | } 73 | 74 | public Stream GetRequestStream() { 75 | return new MemoryStream(); 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /DynamicRest.UnitTests/TestDoubles/FakeHttpWebResponseWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | using System.Runtime.Serialization; 5 | using System.Text; 6 | using System.Xml.Serialization; 7 | using DynamicRest.HTTPInterfaces; 8 | 9 | namespace DynamicRest.UnitTests.TestDoubles 10 | { 11 | public class FakeHttpWebResponseWrapper : IHttpResponse, IDisposable { 12 | private WebHeaderCollection _webHeaderCollection = new WebHeaderCollection(); 13 | private HttpStatusCode _httpStatusCode = HttpStatusCode.OK; 14 | private Stream _responseStream; 15 | private ResponseContent _responseContent = new ResponseContent(); 16 | private long _contentLength = 0; 17 | 18 | public ResponseContent Content 19 | { 20 | set { _responseContent = value; } 21 | } 22 | 23 | public string ContentEncoding 24 | { 25 | get { return _responseContent.ContentEncoding; } 26 | } 27 | 28 | public long ContentLength 29 | { 30 | get { return _responseContent.ContentLength; } 31 | } 32 | 33 | public WebHeaderCollection Headers { 34 | get { return _webHeaderCollection; } 35 | set { _webHeaderCollection = value; } 36 | } 37 | 38 | public HttpStatusCode StatusCode { 39 | get { return _httpStatusCode; } 40 | set { _httpStatusCode = value; } 41 | } 42 | 43 | public string StatusDescription { 44 | get { return _httpStatusCode.ToString(); } 45 | } 46 | 47 | public Stream GetResponseStream() 48 | { 49 | _responseStream = _responseContent.GetResponseStream(); 50 | return _responseStream; 51 | } 52 | 53 | public void Dispose() 54 | { 55 | if (_responseStream != null) 56 | _responseStream.Close(); 57 | } 58 | 59 | 60 | public class ResponseContent{ 61 | private string _rawContent = string.Empty; 62 | private readonly string _contentEncoding = "text/plain"; 63 | private ResponseContentType _contentType; 64 | private object _graph; 65 | private Type _graphRootType; 66 | private long _contentLength = 0; 67 | 68 | public ResponseContent(){} 69 | 70 | public ResponseContent(string contentEncoding) 71 | { 72 | _contentEncoding = contentEncoding; 73 | } 74 | 75 | public long ContentLength 76 | { 77 | get { return _contentLength; } 78 | } 79 | 80 | public string ContentEncoding 81 | { 82 | get { return _contentEncoding; } 83 | } 84 | 85 | public void SetContent(string content){ 86 | _contentType = ResponseContentType.Raw; 87 | _rawContent = content; 88 | } 89 | 90 | public void SetContent(T responseContent) 91 | { 92 | _graph = responseContent; 93 | _graphRootType = typeof (T); 94 | _contentType = ResponseContentType.SerializedObject; 95 | } 96 | 97 | public Stream GetResponseStream() 98 | { 99 | if (_contentType == ResponseContentType.Raw){ 100 | return GetRawContentResponseStream(); 101 | } 102 | 103 | return GetSerializedObjectResponseStream(); 104 | } 105 | 106 | private Stream GetSerializedObjectResponseStream() 107 | { 108 | var serializer = new XmlSerializer(_graphRootType); 109 | Stream stream = new MemoryStream(); 110 | serializer.Serialize(stream, _graph); 111 | 112 | _contentLength = stream.Length; 113 | 114 | stream.Position = 0; 115 | return stream; 116 | } 117 | 118 | private Stream GetRawContentResponseStream() 119 | { 120 | byte[] bytes = GetBytes(); 121 | 122 | _contentLength = bytes.Length; 123 | 124 | var responseStream = new MemoryStream(); 125 | 126 | responseStream.Write(bytes, 0, bytes.Length); 127 | 128 | responseStream.Seek(0, 0); 129 | 130 | return responseStream; 131 | } 132 | 133 | internal byte[] GetBytes() 134 | { 135 | return Encoding.UTF8.GetBytes(_rawContent); 136 | } 137 | 138 | internal enum ResponseContentType{ 139 | Raw, 140 | SerializedObject 141 | } 142 | } 143 | } 144 | } -------------------------------------------------------------------------------- /DynamicRest.UnitTests/TestDoubles/FakeWebProxy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | 4 | namespace DynamicRest.UnitTests.TestDoubles 5 | { 6 | internal class FakeWebProxy : IWebProxy 7 | { 8 | readonly Uri _proxyUri; 9 | 10 | public FakeWebProxy(Uri proxyUri) 11 | { 12 | this._proxyUri = proxyUri; 13 | } 14 | 15 | public Uri GetProxy(Uri destination) 16 | { 17 | return this._proxyUri; 18 | } 19 | 20 | public bool IsBypassed(Uri host) 21 | { 22 | return true; 23 | } 24 | 25 | public ICredentials Credentials { get; set; } 26 | } 27 | } -------------------------------------------------------------------------------- /DynamicRest.UnitTests/Xml/XmlResponsesTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using DynamicRest.Xml; 3 | using Machine.Specifications; 4 | 5 | namespace DynamicRest.UnitTests.Xml 6 | { 7 | [Subject(typeof(StandardResultBuilder))] 8 | public class When_a_response_contains_a_collection { 9 | 10 | static StandardResultBuilder _resultBuilder; 11 | static dynamic _response; 12 | 13 | Establish context = () => 14 | { 15 | _resultBuilder = new StandardResultBuilder(RestService.Xml); 16 | }; 17 | 18 | Because the_response_is_created = () => { _response = _resultBuilder.CreateResult(_xml); }; 19 | 20 | It should_contain_the_media_0_url = () => (_response.item.media[0].url as string).ShouldEqual("http://media0url"); 21 | It should_contain_the_media_1_url = () => (_response.item.media[1].url as string).ShouldEqual("http://media1url"); 22 | It should_contain_the_image_0_url = () => (_response.item.images.image[0].src as string).ShouldEqual("http://image0url"); 23 | It should_contain_the_image_1_url = () => (_response.item.images.image[1].src as string).ShouldEqual("http://image1url"); 24 | It should_contain_the_link_using_array_access = () => (_response.item.link[0].Value as string).ShouldEqual("http://www.bbc.co.uk/go/rss/int/news/-/news/world-africa-12673956"); 25 | It should_contain_the_attachment_title = () => (_response.item.attachments.attachment[0].title.Value as string).ShouldEqual("this is the title"); 26 | It should_contain_the_numbers_of_attachments = () => ((int)_response.item.attachments.Count).ShouldEqual(1); 27 | 28 | It should_contain_the_pubdate = 29 | () => ((DateTime)_response.item.pubDate).ShouldEqual(new DateTime(2011, 3, 8, 11, 21, 16)); 30 | 31 | It should_work_when_a_refence_is_used_as_an_array = () => 32 | { 33 | var linkAsArray = _response.item.link; 34 | ((string)linkAsArray[0].Value).ShouldEqual("http://www.bbc.co.uk/go/rss/int/news/-/news/world-africa-12673956"); 35 | }; 36 | 37 | static string _xml = @" 38 | 39 | 40 | Gaddafi renews attack on rebels 41 | 5 42 | Forces loyal to Libyan leader Col Muammar Gaddafi launch fresh air strikes on the rebel-held Ras Lanuf, as they try to retake the oil-rich town. 43 | http://www.bbc.co.uk/go/rss/int/news/-/news/world-africa-12673956 44 | http://www.bbc.co.uk/go/rss/int/news/-/news/world-africa-12673956 45 | http://www.bbc.co.uk/news/world-africa-12673956 46 | Tue, 08 Mar 2011 11:21:16 GMT 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | this is the title 56 | 57 | 58 | 59 | "; 60 | } 61 | 62 | [Subject(typeof(XmlNode))] 63 | public class When_accessing_a_non_existing_element { 64 | 65 | static StandardResultBuilder _resultBuilder; 66 | static dynamic _response; 67 | 68 | Establish context = () => 69 | { 70 | _resultBuilder = new StandardResultBuilder(RestService.Xml); 71 | _response = _resultBuilder.CreateResult(_xml); 72 | }; 73 | 74 | private Because the_response_is_created = () => _thrownException = Catch.Exception(() => { var junk = _response.item.desc; }); 75 | 76 | It should_work_when_a_refence_is_used_as_an_array = () => 77 | _thrownException.Message.ShouldEqual("No element or attribute named 'desc' found in the response."); 78 | 79 | static string _xml = @" 80 | 81 | 82 | Gaddafi renews attack on rebels 83 | Forces loyal to Libyan leader Col Muammar Gaddafi launch fresh air strikes on the rebel-held Ras Lanuf, as they try to retake the oil-rich town. 84 | http://www.bbc.co.uk/go/rss/int/news/-/news/world-africa-12673956 85 | http://www.bbc.co.uk/go/rss/int/news/-/news/world-africa-12673956 86 | http://www.bbc.co.uk/news/world-africa-12673956 87 | Tue, 08 Mar 2011 11:21:16 GMT 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | this is the title 97 | 98 | 99 | 100 | "; 101 | 102 | private static Exception _thrownException; 103 | } 104 | 105 | 106 | namespace When_casting_strings_to_datetimes 107 | { 108 | [Subject(typeof(XmlString))] 109 | public class When_a_string_is_a_valid_date 110 | { 111 | Establish context = () => The_string = new XmlString("2011-03-08T11:21:16Z"); 112 | 113 | Because we_parse_as_a_date = () => The_result = (DateTime)The_string; 114 | 115 | It should_have_the_expected_value = () => The_result.ShouldEqual(new DateTime(2011, 3, 8, 11, 21, 16)); 116 | 117 | private static XmlString The_string; 118 | private static DateTime The_result; 119 | } 120 | 121 | [Subject(typeof(XmlString))] 122 | public class When_parsing_fails 123 | { 124 | Establish context = () => The_string = new XmlString("I am not a valid string"); 125 | 126 | Because we_parse_as_a_date = () => The_result = Catch.Exception(() => Console.Write((DateTime)The_string)); 127 | 128 | private It should_have_the_expected_value = () => The_result.ShouldBeAssignableTo(); 129 | 130 | private static XmlString The_string; 131 | private static Exception The_result; 132 | } 133 | } 134 | 135 | namespace When_casting_strings_to_integers 136 | { 137 | [Subject(typeof(XmlString))] 138 | public class When_a_string_is_a_valid_int 139 | { 140 | Establish context = () => The_string = new XmlString("8787645"); 141 | 142 | Because we_parse_as_an_int = () => The_result = (int)The_string; 143 | 144 | It should_have_the_expected_value = () => The_result.ShouldEqual(8787645); 145 | 146 | private static XmlString The_string; 147 | private static int The_result; 148 | } 149 | 150 | [Subject(typeof(XmlString))] 151 | public class When_a_string_is_an_overflow 152 | { 153 | Establish context = () => The_string = new XmlString( (((long)int.MaxValue) + 1).ToString()); 154 | 155 | Because we_parse_as_an_int = () => The_result = Catch.Exception(() => Console.Write((int)The_string)); 156 | 157 | private It should_have_the_expected_value = () => The_result.ShouldBeAssignableTo(); 158 | 159 | private static XmlString The_string; 160 | private static Exception The_result; 161 | } 162 | 163 | 164 | [Subject(typeof(XmlString))] 165 | public class When_parsing_fails 166 | { 167 | Establish context = () => The_string = new XmlString("I am not a valid string"); 168 | 169 | Because we_parse_as_an_int= () => The_result = Catch.Exception(() => Console.Write((int)The_string)); 170 | 171 | private It should_have_the_expected_value = () => The_result.ShouldBeAssignableTo(); 172 | 173 | private static XmlString The_string; 174 | private static Exception The_result; 175 | } 176 | } 177 | 178 | namespace When_casting_strings_to_longs 179 | { 180 | [Subject(typeof(XmlString))] 181 | public class When_a_string_is_a_valid_long 182 | { 183 | Establish context = () => The_string = new XmlString("8787645"); 184 | 185 | Because we_parse_as_a_long = () => The_result = (long)The_string; 186 | 187 | It should_have_the_expected_value = () => The_result.ShouldEqual(8787645); 188 | 189 | private static XmlString The_string; 190 | private static long The_result; 191 | } 192 | 193 | [Subject(typeof(XmlString))] 194 | public class When_a_string_is_an_overflow 195 | { 196 | Establish context = () => The_string = new XmlString((long.MaxValue)+"1"); 197 | 198 | Because we_parse_as_a_long = () => The_result = Catch.Exception(() => Console.Write((long)The_string)); 199 | 200 | private It should_have_the_expected_value = () => The_result.ShouldBeAssignableTo(); 201 | 202 | private static XmlString The_string; 203 | private static Exception The_result; 204 | } 205 | 206 | 207 | [Subject(typeof(XmlString))] 208 | public class When_parsing_fails 209 | { 210 | Establish context = () => The_string = new XmlString("I am not a valid string"); 211 | 212 | Because we_parse_as_a_long = () => The_result = Catch.Exception(() => Console.Write((long)The_string)); 213 | 214 | private It should_have_the_expected_value = () => The_result.ShouldBeAssignableTo(); 215 | 216 | private static XmlString The_string; 217 | private static Exception The_result; 218 | } 219 | } 220 | 221 | namespace When_casting_strings_to_bools 222 | { 223 | public class When_parsing_the_empty_string 224 | { 225 | Because we_parse_string_empty = 226 | () => Result = Catch.Exception(() => Console.Write((bool) new XmlString(string.Empty))); 227 | 228 | It should_throw_format_exception = () => Result.ShouldBeAssignableTo(); 229 | 230 | protected static Exception Result { get; set; } 231 | } 232 | 233 | public class When_parsing_the_zero_string 234 | { 235 | Because we_parse_string_empty = 236 | () => Result = (bool) new XmlString("0"); 237 | 238 | It should_be_false = () => Result.ShouldBeFalse(); 239 | 240 | protected static bool Result { get; set; } 241 | } 242 | 243 | public class When_parsing_negative_one 244 | { 245 | Because we_parse_string_empty = 246 | () => Result = (bool)new XmlString("-1"); 247 | 248 | It should_be_false = () => Result.ShouldBeFalse(); 249 | 250 | protected static bool Result { get; set; } 251 | } 252 | 253 | public class When_parsing_one 254 | { 255 | Because we_parse_string_empty = 256 | () => Result = (bool)new XmlString("1"); 257 | 258 | It should_be_false = () => Result.ShouldBeTrue(); 259 | 260 | protected static bool Result { get; set; } 261 | } 262 | 263 | public class When_parsing_true 264 | { 265 | Because we_parse_string_empty = 266 | () => Result = (bool)new XmlString("true"); 267 | 268 | It should_be_false = () => Result.ShouldBeTrue(); 269 | 270 | protected static bool Result { get; set; } 271 | } 272 | 273 | public class When_parsing_false 274 | { 275 | Because we_parse_string_empty = 276 | () => Result = (bool)new XmlString("false"); 277 | 278 | It should_be_false = () => Result.ShouldBeFalse(); 279 | 280 | protected static bool Result { get; set; } 281 | } 282 | 283 | public class When_parsing_weird_casing 284 | { 285 | Because we_parse_string_empty = 286 | () => Result = (bool)new XmlString("tRUe"); 287 | 288 | It should_be_false = () => Result.ShouldBeTrue(); 289 | 290 | protected static bool Result { get; set; } 291 | } 292 | } 293 | } -------------------------------------------------------------------------------- /DynamicRest.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30711.63 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynamicRest", "DynamicRest\DynamicRest.csproj", "{6761F94D-EFCD-49C7-9E8E-ECBCA014FF63}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples", "Samples\Samples.csproj", "{6761F94D-EFCD-49C7-9E8E-ECBCA014FF64}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynamicRest.UnitTests", "DynamicRest.UnitTests\DynamicRest.UnitTests.csproj", "{18F63953-D302-415F-AE2E-B97A41AEAD6B}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {6761F94D-EFCD-49C7-9E8E-ECBCA014FF63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {6761F94D-EFCD-49C7-9E8E-ECBCA014FF63}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {6761F94D-EFCD-49C7-9E8E-ECBCA014FF63}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {6761F94D-EFCD-49C7-9E8E-ECBCA014FF63}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {6761F94D-EFCD-49C7-9E8E-ECBCA014FF64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {6761F94D-EFCD-49C7-9E8E-ECBCA014FF64}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {6761F94D-EFCD-49C7-9E8E-ECBCA014FF64}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {6761F94D-EFCD-49C7-9E8E-ECBCA014FF64}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {18F63953-D302-415F-AE2E-B97A41AEAD6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {18F63953-D302-415F-AE2E-B97A41AEAD6B}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {18F63953-D302-415F-AE2E-B97A41AEAD6B}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {18F63953-D302-415F-AE2E-B97A41AEAD6B}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {172A248B-1348-4FF8-AB2A-1E8489CD7D23} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /DynamicRest/BinaryResponseProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace DynamicRest 2 | { 3 | public class BinaryResponseProcessor : ResponseProcessor 4 | { 5 | public BinaryResponseProcessor() : base(new StandardResultBuilder(RestService.Binary)) 6 | { 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /DynamicRest/DynamicParsingException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DynamicRest { 4 | 5 | public class DynamicParsingException : Exception { 6 | 7 | public DynamicParsingException(string message) 8 | : base(message) {} 9 | } 10 | } -------------------------------------------------------------------------------- /DynamicRest/DynamicRest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net452 5 | 6 | 7 | 8 | Huddle 9 | Nikhil Kothari, Huddle 10 | Copyright 2015 Huddle and Nikhil Kothari 11 | https://github.com/HuddleEng/dynamicrest/ 12 | BSD-3-Clause 13 | 14 | true 15 | https://github.com/HuddleEng/dynamicrest/ 16 | git 17 | Huddle.DynamicRest 18 | 1.0.0.0 19 | 1.0.0.0 20 | 1.0.0 21 | 22 | true 23 | 24 | 25 | true 26 | 27 | 28 | true 29 | snupkg 30 | REST API Client working against JSON data and XML data using late-bound 31 | dynamic code using the dynamic programming features 32 | 33 | 34 | 35 | 36 | PreserveNewest 37 | 38 | 39 | 40 | 41 | 42 | all 43 | runtime; build; native; contentfiles; analyzers; buildtransitive 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /DynamicRest/Fluent/IRestClientBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | 5 | namespace DynamicRest.Fluent { 6 | 7 | public interface IRestClientBuilder { 8 | 9 | dynamic Build(); 10 | 11 | IRestClientBuilder WithContentType(string contentType); 12 | IRestClientBuilder WithRequestBuilder(IBuildRequests requestBuilder); 13 | IRestClientBuilder WithUri(string uri); 14 | IRestClientBuilder WithBody(string body); 15 | IRestClientBuilder WithAcceptHeader(string acceptType); 16 | IRestClientBuilder WithOAuth2Token(string token); 17 | IRestClientBuilder WithResponseProcessor(IProcessResponses responseProcessor); 18 | IRestClientBuilder WithNoAcceptHeader(); 19 | IRestClientBuilder WithAutoRedirect(bool autoRedirect); 20 | IRestClientBuilder WithAcceptEncodingHeader(string acceptEncodingType); 21 | IRestClientBuilder WithIfModifiedSinceDate(DateTime ifModifiedSince); 22 | IRestClientBuilder WithHeaders(Dictionary headers); 23 | IRestClientBuilder WithCustomHeaders(Dictionary headers); 24 | IRestClientBuilder WithUserAgent(string userAgent); 25 | IRestClientBuilder WithTimeout(int timeout); 26 | IRestClientBuilder WithProxy(IWebProxy webProxy); 27 | } 28 | } -------------------------------------------------------------------------------- /DynamicRest/Fluent/RestClientBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using DynamicRest.HTTPInterfaces.WebWrappers; 5 | 6 | namespace DynamicRest.Fluent { 7 | 8 | public class RestClientBuilder : IRestClientBuilder { 9 | 10 | IBuildRequests _requestBuilder; 11 | IProcessResponses _responseProcessor; 12 | string _uri; 13 | string _contentType; 14 | string _body; 15 | string _acceptType; 16 | string _token; 17 | bool _noAcceptHeader; 18 | private bool _autoRedirect; 19 | private string _acceptEncodingType; 20 | Dictionary _headers; 21 | Dictionary _customHeaders; 22 | DateTime? _ifModifiedSince; 23 | private string _userAgent; 24 | private int _timeout; 25 | 26 | IWebProxy _proxy; 27 | 28 | public RestClientBuilder() 29 | { 30 | _headers = new Dictionary(); 31 | _customHeaders = new Dictionary(); 32 | } 33 | 34 | public dynamic Build() 35 | { 36 | _timeout = _timeout == 0 ? 100000 : _timeout; // default timeout i the CLR is 100s 37 | 38 | _contentType = _contentType ?? "application/xml"; 39 | _acceptType = _acceptType ?? "application/xml"; 40 | if (_noAcceptHeader){ 41 | _acceptType = string.Empty; 42 | } 43 | 44 | if (_requestBuilder == null) { 45 | _requestBuilder = new HttpVerbRequestBuilder(new RequestFactory()); 46 | } 47 | 48 | if (_responseProcessor == null) { 49 | var serviceType = _acceptType.Contains("xml") ? RestService.Xml : _acceptType.Contains("json") ? RestService.Json : _acceptType.EndsWith("plain") ? RestService.Text: RestService.Binary; 50 | if (_noAcceptHeader){ 51 | serviceType = RestService.Xml; 52 | } 53 | 54 | this._responseProcessor = new ResponseProcessor(new StandardResultBuilder(serviceType)); 55 | } 56 | 57 | if (_proxy != null) 58 | { 59 | _requestBuilder.Proxy = _proxy; 60 | } 61 | 62 | _requestBuilder.Uri = _uri; 63 | _requestBuilder.ContentType = _contentType; 64 | _requestBuilder.AcceptHeader = _acceptType; 65 | _requestBuilder.Body = _body; 66 | _requestBuilder.AllowAutoRedirect = _autoRedirect; 67 | _requestBuilder.UserAgent = _userAgent; 68 | _requestBuilder.Timeout = _timeout; 69 | if (!string.IsNullOrEmpty(_token)) 70 | { 71 | _requestBuilder.SetOAuth2AuthorizationHeader(_token); 72 | } 73 | if(!string.IsNullOrEmpty(_acceptEncodingType)) 74 | { 75 | _requestBuilder.AddHeader(HttpRequestHeader.AcceptEncoding, _acceptEncodingType); 76 | } 77 | 78 | if(_ifModifiedSince.HasValue) 79 | { 80 | 81 | _requestBuilder.IfModifiedSince(_ifModifiedSince.Value); 82 | } 83 | 84 | foreach (var header in _headers) 85 | { 86 | _requestBuilder.AddHeader(header.Key, header.Value); 87 | } 88 | 89 | foreach (var header in _customHeaders) 90 | { 91 | _requestBuilder.AddCustomHeader(header.Key, header.Value); 92 | } 93 | 94 | return new RestClient(_requestBuilder, _responseProcessor); 95 | } 96 | 97 | public IRestClientBuilder WithContentType(string contentType) { 98 | _contentType = contentType; 99 | return this; 100 | } 101 | 102 | public IRestClientBuilder WithRequestBuilder(IBuildRequests requestBuilder) { 103 | _requestBuilder = requestBuilder; 104 | return this; 105 | } 106 | 107 | public IRestClientBuilder WithUri(string uri) { 108 | _uri = uri; 109 | return this; 110 | } 111 | 112 | public IRestClientBuilder WithBody(string body) { 113 | _body = body; 114 | return this; 115 | } 116 | 117 | public IRestClientBuilder WithAcceptHeader(string acceptType) { 118 | _acceptType = acceptType; 119 | return this; 120 | } 121 | 122 | public IRestClientBuilder WithAcceptEncodingHeader(string acceptEncodingType) 123 | { 124 | _acceptEncodingType = acceptEncodingType; 125 | return this; 126 | } 127 | 128 | public IRestClientBuilder WithIfModifiedSinceDate(DateTime ifModifiedSince) 129 | { 130 | _ifModifiedSince = ifModifiedSince; 131 | return this; 132 | } 133 | 134 | public IRestClientBuilder WithOAuth2Token(string token) { 135 | _token = token; 136 | return this; 137 | } 138 | 139 | public IRestClientBuilder WithResponseProcessor(IProcessResponses responseProcessor) { 140 | _responseProcessor = responseProcessor; 141 | return this; 142 | } 143 | 144 | public IRestClientBuilder WithNoAcceptHeader() { 145 | _noAcceptHeader = true; 146 | return this; 147 | } 148 | 149 | public IRestClientBuilder WithAutoRedirect(bool autoRedirect) { 150 | _autoRedirect = autoRedirect; 151 | return this; 152 | } 153 | 154 | public IRestClientBuilder WithHeaders(Dictionary headers) 155 | { 156 | foreach (var header in headers) 157 | { 158 | _headers.Add(header.Key, header.Value); 159 | } 160 | return this; 161 | } 162 | 163 | public IRestClientBuilder WithCustomHeaders(Dictionary headers) 164 | { 165 | foreach (var header in headers) 166 | { 167 | _customHeaders.Add(header.Key, header.Value); 168 | } 169 | return this; 170 | } 171 | 172 | public IRestClientBuilder WithUserAgent(string userAgent) 173 | { 174 | _userAgent = userAgent; 175 | return this; 176 | } 177 | 178 | public IRestClientBuilder WithTimeout(int timeout) 179 | { 180 | _timeout = timeout; 181 | return this; 182 | } 183 | 184 | public IRestClientBuilder WithProxy(IWebProxy webProxy) 185 | { 186 | _proxy = webProxy; 187 | return this; 188 | } 189 | } 190 | } -------------------------------------------------------------------------------- /DynamicRest/HTTPInterfaces/HttpVerb.cs: -------------------------------------------------------------------------------- 1 | namespace DynamicRest.HTTPInterfaces { 2 | 3 | public enum HttpVerb { 4 | GET, 5 | POST, 6 | PUT, 7 | DELETE, 8 | OPTIONS, 9 | HEAD, 10 | PATCH 11 | } 12 | } -------------------------------------------------------------------------------- /DynamicRest/HTTPInterfaces/HttpWebRequestWrappers/HttpWebRequestFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | 4 | namespace DynamicRest.HTTPInterfaces.HttpWebRequestWrappers { 5 | 6 | public class HttpWebRequestFactory : IHttpRequestFactory 7 | { 8 | public IHttpRequest Create(Uri uri) { 9 | return new HttpWebRequestWrapper((HttpWebRequest) WebRequest.Create(uri)); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /DynamicRest/HTTPInterfaces/HttpWebRequestWrappers/HttpWebRequestWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | using System.Text; 5 | using DynamicRest.Helpers; 6 | 7 | namespace DynamicRest.HTTPInterfaces.HttpWebRequestWrappers { 8 | 9 | public class HttpWebRequestWrapper : IHttpRequest { 10 | 11 | private readonly HttpWebRequest _webRequest; 12 | 13 | public HttpWebRequestWrapper(HttpWebRequest webRequest) { 14 | _webRequest = webRequest; 15 | } 16 | 17 | public Uri RequestURI => _webRequest.RequestUri; 18 | 19 | public HttpVerb HttpVerb { 20 | get => _webRequest.Method.ToHttpVerb(); 21 | set => _webRequest.Method = value.ToString(); 22 | } 23 | 24 | public WebHeaderCollection Headers => _webRequest.Headers; 25 | 26 | public string Accept { 27 | get => _webRequest.Accept; 28 | set => _webRequest.Accept = value; 29 | } 30 | 31 | public string ContentType => _webRequest.ContentType; 32 | 33 | public bool AllowAutoRedirect { 34 | get => _webRequest.AllowAutoRedirect; 35 | set => _webRequest.AllowAutoRedirect = value; 36 | } 37 | 38 | public string UserAgent 39 | { 40 | get => _webRequest.UserAgent; 41 | set => _webRequest.UserAgent = value; 42 | } 43 | 44 | public int Timeout 45 | { 46 | get => _webRequest.Timeout; 47 | set => _webRequest.Timeout = value; 48 | } 49 | 50 | public IWebProxy Proxy 51 | { 52 | get => _webRequest.Proxy; 53 | set => _webRequest.Proxy = value; 54 | } 55 | 56 | public void AddCredentials(ICredentials credentials) { 57 | _webRequest.Credentials = credentials; 58 | } 59 | 60 | public void AddHeaders(WebHeaderCollection headers) { 61 | _webRequest.Headers.Add(headers); 62 | } 63 | 64 | public void AddRequestBody(string contentType, string content) { 65 | if (content != null) { 66 | byte[] bytes = Encoding.UTF8.GetBytes(content); 67 | _webRequest.ContentType = contentType; 68 | _webRequest.ContentLength = bytes.Length; 69 | using (Stream requestStream = _webRequest.GetRequestStream()) { 70 | requestStream.Write(bytes, 0, bytes.Length); 71 | } 72 | } 73 | } 74 | 75 | public void BeginGetResponse(Action action, object asyncRequest) { 76 | _webRequest.BeginGetResponse(ar => action(ar), asyncRequest); 77 | } 78 | 79 | public IHttpResponse EndGetResponse(object asyncRequest) { 80 | return new HttpWebResponseWrapper((HttpWebResponse) _webRequest.EndGetResponse((IAsyncResult) asyncRequest)); 81 | } 82 | 83 | public IHttpResponse GetResponse() { 84 | return new HttpWebResponseWrapper(_webRequest.GetResponse() as HttpWebResponse); 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /DynamicRest/HTTPInterfaces/HttpWebRequestWrappers/HttpWebResponseWrapper.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Net; 3 | 4 | namespace DynamicRest.HTTPInterfaces.HttpWebRequestWrappers { 5 | 6 | public class HttpWebResponseWrapper : IHttpResponse { 7 | 8 | private readonly HttpWebResponse webResponse; 9 | 10 | public HttpWebResponseWrapper(HttpWebResponse webResponse) { 11 | this.webResponse = webResponse; 12 | } 13 | 14 | public string ContentEncoding => webResponse.ContentEncoding; 15 | 16 | public long ContentLength => webResponse.ContentLength; 17 | 18 | public WebHeaderCollection Headers => webResponse.Headers; 19 | 20 | public HttpStatusCode StatusCode => webResponse.StatusCode; 21 | 22 | public string StatusDescription => webResponse.StatusDescription; 23 | 24 | public Stream GetResponseStream() { 25 | return webResponse.GetResponseStream(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /DynamicRest/HTTPInterfaces/IHttpRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | 4 | namespace DynamicRest.HTTPInterfaces 5 | { 6 | public interface IHttpRequest 7 | { 8 | Uri RequestURI { get; } 9 | HttpVerb HttpVerb { get; set; } 10 | WebHeaderCollection Headers { get; } 11 | string Accept { get; set; } 12 | string ContentType { get; } 13 | bool AllowAutoRedirect { get; set; } 14 | string UserAgent { get; set; } 15 | int Timeout { get; set; } 16 | IWebProxy Proxy { get; set; } 17 | 18 | void AddCredentials(ICredentials credentials); 19 | void AddHeaders(WebHeaderCollection headers); 20 | void AddRequestBody(string contentType, string content); 21 | void BeginGetResponse(Action action, object asyncRequest); 22 | IHttpResponse EndGetResponse(object asyncRequest); 23 | IHttpResponse GetResponse(); 24 | } 25 | } -------------------------------------------------------------------------------- /DynamicRest/HTTPInterfaces/IHttpRequestFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DynamicRest.HTTPInterfaces { 4 | 5 | public interface IHttpRequestFactory { 6 | IHttpRequest Create(Uri uri); 7 | } 8 | } -------------------------------------------------------------------------------- /DynamicRest/HTTPInterfaces/IHttpResponse.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Net; 3 | 4 | namespace DynamicRest.HTTPInterfaces { 5 | 6 | public interface IHttpResponse { 7 | string ContentEncoding { get; } 8 | long ContentLength { get; } 9 | WebHeaderCollection Headers { get; } 10 | HttpStatusCode StatusCode { get; } 11 | string StatusDescription { get; } 12 | Stream GetResponseStream(); 13 | } 14 | } -------------------------------------------------------------------------------- /DynamicRest/HTTPInterfaces/WebWrappers/HttpWebRequestWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | using System.Text; 5 | using DynamicRest.Helpers; 6 | 7 | namespace DynamicRest.HTTPInterfaces.WebWrappers { 8 | 9 | public class HttpWebRequestWrapper : IHttpRequest { 10 | 11 | private readonly HttpWebRequest _webrequest; 12 | 13 | public HttpWebRequestWrapper(HttpWebRequest webrequest) { 14 | _webrequest = webrequest; 15 | } 16 | 17 | public Uri RequestURI { 18 | get { return _webrequest.RequestUri; } 19 | } 20 | 21 | public HttpVerb HttpVerb { 22 | get { 23 | return _webrequest.Method.ToHttpVerb(); 24 | } 25 | set { 26 | _webrequest.Method = value.ToString().ToUpper(); 27 | } 28 | } 29 | 30 | public WebHeaderCollection Headers { 31 | get { return _webrequest.Headers; } 32 | } 33 | 34 | public string Accept { 35 | get { 36 | return _webrequest.Accept; 37 | } 38 | set { 39 | _webrequest.Accept = value; 40 | } 41 | } 42 | 43 | public string ContentType { 44 | get { 45 | return _webrequest.ContentType; 46 | } 47 | } 48 | 49 | public bool AllowAutoRedirect { 50 | get { return _webrequest.AllowAutoRedirect; } 51 | set { _webrequest.AllowAutoRedirect = value; } 52 | } 53 | 54 | public string UserAgent { 55 | get { 56 | return _webrequest.UserAgent; 57 | } 58 | set { 59 | if (!string.IsNullOrWhiteSpace(value)) 60 | { 61 | _webrequest.UserAgent = value; 62 | } 63 | } 64 | } 65 | 66 | public int Timeout 67 | { 68 | get { return _webrequest.Timeout; } 69 | set { _webrequest.Timeout = value; } 70 | } 71 | 72 | public IWebProxy Proxy 73 | { 74 | get { return _webrequest.Proxy; } 75 | set { _webrequest.Proxy = value; } 76 | } 77 | 78 | public DateTime IfModifiedSince 79 | { 80 | get { return _webrequest.IfModifiedSince; } 81 | set { _webrequest.IfModifiedSince = value; } 82 | } 83 | 84 | public void AddCredentials(ICredentials credentials) { 85 | _webrequest.Credentials = credentials; 86 | } 87 | 88 | public void AddHeaders(WebHeaderCollection headers) { 89 | _webrequest.Headers.Add(headers); 90 | } 91 | 92 | public void AddRequestBody(string contentType, string content) { 93 | if (content != null) { 94 | byte[] bytes = Encoding.UTF8.GetBytes(content); 95 | _webrequest.ContentType = contentType; 96 | _webrequest.ContentLength = bytes.Length; 97 | using (Stream requestStream = _webrequest.GetRequestStream()) { 98 | requestStream.Write(bytes, 0, bytes.Length); 99 | } 100 | } 101 | } 102 | 103 | public void BeginGetResponse(Action action, object asyncRequest) { 104 | _webrequest.BeginGetResponse(ar => action(ar), asyncRequest); 105 | } 106 | 107 | public IHttpResponse EndGetResponse(object asyncRequest) { 108 | return new HttpWebResponseWrapper((HttpWebResponse) _webrequest.EndGetResponse((IAsyncResult) asyncRequest)); 109 | } 110 | 111 | public IHttpResponse GetResponse() 112 | { 113 | return new HttpWebResponseWrapper(_webrequest.GetResponse() as HttpWebResponse); 114 | } 115 | } 116 | } -------------------------------------------------------------------------------- /DynamicRest/HTTPInterfaces/WebWrappers/HttpWebResponseWrapper.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Net; 3 | 4 | namespace DynamicRest.HTTPInterfaces.WebWrappers { 5 | 6 | public class HttpWebResponseWrapper : IHttpResponse { 7 | 8 | private readonly HttpWebResponse webResponse; 9 | 10 | public HttpWebResponseWrapper(HttpWebResponse webResponse) { 11 | this.webResponse = webResponse; 12 | } 13 | 14 | public string ContentEncoding 15 | { 16 | get { return webResponse.ContentEncoding; } 17 | } 18 | 19 | public long ContentLength 20 | { 21 | get { return webResponse.ContentLength; } 22 | } 23 | 24 | public WebHeaderCollection Headers { 25 | get { 26 | return webResponse.Headers; 27 | } 28 | } 29 | 30 | public HttpStatusCode StatusCode { 31 | get { 32 | return webResponse.StatusCode; 33 | } 34 | } 35 | 36 | public string StatusDescription { 37 | get { 38 | return webResponse.StatusDescription; 39 | } 40 | } 41 | 42 | public Stream GetResponseStream() { 43 | return webResponse.GetResponseStream(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /DynamicRest/HTTPInterfaces/WebWrappers/RequestFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | 4 | namespace DynamicRest.HTTPInterfaces.WebWrappers { 5 | 6 | public class RequestFactory : IHttpRequestFactory 7 | { 8 | public IHttpRequest Create(Uri uri) { 9 | return new HttpWebRequestWrapper((HttpWebRequest) WebRequest.Create(uri)); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /DynamicRest/Helpers/HttpVerbHelpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using DynamicRest.HTTPInterfaces; 3 | 4 | namespace DynamicRest.Helpers { 5 | 6 | public static class HttpVerbHelpers { 7 | 8 | public static HttpVerb ToHttpVerb(this string operationName) { 9 | HttpVerb result; 10 | if (Enum.TryParse(operationName, true, out result)) 11 | { 12 | return result; 13 | } 14 | 15 | throw new InvalidOperationException(string.Format("The operation {0} is not a valid Http verb", operationName)); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /DynamicRest/HttpVerbRequestBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using DynamicRest.Helpers; 4 | using DynamicRest.HTTPInterfaces; 5 | using DynamicRest.HTTPInterfaces.WebWrappers; 6 | using DynamicRest.Json; 7 | 8 | namespace DynamicRest { 9 | 10 | public class HttpVerbRequestBuilder : IBuildRequests { 11 | 12 | private readonly IHttpRequestFactory _requestFactory; 13 | private readonly WebHeaderCollection _headers = new WebHeaderCollection(); 14 | DateTime? _ifModifiedSince; 15 | 16 | public HttpVerbRequestBuilder(IHttpRequestFactory requestFactory) { 17 | _requestFactory = requestFactory; 18 | 19 | ParametersStore = new ParametersStore(); 20 | } 21 | 22 | public ParametersStore ParametersStore { get; set; } 23 | public string Body { get; set; } 24 | public string ContentType { get; set; } 25 | public ICredentials Credentials { private get; set; } 26 | public string Uri { get; set; } 27 | public string AcceptHeader { get; set; } 28 | public bool AllowAutoRedirect { get; set; } 29 | public string UserAgent { get; set; } 30 | public int Timeout { get; set; } 31 | public IWebProxy Proxy { get; set; } 32 | 33 | public IHttpRequest CreateRequest(string operationName, JsonObject parameters) { 34 | if (string.IsNullOrEmpty(Uri)) { 35 | throw new InvalidOperationException("You must set a Uri for the request."); 36 | } 37 | IHttpRequest webRequest = _requestFactory.Create(new Uri(Uri)); 38 | 39 | webRequest.HttpVerb = operationName.ToHttpVerb(); 40 | webRequest.AddHeaders(_headers); 41 | webRequest.AddCredentials(Credentials); 42 | webRequest.Accept = AcceptHeader; 43 | if (Proxy != null) 44 | { 45 | webRequest.Proxy = Proxy; 46 | } 47 | webRequest.AllowAutoRedirect = AllowAutoRedirect; 48 | webRequest.UserAgent = UserAgent; 49 | webRequest.Timeout = Timeout; 50 | 51 | if(_ifModifiedSince.HasValue) 52 | { 53 | ((HttpWebRequestWrapper)webRequest).IfModifiedSince = _ifModifiedSince.Value; 54 | } 55 | webRequest.AddRequestBody(ContentType, Body); 56 | 57 | return webRequest; 58 | } 59 | 60 | public void AddCustomHeader(string headerKey, string value) 61 | { 62 | _headers.Add(headerKey, value); 63 | } 64 | 65 | public void SetOAuth2AuthorizationHeader(string oAuth2Token) 66 | { 67 | _headers.Add(HttpRequestHeader.Authorization, $"Bearer {oAuth2Token}"); 68 | } 69 | 70 | public void IfModifiedSince(DateTime ifModifiedSince) 71 | { 72 | _ifModifiedSince = ifModifiedSince; 73 | } 74 | 75 | public void AddHeader(HttpRequestHeader headerType, string value) 76 | { 77 | _headers.Add(headerType, value); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /DynamicRest/IBuildDynamicResults.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace DynamicRest { 4 | 5 | public interface IBuildDynamicResults { 6 | object CreateResult(string responseText); 7 | BuilderResponse ProcessResponse(Stream responseStream); 8 | RestService ServiceType { get; } 9 | } 10 | } -------------------------------------------------------------------------------- /DynamicRest/IBuildRequests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using DynamicRest.HTTPInterfaces; 4 | using DynamicRest.Json; 5 | 6 | namespace DynamicRest 7 | { 8 | public interface IBuildRequests { 9 | string Uri { set; } 10 | string ContentType { get; set; } 11 | string AcceptHeader { get; set; } 12 | string Body { get; set; } 13 | ParametersStore ParametersStore { get; set; } 14 | ICredentials Credentials { set; } 15 | bool AllowAutoRedirect { get; set; } 16 | string UserAgent { get; set; } 17 | int Timeout { get; set; } 18 | IWebProxy Proxy { get; set; } 19 | 20 | IHttpRequest CreateRequest(string operationName, JsonObject parameters); 21 | void AddHeader(HttpRequestHeader headerType, string value); 22 | void AddCustomHeader(string headerKey, string value); 23 | void SetOAuth2AuthorizationHeader(string oAuth2Token); 24 | void IfModifiedSince(DateTime ifModifiedSince); 25 | } 26 | } -------------------------------------------------------------------------------- /DynamicRest/IProcessResponses.cs: -------------------------------------------------------------------------------- 1 | using DynamicRest.HTTPInterfaces; 2 | 3 | namespace DynamicRest { 4 | public interface IProcessResponses { 5 | void Process(IHttpResponse webResponse, RestOperation operation); 6 | } 7 | } -------------------------------------------------------------------------------- /DynamicRest/IRestUriTransformer.cs: -------------------------------------------------------------------------------- 1 | // IRestUriTransformer.cs 2 | // DynamicRest provides REST service access using C# 4.0 dynamic programming. 3 | // The latest information and code for the project can be found at 4 | // https://github.com/NikhilK/dynamicrest 5 | // 6 | // This project is licensed under the BSD license. See the License.txt file for 7 | // more information. 8 | // 9 | 10 | using System; 11 | 12 | namespace DynamicRest { 13 | 14 | public interface IRestUriTransformer { 15 | Uri TransformUri(Uri uri); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /DynamicRest/Json/JsonArray.cs: -------------------------------------------------------------------------------- 1 | // JsonArray.cs 2 | // DynamicRest provides REST service access using C# 4.0 dynamic programming. 3 | // The latest information and code for the project can be found at 4 | // https://github.com/NikhilK/dynamicrest 5 | // 6 | // This project is licensed under the BSD license. See the License.txt file for 7 | // more information. 8 | // 9 | 10 | using System; 11 | using System.Collections; 12 | using System.Collections.Generic; 13 | using System.Dynamic; 14 | 15 | namespace DynamicRest.Json { 16 | 17 | public sealed class JsonArray : DynamicObject, ICollection, ICollection { 18 | 19 | private List _members; 20 | 21 | public JsonArray() { 22 | _members = new List(); 23 | } 24 | 25 | public JsonArray(object o) 26 | : this() { 27 | _members.Add(o); 28 | } 29 | 30 | public JsonArray(object o1, object o2) 31 | : this() { 32 | _members.Add(o1); 33 | _members.Add(o2); 34 | } 35 | 36 | public JsonArray(params object[] objects) 37 | : this() { 38 | _members.AddRange(objects); 39 | } 40 | 41 | public int Count { 42 | get { 43 | return _members.Count; 44 | } 45 | } 46 | 47 | public object this[int index] { 48 | get { 49 | return _members[index]; 50 | } 51 | } 52 | 53 | public override bool TryConvert(ConvertBinder binder, out object result) { 54 | Type targetType = binder.Type; 55 | 56 | if ((targetType == typeof(IEnumerable)) || 57 | (targetType == typeof(IEnumerable)) || 58 | (targetType == typeof(ICollection)) || 59 | (targetType == typeof(ICollection))) { 60 | result = this; 61 | return true; 62 | } 63 | 64 | return base.TryConvert(binder, out result); 65 | } 66 | 67 | public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { 68 | if (String.Compare(binder.Name, "Add", StringComparison.Ordinal) == 0) { 69 | if (args.Length == 1) { 70 | _members.Add(args[0]); 71 | result = null; 72 | return true; 73 | } 74 | result = null; 75 | return false; 76 | } 77 | else if (String.Compare(binder.Name, "Insert", StringComparison.Ordinal) == 0) { 78 | if (args.Length == 2) { 79 | _members.Insert(Convert.ToInt32(args[0]), args[1]); 80 | result = null; 81 | return true; 82 | } 83 | result = null; 84 | return false; 85 | } 86 | else if (String.Compare(binder.Name, "IndexOf", StringComparison.Ordinal) == 0) { 87 | if (args.Length == 1) { 88 | result = _members.IndexOf(args[0]); 89 | return true; 90 | } 91 | result = null; 92 | return false; 93 | } 94 | else if (String.Compare(binder.Name, "Clear", StringComparison.Ordinal) == 0) { 95 | if (args.Length == 0) { 96 | _members.Clear(); 97 | result = null; 98 | return true; 99 | } 100 | result = null; 101 | return false; 102 | } 103 | else if (String.Compare(binder.Name, "Remove", StringComparison.Ordinal) == 0) { 104 | if (args.Length == 1) { 105 | result = _members.Remove(args[0]); 106 | return true; 107 | } 108 | result = null; 109 | return false; 110 | } 111 | else if (String.Compare(binder.Name, "RemoveAt", StringComparison.Ordinal) == 0) { 112 | if (args.Length == 1) { 113 | _members.RemoveAt(Convert.ToInt32(args[0])); 114 | result = null; 115 | return true; 116 | } 117 | result = null; 118 | return false; 119 | } 120 | 121 | return base.TryInvokeMember(binder, args, out result); 122 | } 123 | 124 | public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) { 125 | if (indexes.Length == 1) { 126 | result = _members[Convert.ToInt32(indexes[0])]; 127 | return true; 128 | } 129 | 130 | return base.TryGetIndex(binder, indexes, out result); 131 | } 132 | 133 | public override bool TryGetMember(GetMemberBinder binder, out object result) { 134 | if (String.Compare("Length", binder.Name, StringComparison.Ordinal) == 0) { 135 | result = _members.Count; 136 | return true; 137 | } 138 | 139 | var memberExists = base.TryGetMember(binder, out result); 140 | if (result == null) { 141 | throw new DynamicParsingException(string.Format("No member named '{0}' found in the response.", binder.Name)); 142 | } 143 | return memberExists; 144 | } 145 | 146 | public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) { 147 | if (indexes.Length == 1) { 148 | _members[Convert.ToInt32(indexes[0])] = value; 149 | return true; 150 | } 151 | 152 | return base.TrySetIndex(binder, indexes, value); 153 | } 154 | 155 | #region Implementation of IEnumerable 156 | IEnumerator IEnumerable.GetEnumerator() { 157 | return _members.GetEnumerator(); 158 | } 159 | #endregion 160 | 161 | #region Implementation of IEnumerable 162 | IEnumerator IEnumerable.GetEnumerator() { 163 | return _members.GetEnumerator(); 164 | } 165 | #endregion 166 | 167 | #region Implementation of ICollection 168 | int ICollection.Count { 169 | get { 170 | return _members.Count; 171 | } 172 | } 173 | 174 | bool ICollection.IsSynchronized { 175 | get { 176 | return false; 177 | } 178 | } 179 | 180 | object ICollection.SyncRoot { 181 | get { 182 | return this; 183 | } 184 | } 185 | 186 | void ICollection.CopyTo(Array array, int index) { 187 | throw new NotImplementedException(); 188 | } 189 | #endregion 190 | 191 | #region Implementation of ICollection 192 | int ICollection.Count { 193 | get { 194 | return _members.Count; 195 | } 196 | } 197 | 198 | bool ICollection.IsReadOnly { 199 | get { 200 | return false; 201 | } 202 | } 203 | 204 | void ICollection.Add(object item) { 205 | ((ICollection)_members).Add(item); 206 | } 207 | 208 | void ICollection.Clear() { 209 | ((ICollection)_members).Clear(); 210 | } 211 | 212 | bool ICollection.Contains(object item) { 213 | return ((ICollection)_members).Contains(item); 214 | } 215 | 216 | void ICollection.CopyTo(object[] array, int arrayIndex) { 217 | ((ICollection)_members).CopyTo(array, arrayIndex); 218 | } 219 | 220 | bool ICollection.Remove(object item) { 221 | return ((ICollection)_members).Remove(item); 222 | } 223 | #endregion 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /DynamicRest/Json/JsonObject.cs: -------------------------------------------------------------------------------- 1 | // JsonObject.cs 2 | // DynamicRest provides REST service access using C# 4.0 dynamic programming. 3 | // The latest information and code for the project can be found at 4 | // https://github.com/NikhilK/dynamicrest 5 | // 6 | // This project is licensed under the BSD license. See the License.txt file for 7 | // more information. 8 | // 9 | 10 | using System; 11 | using System.Collections; 12 | using System.Collections.Generic; 13 | using System.Dynamic; 14 | 15 | namespace DynamicRest.Json { 16 | 17 | public sealed class JsonObject : DynamicObject, IDictionary, IDictionary { 18 | 19 | private Dictionary _members; 20 | 21 | public JsonObject() { 22 | _members = new Dictionary(); 23 | } 24 | 25 | public JsonObject(params object[] nameValuePairs) 26 | : this() { 27 | if (nameValuePairs != null) { 28 | if (nameValuePairs.Length % 2 != 0) { 29 | throw new ArgumentException("Mismatch in name/value pairs."); 30 | } 31 | 32 | for (int i = 0; i < nameValuePairs.Length; i += 2) { 33 | if (!(nameValuePairs[i] is string)) { 34 | throw new ArgumentException("Name parameters must be strings."); 35 | } 36 | 37 | _members[(string)nameValuePairs[i]] = nameValuePairs[i + 1]; 38 | } 39 | } 40 | } 41 | 42 | public object this[string key] { 43 | get { 44 | return ((IDictionary)this)[key]; 45 | } 46 | set { 47 | ((IDictionary)this)[key] = value; 48 | } 49 | } 50 | 51 | public bool ContainsKey(string key) { 52 | return ((IDictionary)this).ContainsKey(key); 53 | } 54 | 55 | public override bool TryConvert(ConvertBinder binder, out object result) { 56 | Type targetType = binder.Type; 57 | 58 | if ((targetType == typeof(IEnumerable)) || 59 | (targetType == typeof(IEnumerable>)) || 60 | (targetType == typeof(IDictionary)) || 61 | (targetType == typeof(IDictionary))) { 62 | result = this; 63 | return true; 64 | } 65 | 66 | return base.TryConvert(binder, out result); 67 | } 68 | 69 | public override bool TryDeleteMember(DeleteMemberBinder binder) { 70 | return _members.Remove(binder.Name); 71 | } 72 | 73 | public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) { 74 | if (indexes.Length == 1) { 75 | result = ((IDictionary)this)[(string)indexes[0]]; 76 | return true; 77 | } 78 | 79 | return base.TryGetIndex(binder, indexes, out result); 80 | } 81 | 82 | public override bool TryGetMember(GetMemberBinder binder, out object result) { 83 | object value; 84 | if (_members.TryGetValue(binder.Name, out value)) { 85 | result = value; 86 | return true; 87 | } 88 | 89 | var memberExists = base.TryGetMember(binder, out result); 90 | if (result == null) { 91 | throw new DynamicParsingException(string.Format("No member named '{0}' found in the response.", binder.Name)); 92 | } 93 | return memberExists; 94 | } 95 | 96 | public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) { 97 | if (indexes.Length == 1) { 98 | ((IDictionary)this)[(string)indexes[0]] = value; 99 | return true; 100 | } 101 | 102 | return base.TrySetIndex(binder, indexes, value); 103 | } 104 | 105 | public override bool TrySetMember(SetMemberBinder binder, object value) { 106 | _members[binder.Name] = value; 107 | return true; 108 | } 109 | 110 | #region Implementation of IEnumerable 111 | IEnumerator IEnumerable.GetEnumerator() { 112 | return new DictionaryEnumerator(_members.GetEnumerator()); 113 | } 114 | #endregion 115 | 116 | #region Implementation of IEnumerable> 117 | IEnumerator> IEnumerable>.GetEnumerator() { 118 | return _members.GetEnumerator(); 119 | } 120 | #endregion 121 | 122 | #region Implementation of ICollection 123 | int ICollection.Count { 124 | get { 125 | return _members.Count; 126 | } 127 | } 128 | 129 | bool ICollection.IsSynchronized { 130 | get { 131 | return false; 132 | } 133 | } 134 | 135 | object ICollection.SyncRoot { 136 | get { 137 | return this; 138 | } 139 | } 140 | 141 | void ICollection.CopyTo(Array array, int index) { 142 | throw new NotImplementedException(); 143 | } 144 | #endregion 145 | 146 | #region Implementation of ICollection> 147 | int ICollection>.Count { 148 | get { 149 | return _members.Count; 150 | } 151 | } 152 | 153 | bool ICollection>.IsReadOnly { 154 | get { 155 | return false; 156 | } 157 | } 158 | 159 | void ICollection>.Add(KeyValuePair item) { 160 | ((IDictionary)_members).Add(item); 161 | } 162 | 163 | void ICollection>.Clear() { 164 | _members.Clear(); 165 | } 166 | 167 | bool ICollection>.Contains(KeyValuePair item) { 168 | return ((IDictionary)_members).Contains(item); 169 | } 170 | 171 | void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) { 172 | ((IDictionary)_members).CopyTo(array, arrayIndex); 173 | } 174 | 175 | bool ICollection>.Remove(KeyValuePair item) { 176 | return ((IDictionary)_members).Remove(item); 177 | } 178 | #endregion 179 | 180 | #region Implementation of IDictionary 181 | bool IDictionary.IsFixedSize { 182 | get { 183 | return false; 184 | } 185 | } 186 | 187 | bool IDictionary.IsReadOnly { 188 | get { 189 | return false; 190 | } 191 | } 192 | 193 | ICollection IDictionary.Keys { 194 | get { 195 | return _members.Keys; 196 | } 197 | } 198 | 199 | object IDictionary.this[object key] { 200 | get { 201 | return ((IDictionary)this)[(string)key]; 202 | } 203 | set { 204 | ((IDictionary)this)[(string)key] = value; 205 | } 206 | } 207 | 208 | ICollection IDictionary.Values { 209 | get { 210 | return _members.Values; 211 | } 212 | } 213 | 214 | void IDictionary.Add(object key, object value) { 215 | _members.Add((string)key, value); 216 | } 217 | 218 | void IDictionary.Clear() { 219 | _members.Clear(); 220 | } 221 | 222 | bool IDictionary.Contains(object key) { 223 | return _members.ContainsKey((string)key); 224 | } 225 | 226 | IDictionaryEnumerator IDictionary.GetEnumerator() { 227 | return (IDictionaryEnumerator)((IEnumerable)this).GetEnumerator(); 228 | } 229 | 230 | void IDictionary.Remove(object key) { 231 | _members.Remove((string)key); 232 | } 233 | #endregion 234 | 235 | #region Implementation of IDictionary 236 | ICollection IDictionary.Keys { 237 | get { 238 | return _members.Keys; 239 | } 240 | } 241 | 242 | object IDictionary.this[string key] { 243 | get { 244 | object value = null; 245 | if (_members.TryGetValue(key, out value)) { 246 | return value; 247 | } 248 | return null; 249 | } 250 | set { 251 | _members[key] = value; 252 | } 253 | } 254 | 255 | ICollection IDictionary.Values { 256 | get { 257 | return _members.Values; 258 | } 259 | } 260 | 261 | void IDictionary.Add(string key, object value) { 262 | _members.Add(key, value); 263 | } 264 | 265 | bool IDictionary.ContainsKey(string key) { 266 | return _members.ContainsKey(key); 267 | } 268 | 269 | bool IDictionary.Remove(string key) { 270 | return _members.Remove(key); 271 | } 272 | 273 | bool IDictionary.TryGetValue(string key, out object value) { 274 | return _members.TryGetValue(key, out value); 275 | } 276 | #endregion 277 | 278 | 279 | private sealed class DictionaryEnumerator : IDictionaryEnumerator { 280 | 281 | private IEnumerator> _enumerator; 282 | 283 | public DictionaryEnumerator(IEnumerator> enumerator) { 284 | _enumerator = enumerator; 285 | } 286 | 287 | public object Current { 288 | get { 289 | return Entry; 290 | } 291 | } 292 | 293 | public DictionaryEntry Entry { 294 | get { 295 | return new DictionaryEntry(_enumerator.Current.Key, _enumerator.Current.Value); 296 | } 297 | } 298 | 299 | public object Key { 300 | get { 301 | return _enumerator.Current.Key; 302 | } 303 | } 304 | 305 | public object Value { 306 | get { 307 | return _enumerator.Current.Value; 308 | } 309 | } 310 | 311 | public bool MoveNext() { 312 | return _enumerator.MoveNext(); 313 | } 314 | 315 | public void Reset() { 316 | _enumerator.Reset(); 317 | } 318 | } 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /DynamicRest/Json/JsonReader.cs: -------------------------------------------------------------------------------- 1 | // JsonReader.cs 2 | // DynamicRest provides REST service access using C# 4.0 dynamic programming. 3 | // The latest information and code for the project can be found at 4 | // https://github.com/NikhilK/dynamicrest 5 | // 6 | // This project is licensed under the BSD license. See the License.txt file for 7 | // more information. 8 | // 9 | 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Globalization; 13 | using System.IO; 14 | using System.Text; 15 | 16 | namespace DynamicRest.Json { 17 | 18 | public sealed class JsonReader { 19 | 20 | internal static readonly long MinDateTimeTicks = (new DateTime(1970, 1, 1, 0, 0, 0)).Ticks; 21 | internal static readonly DateTime MinDate = new DateTime(100, 1, 1, 0, 0, 0); 22 | 23 | private TextReader _reader; 24 | 25 | public JsonReader(string jsonText) 26 | : this(new StringReader(jsonText)) { 27 | } 28 | 29 | public JsonReader(TextReader reader) { 30 | _reader = reader; 31 | } 32 | 33 | private char GetNextCharacter() { 34 | return (char)_reader.Read(); 35 | } 36 | 37 | private char GetNextSignificantCharacter() { 38 | char ch = (char)_reader.Read(); 39 | while ((ch != '\0') && Char.IsWhiteSpace(ch)) { 40 | ch = (char)_reader.Read(); 41 | } 42 | return ch; 43 | } 44 | 45 | private string GetCharacters(int count) { 46 | string s = String.Empty; 47 | for (int i = 0; i < count; i++) { 48 | char ch = (char)_reader.Read(); 49 | if (ch == '\0') { 50 | return null; 51 | } 52 | s += ch; 53 | } 54 | return s; 55 | } 56 | 57 | private char PeekNextSignificantCharacter() { 58 | char ch = (char)_reader.Peek(); 59 | while ((ch != '\0') && Char.IsWhiteSpace(ch)) { 60 | _reader.Read(); 61 | ch = (char)_reader.Peek(); 62 | } 63 | return ch; 64 | } 65 | 66 | private JsonArray ReadArray() { 67 | JsonArray array = new JsonArray(); 68 | ICollection arrayItems = (ICollection)array; 69 | 70 | // Consume the '[' 71 | _reader.Read(); 72 | 73 | while (true) { 74 | char ch = PeekNextSignificantCharacter(); 75 | if (ch == '\0') { 76 | throw new FormatException("Unterminated array literal."); 77 | } 78 | 79 | if (ch == ']') { 80 | _reader.Read(); 81 | return array; 82 | } 83 | 84 | if (arrayItems.Count != 0) { 85 | if (ch != ',') { 86 | throw new FormatException("Invalid array literal."); 87 | } 88 | else { 89 | _reader.Read(); 90 | } 91 | } 92 | 93 | object item = ReadValue(); 94 | arrayItems.Add(item); 95 | } 96 | } 97 | 98 | private bool ReadBoolean() { 99 | string s = ReadName(/* allowQuotes */ false); 100 | 101 | if (s != null) { 102 | if (s.Equals("true", StringComparison.Ordinal)) { 103 | return true; 104 | } 105 | else if (s.Equals("false", StringComparison.Ordinal)) { 106 | return false; 107 | } 108 | } 109 | 110 | throw new FormatException("Invalid boolean literal."); 111 | } 112 | 113 | private string ReadName(bool allowQuotes) { 114 | char ch = PeekNextSignificantCharacter(); 115 | 116 | if ((ch == '"') || (ch == '\'')) { 117 | if (allowQuotes) { 118 | return ReadString(); 119 | } 120 | } 121 | else { 122 | StringBuilder sb = new StringBuilder(); 123 | 124 | while (true) { 125 | ch = (char)_reader.Peek(); 126 | if ((ch == '_') || Char.IsLetterOrDigit(ch)) { 127 | _reader.Read(); 128 | sb.Append(ch); 129 | } 130 | else { 131 | return sb.ToString(); 132 | } 133 | } 134 | } 135 | 136 | return null; 137 | } 138 | 139 | private void ReadNull() { 140 | string s = ReadName(/* allowQuotes */ false); 141 | 142 | if ((s == null) || !s.Equals("null", StringComparison.Ordinal)) { 143 | throw new FormatException("Invalid null literal."); 144 | } 145 | } 146 | 147 | private object ReadNumber() { 148 | char ch = (char)_reader.Read(); 149 | 150 | StringBuilder sb = new StringBuilder(); 151 | bool hasDecimal = (ch == '.'); 152 | 153 | sb.Append(ch); 154 | while (true) { 155 | ch = PeekNextSignificantCharacter(); 156 | 157 | if (Char.IsDigit(ch) || (ch == '.')) { 158 | hasDecimal = hasDecimal || (ch == '.'); 159 | 160 | _reader.Read(); 161 | sb.Append(ch); 162 | } 163 | else { 164 | break; 165 | } 166 | } 167 | 168 | string s = sb.ToString(); 169 | if (hasDecimal) { 170 | float value; 171 | if (Single.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out value)) { 172 | return value; 173 | } 174 | } 175 | else { 176 | int value; 177 | if (Int32.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out value)) { 178 | return value; 179 | } 180 | else { 181 | long lvalue; 182 | if (Int64.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out lvalue)) { 183 | return lvalue; 184 | } 185 | } 186 | } 187 | 188 | throw new FormatException("Invalid numeric literal."); 189 | } 190 | 191 | private JsonObject ReadObject() { 192 | JsonObject record = new JsonObject(); 193 | IDictionary recordItems = (IDictionary)record; 194 | 195 | // Consume the '{' 196 | _reader.Read(); 197 | 198 | while (true) { 199 | char ch = PeekNextSignificantCharacter(); 200 | if (ch == '\0') { 201 | throw new FormatException("Unterminated object literal."); 202 | } 203 | 204 | if (ch == '}') { 205 | _reader.Read(); 206 | return record; 207 | } 208 | 209 | if (recordItems.Count != 0) { 210 | if (ch != ',') { 211 | throw new FormatException("Invalid object literal."); 212 | } 213 | else { 214 | _reader.Read(); 215 | } 216 | } 217 | 218 | string name = ReadName(/* allowQuotes */ true); 219 | ch = PeekNextSignificantCharacter(); 220 | 221 | if (ch != ':') { 222 | throw new FormatException("Unexpected name/value pair syntax in object literal."); 223 | } 224 | else { 225 | _reader.Read(); 226 | } 227 | 228 | object item = ReadValue(); 229 | recordItems[name] = item; 230 | } 231 | } 232 | 233 | private string ReadString() { 234 | bool dummy; 235 | return ReadString(out dummy); 236 | } 237 | 238 | private string ReadString(out bool hasLeadingSlash) { 239 | StringBuilder sb = new StringBuilder(); 240 | 241 | char endQuoteCharacter = (char)_reader.Read(); 242 | bool inEscape = false; 243 | bool firstCharacter = true; 244 | 245 | hasLeadingSlash = false; 246 | 247 | while (true) { 248 | char ch = GetNextCharacter(); 249 | if (ch == '\0') { 250 | throw new FormatException("Unterminated string literal."); 251 | } 252 | if (firstCharacter) { 253 | if (ch == '\\') { 254 | hasLeadingSlash = true; 255 | } 256 | firstCharacter = false; 257 | } 258 | 259 | if (inEscape) { 260 | if (ch == 'u') { 261 | string unicodeSequence = GetCharacters(4); 262 | if (unicodeSequence == null) { 263 | throw new FormatException("Unterminated string literal."); 264 | } 265 | ch = (char)Int32.Parse(unicodeSequence, NumberStyles.HexNumber, CultureInfo.InvariantCulture); 266 | } 267 | else if (ch == 'n') { 268 | ch = '\n'; 269 | } 270 | else if (ch == 't') { 271 | ch = '\t'; 272 | } 273 | else if (ch == 'r') { 274 | ch = '\r'; 275 | } 276 | 277 | sb.Append(ch); 278 | inEscape = false; 279 | continue; 280 | } 281 | 282 | if (ch == '\\') { 283 | inEscape = true; 284 | continue; 285 | } 286 | 287 | if (ch == endQuoteCharacter) { 288 | return sb.ToString(); 289 | } 290 | 291 | sb.Append(ch); 292 | } 293 | } 294 | 295 | public object ReadValue() { 296 | object value = null; 297 | bool allowNull = false; 298 | 299 | char ch = PeekNextSignificantCharacter(); 300 | if (ch == '[') { 301 | value = ReadArray(); 302 | } 303 | else if (ch == '{') { 304 | value = ReadObject(); 305 | } 306 | else if ((ch == '\'') || (ch == '"')) { 307 | bool hasLeadingSlash; 308 | string s = ReadString(out hasLeadingSlash); 309 | if (hasLeadingSlash && s.StartsWith("@") && s.EndsWith("@")) { 310 | long ticks; 311 | 312 | if (Int64.TryParse(s.Substring(1, s.Length - 2), NumberStyles.Any, CultureInfo.InvariantCulture, out ticks)) { 313 | value = new DateTime(ticks * 10000 + JsonReader.MinDateTimeTicks, DateTimeKind.Utc); 314 | } 315 | } 316 | 317 | if (value == null) { 318 | value = s; 319 | } 320 | } 321 | else if (Char.IsDigit(ch) || (ch == '-') || (ch == '.')) { 322 | value = ReadNumber(); 323 | } 324 | else if ((ch == 't') || (ch == 'f')) { 325 | value = ReadBoolean(); 326 | } 327 | else if (ch == 'n') { 328 | ReadNull(); 329 | allowNull = true; 330 | } 331 | 332 | if ((value == null) && (allowNull == false)) { 333 | throw new FormatException("Invalid JSON text."); 334 | } 335 | return value; 336 | } 337 | } 338 | } 339 | -------------------------------------------------------------------------------- /DynamicRest/Json/JsonWriter.cs: -------------------------------------------------------------------------------- 1 | // JsonWriter.cs 2 | // DynamicRest provides REST service access using C# 4.0 dynamic programming. 3 | // The latest information and code for the project can be found at 4 | // https://github.com/NikhilK/dynamicrest 5 | // 6 | // This project is licensed under the BSD license. See the License.txt file for 7 | // more information. 8 | // 9 | 10 | using System; 11 | using System.Collections; 12 | using System.Collections.Generic; 13 | using System.ComponentModel; 14 | using System.Diagnostics; 15 | using System.Globalization; 16 | using System.IO; 17 | using System.Text; 18 | 19 | namespace DynamicRest.Json { 20 | 21 | // TODO: Add date serialization options 22 | // ScriptDate, Ticks, Formatted, Object 23 | 24 | public sealed class JsonWriter { 25 | 26 | private StringWriter _internalWriter; 27 | private IndentedTextWriter _writer; 28 | private Stack _scopes; 29 | 30 | public JsonWriter() 31 | : this(/* minimizeWhitespace */ false) { 32 | } 33 | 34 | public JsonWriter(bool minimizeWhitespace) 35 | : this(new StringWriter(), minimizeWhitespace) { 36 | _internalWriter = (StringWriter)_writer.Target; 37 | } 38 | 39 | public JsonWriter(TextWriter writer) 40 | : this(writer, /* minimizeWhitespace */ false) { 41 | } 42 | 43 | public JsonWriter(TextWriter writer, bool minimizeWhitespace) { 44 | _writer = new IndentedTextWriter(writer, minimizeWhitespace); 45 | _scopes = new Stack(); 46 | } 47 | 48 | public string Json { 49 | get { 50 | if (_internalWriter != null) { 51 | return _internalWriter.ToString(); 52 | } 53 | throw new InvalidOperationException("Only available when you create JsonWriter without passing in your own TextWriter."); 54 | } 55 | } 56 | 57 | public void EndScope() { 58 | if (_scopes.Count == 0) { 59 | throw new InvalidOperationException("No active scope to end."); 60 | } 61 | 62 | _writer.WriteLine(); 63 | _writer.Indent--; 64 | 65 | Scope scope = _scopes.Pop(); 66 | if (scope.Type == ScopeType.Array) { 67 | _writer.Write("]"); 68 | } 69 | else { 70 | _writer.Write("}"); 71 | } 72 | } 73 | 74 | internal static string EscapeString(string s) { 75 | if (String.IsNullOrEmpty(s)) { 76 | return String.Empty; 77 | } 78 | 79 | StringBuilder b = null; 80 | int startIndex = 0; 81 | int count = 0; 82 | for (int i = 0; i < s.Length; i++) { 83 | char c = s[i]; 84 | 85 | // Append the unhandled characters (that do not require special treament) 86 | // to the string builder when special characters are detected. 87 | if (c == '\n' || c == '\t' || c == '\"' || 88 | c == '\\' || c == '\r' || c < ' ' || c > 0x7F) { 89 | if (b == null) { 90 | b = new StringBuilder(s.Length + 6); 91 | } 92 | 93 | if (count > 0) { 94 | b.Append(s, startIndex, count); 95 | } 96 | 97 | startIndex = i + 1; 98 | count = 0; 99 | } 100 | 101 | switch (c) { 102 | case '\r': 103 | b.Append("\\r"); 104 | break; 105 | case '\t': 106 | b.Append("\\t"); 107 | break; 108 | case '\"': 109 | b.Append("\\\""); 110 | break; 111 | case '\\': 112 | b.Append("\\\\"); 113 | break; 114 | case '\n': 115 | b.Append("\\n"); 116 | break; 117 | default: 118 | if ((c < ' ') || (c > 0x7F)) { 119 | b.AppendFormat(CultureInfo.InvariantCulture, "\\u{0:x4}", (int)c); 120 | } 121 | else { 122 | count++; 123 | } 124 | break; 125 | } 126 | } 127 | 128 | string processedString = s; 129 | if (b != null) { 130 | if (count > 0) { 131 | b.Append(s, startIndex, count); 132 | } 133 | processedString = b.ToString(); 134 | } 135 | 136 | return processedString; 137 | } 138 | 139 | public void StartArrayScope() { 140 | StartScope(ScopeType.Array); 141 | } 142 | 143 | public void StartObjectScope() { 144 | StartScope(ScopeType.Object); 145 | } 146 | 147 | private void StartScope(ScopeType type) { 148 | if (_scopes.Count != 0) { 149 | Scope currentScope = _scopes.Peek(); 150 | if ((currentScope.Type == ScopeType.Array) && 151 | (currentScope.ObjectCount != 0)) { 152 | _writer.WriteTrimmed(", "); 153 | } 154 | 155 | currentScope.ObjectCount++; 156 | } 157 | 158 | Scope scope = new Scope(type); 159 | _scopes.Push(scope); 160 | 161 | if (type == ScopeType.Array) { 162 | _writer.Write("["); 163 | } 164 | else { 165 | _writer.Write("{"); 166 | } 167 | _writer.Indent++; 168 | _writer.WriteLine(); 169 | } 170 | 171 | public void WriteName(string name) { 172 | if (String.IsNullOrEmpty(name)) { 173 | throw new ArgumentNullException("name"); 174 | } 175 | if (_scopes.Count == 0) { 176 | throw new InvalidOperationException("No active scope to write into."); 177 | } 178 | if (_scopes.Peek().Type != ScopeType.Object) { 179 | throw new InvalidOperationException("Names can only be written into Object scopes."); 180 | } 181 | 182 | Scope currentScope = _scopes.Peek(); 183 | if (currentScope.Type == ScopeType.Object) { 184 | if (currentScope.ObjectCount != 0) { 185 | _writer.WriteTrimmed(", "); 186 | } 187 | 188 | currentScope.ObjectCount++; 189 | } 190 | 191 | _writer.Write("\""); 192 | _writer.Write(name); 193 | _writer.WriteTrimmed("\": "); 194 | } 195 | 196 | private void WriteCore(string text, bool quotes) { 197 | if (_scopes.Count != 0) { 198 | Scope currentScope = _scopes.Peek(); 199 | if (currentScope.Type == ScopeType.Array) { 200 | if (currentScope.ObjectCount != 0) { 201 | _writer.WriteTrimmed(", "); 202 | } 203 | 204 | currentScope.ObjectCount++; 205 | } 206 | } 207 | 208 | if (quotes) { 209 | _writer.Write('"'); 210 | } 211 | _writer.Write(text); 212 | if (quotes) { 213 | _writer.Write('"'); 214 | } 215 | } 216 | 217 | public void WriteValue(bool value) { 218 | WriteCore(value ? "true" : "false", /* quotes */ false); 219 | } 220 | 221 | public void WriteValue(int value) { 222 | WriteCore(value.ToString(CultureInfo.InvariantCulture), /* quotes */ false); 223 | } 224 | 225 | public void WriteValue(float value) { 226 | WriteCore(value.ToString(CultureInfo.InvariantCulture), /* quotes */ false); 227 | } 228 | 229 | public void WriteValue(double value) { 230 | WriteCore(value.ToString(CultureInfo.InvariantCulture), /* quotes */ false); 231 | } 232 | 233 | public void WriteValue(DateTime dateTime) { 234 | if (dateTime < JsonReader.MinDate) { 235 | throw new ArgumentOutOfRangeException("dateTime"); 236 | } 237 | 238 | long value = ((dateTime.Ticks - JsonReader.MinDateTimeTicks) / 10000); 239 | WriteCore("\\@" + value.ToString(CultureInfo.InvariantCulture) + "@", /* quotes */ true); 240 | } 241 | 242 | public void WriteValue(string s) { 243 | if (s == null) { 244 | WriteCore("null", /* quotes */ false); 245 | } 246 | else { 247 | WriteCore(EscapeString(s), /* quotes */ true); 248 | } 249 | } 250 | 251 | public void WriteValue(ICollection items) { 252 | if ((items == null) || (items.Count == 0)) { 253 | WriteCore("[]", /* quotes */ false); 254 | } 255 | else { 256 | StartArrayScope(); 257 | 258 | foreach (object o in items) { 259 | WriteValue(o); 260 | } 261 | 262 | EndScope(); 263 | } 264 | } 265 | 266 | public void WriteValue(IDictionary record) { 267 | if ((record == null) || (record.Count == 0)) { 268 | WriteCore("{}", /* quotes */ false); 269 | } 270 | else { 271 | StartObjectScope(); 272 | 273 | foreach (DictionaryEntry entry in record) { 274 | string name = entry.Key as string; 275 | if (String.IsNullOrEmpty(name)) { 276 | throw new ArgumentException("Key of unsupported type contained in Hashtable."); 277 | } 278 | 279 | WriteName(name); 280 | WriteValue(entry.Value); 281 | } 282 | 283 | EndScope(); 284 | } 285 | } 286 | 287 | public void WriteValue(object o) { 288 | if (o == null) { 289 | WriteCore("null", /* quotes */ false); 290 | } 291 | else if (o is bool) { 292 | WriteValue((bool)o); 293 | } 294 | else if (o is int) { 295 | WriteValue((int)o); 296 | } 297 | else if (o is float) { 298 | WriteValue((float)o); 299 | } 300 | else if (o is double) { 301 | WriteValue((double)o); 302 | } 303 | else if (o is DateTime) { 304 | WriteValue((DateTime)o); 305 | } 306 | else if (o is string) { 307 | WriteValue((string)o); 308 | } 309 | else if (o is IDictionary) { 310 | WriteValue((IDictionary)o); 311 | } 312 | else if (o is ICollection) { 313 | WriteValue((ICollection)o); 314 | } 315 | else { 316 | StartObjectScope(); 317 | 318 | PropertyDescriptorCollection propDescs = TypeDescriptor.GetProperties(o); 319 | foreach (PropertyDescriptor propDesc in propDescs) { 320 | WriteName(propDesc.Name); 321 | WriteValue(propDesc.GetValue(o)); 322 | } 323 | 324 | EndScope(); 325 | } 326 | } 327 | 328 | 329 | private enum ScopeType { 330 | 331 | Array = 0, 332 | 333 | Object = 1 334 | } 335 | 336 | private sealed class Scope { 337 | 338 | private int _objectCount; 339 | private ScopeType _type; 340 | 341 | public Scope(ScopeType type) { 342 | _type = type; 343 | } 344 | 345 | public int ObjectCount { 346 | get { 347 | return _objectCount; 348 | } 349 | set { 350 | _objectCount = value; 351 | } 352 | } 353 | 354 | public ScopeType Type { 355 | get { 356 | return _type; 357 | } 358 | } 359 | } 360 | 361 | 362 | private sealed class IndentedTextWriter : TextWriter { 363 | 364 | private TextWriter _writer; 365 | private bool _minimize; 366 | 367 | private int _indentLevel; 368 | private bool _tabsPending; 369 | private string _tabString; 370 | 371 | public IndentedTextWriter(TextWriter writer, bool minimize) 372 | : base(CultureInfo.InvariantCulture) { 373 | _writer = writer; 374 | _minimize = minimize; 375 | 376 | if (_minimize) { 377 | NewLine = "\r"; 378 | } 379 | 380 | _tabString = " "; 381 | _indentLevel = 0; 382 | _tabsPending = false; 383 | } 384 | 385 | public override Encoding Encoding { 386 | get { 387 | return _writer.Encoding; 388 | } 389 | } 390 | 391 | public override string NewLine { 392 | get { 393 | return _writer.NewLine; 394 | } 395 | set { 396 | _writer.NewLine = value; 397 | } 398 | } 399 | 400 | public int Indent { 401 | get { 402 | return _indentLevel; 403 | } 404 | set { 405 | Debug.Assert(value >= 0); 406 | if (value < 0) { 407 | value = 0; 408 | } 409 | _indentLevel = value; 410 | } 411 | } 412 | 413 | public TextWriter Target { 414 | get { 415 | return _writer; 416 | } 417 | } 418 | 419 | public override void Close() { 420 | _writer.Close(); 421 | } 422 | 423 | public override void Flush() { 424 | _writer.Flush(); 425 | } 426 | 427 | private void OutputTabs() { 428 | if (_tabsPending) { 429 | if (_minimize == false) { 430 | for (int i = 0; i < _indentLevel; i++) { 431 | _writer.Write(_tabString); 432 | } 433 | } 434 | _tabsPending = false; 435 | } 436 | } 437 | 438 | public override void Write(string s) { 439 | OutputTabs(); 440 | _writer.Write(s); 441 | } 442 | 443 | public override void Write(bool value) { 444 | OutputTabs(); 445 | _writer.Write(value); 446 | } 447 | 448 | public override void Write(char value) { 449 | OutputTabs(); 450 | _writer.Write(value); 451 | } 452 | 453 | public override void Write(char[] buffer) { 454 | OutputTabs(); 455 | _writer.Write(buffer); 456 | } 457 | 458 | public override void Write(char[] buffer, int index, int count) { 459 | OutputTabs(); 460 | _writer.Write(buffer, index, count); 461 | } 462 | 463 | public override void Write(double value) { 464 | OutputTabs(); 465 | _writer.Write(value); 466 | } 467 | 468 | public override void Write(float value) { 469 | OutputTabs(); 470 | _writer.Write(value); 471 | } 472 | 473 | public override void Write(int value) { 474 | OutputTabs(); 475 | _writer.Write(value); 476 | } 477 | 478 | public override void Write(long value) { 479 | OutputTabs(); 480 | _writer.Write(value); 481 | } 482 | 483 | public override void Write(object value) { 484 | OutputTabs(); 485 | _writer.Write(value); 486 | } 487 | 488 | public override void Write(string format, object arg0) { 489 | OutputTabs(); 490 | _writer.Write(format, arg0); 491 | } 492 | 493 | public override void Write(string format, object arg0, object arg1) { 494 | OutputTabs(); 495 | _writer.Write(format, arg0, arg1); 496 | } 497 | 498 | public override void Write(string format, params object[] arg) { 499 | OutputTabs(); 500 | _writer.Write(format, arg); 501 | } 502 | 503 | public void WriteLineNoTabs(string s) { 504 | _writer.WriteLine(s); 505 | } 506 | 507 | public override void WriteLine(string s) { 508 | OutputTabs(); 509 | _writer.WriteLine(s); 510 | _tabsPending = true; 511 | } 512 | 513 | public override void WriteLine() { 514 | OutputTabs(); 515 | _writer.WriteLine(); 516 | _tabsPending = true; 517 | } 518 | 519 | public override void WriteLine(bool value) { 520 | OutputTabs(); 521 | _writer.WriteLine(value); 522 | _tabsPending = true; 523 | } 524 | 525 | public override void WriteLine(char value) { 526 | OutputTabs(); 527 | _writer.WriteLine(value); 528 | _tabsPending = true; 529 | } 530 | 531 | public override void WriteLine(char[] buffer) { 532 | OutputTabs(); 533 | _writer.WriteLine(buffer); 534 | _tabsPending = true; 535 | } 536 | 537 | public override void WriteLine(char[] buffer, int index, int count) { 538 | OutputTabs(); 539 | _writer.WriteLine(buffer, index, count); 540 | _tabsPending = true; 541 | } 542 | 543 | public override void WriteLine(double value) { 544 | OutputTabs(); 545 | _writer.WriteLine(value); 546 | _tabsPending = true; 547 | } 548 | 549 | public override void WriteLine(float value) { 550 | OutputTabs(); 551 | _writer.WriteLine(value); 552 | _tabsPending = true; 553 | } 554 | 555 | public override void WriteLine(int value) { 556 | OutputTabs(); 557 | _writer.WriteLine(value); 558 | _tabsPending = true; 559 | } 560 | 561 | public override void WriteLine(long value) { 562 | OutputTabs(); 563 | _writer.WriteLine(value); 564 | _tabsPending = true; 565 | } 566 | 567 | public override void WriteLine(object value) { 568 | OutputTabs(); 569 | _writer.WriteLine(value); 570 | _tabsPending = true; 571 | } 572 | 573 | public override void WriteLine(string format, object arg0) { 574 | OutputTabs(); 575 | _writer.WriteLine(format, arg0); 576 | _tabsPending = true; 577 | } 578 | 579 | public override void WriteLine(string format, object arg0, object arg1) { 580 | OutputTabs(); 581 | _writer.WriteLine(format, arg0, arg1); 582 | _tabsPending = true; 583 | } 584 | 585 | public override void WriteLine(string format, params object[] arg) { 586 | OutputTabs(); 587 | _writer.WriteLine(format, arg); 588 | _tabsPending = true; 589 | } 590 | 591 | public override void WriteLine(UInt32 value) { 592 | OutputTabs(); 593 | _writer.WriteLine(value); 594 | _tabsPending = true; 595 | } 596 | 597 | public void WriteSignificantNewLine() { 598 | WriteLine(); 599 | } 600 | 601 | public void WriteNewLine() { 602 | if (_minimize == false) { 603 | WriteLine(); 604 | } 605 | } 606 | 607 | public void WriteTrimmed(string text) { 608 | if (_minimize == false) { 609 | Write(text); 610 | } 611 | else { 612 | Write(text.Trim()); 613 | } 614 | } 615 | } 616 | } 617 | } 618 | -------------------------------------------------------------------------------- /DynamicRest/ParametersStore.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace DynamicRest { 4 | 5 | public class ParametersStore { 6 | 7 | private Dictionary _parameters = new Dictionary(); 8 | 9 | public object GetParameter(string parameterName) { 10 | return _parameters.ContainsKey(parameterName) ? _parameters[parameterName] : null; 11 | } 12 | 13 | public void SetParameter(string key, object value) { 14 | if (Contains(key)) 15 | { 16 | _parameters[key] = value; 17 | } 18 | 19 | _parameters.Add(key, value); 20 | } 21 | 22 | public bool Contains(string key) { 23 | return _parameters.ContainsKey(key); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /DynamicRest/ResponseProcessor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.IO.Compression; 4 | using System.Net; 5 | using DynamicRest.HTTPInterfaces; 6 | 7 | namespace DynamicRest 8 | { 9 | public class ResponseProcessor : IProcessResponses 10 | { 11 | private readonly IBuildDynamicResults _builder; 12 | 13 | public ResponseProcessor(IBuildDynamicResults resultBuilder) 14 | { 15 | _builder = resultBuilder; 16 | } 17 | 18 | public void Process(IHttpResponse webResponse, RestOperation operation) 19 | { 20 | try 21 | { 22 | var responseStream = webResponse.GetResponseStream(); 23 | if (_builder.ServiceType == RestService.Binary) 24 | { 25 | ProcessResponseStream(webResponse, responseStream, operation); 26 | } 27 | else 28 | { 29 | using (responseStream) 30 | { 31 | if (IsGzippedReponse(webResponse.Headers["Content-Encoding"])) 32 | { 33 | using (var gzipResponseStream = new GZipStream(responseStream, CompressionMode.Decompress)) 34 | { 35 | ProcessResponseStream(webResponse, gzipResponseStream, operation); 36 | } 37 | } 38 | else 39 | { 40 | ProcessResponseStream(webResponse, responseStream, operation); 41 | } 42 | } 43 | } 44 | } 45 | catch(Exception e) 46 | { 47 | operation.Complete(null, new WebException(e.Message, e), 48 | webResponse.StatusCode, webResponse.StatusDescription, webResponse.Headers, null); 49 | } 50 | } 51 | 52 | private static bool IsGzippedReponse(string contentEncodingHeader) 53 | { 54 | if (!string.IsNullOrEmpty(contentEncodingHeader)) 55 | { 56 | if (contentEncodingHeader.ToLower().Contains("gzip")) 57 | { 58 | return true; 59 | } 60 | } 61 | 62 | return false; 63 | } 64 | 65 | private void ProcessResponseStream(IHttpResponse webResponse, Stream responseStream, RestOperation operation) 66 | { 67 | if (webResponse.StatusCode == HttpStatusCode.OK || webResponse.StatusCode == HttpStatusCode.Created) 68 | { 69 | BuilderResponse result = _builder.ProcessResponse(responseStream); 70 | operation.Complete(result.Result, null, 71 | webResponse.StatusCode, webResponse.StatusDescription, webResponse.Headers, result.ResponseText); 72 | } 73 | else 74 | { 75 | BuilderResponse result = _builder.ProcessResponse(responseStream); 76 | operation.Complete(result.Result, new WebException(webResponse.StatusDescription), 77 | webResponse.StatusCode, webResponse.StatusDescription, webResponse.Headers, result.ResponseText); 78 | } 79 | } 80 | } 81 | 82 | } -------------------------------------------------------------------------------- /DynamicRest/RestCallback.cs: -------------------------------------------------------------------------------- 1 | // RestCallback.cs 2 | // DynamicRest provides REST service access using C# 4.0 dynamic programming. 3 | // The latest information and code for the project can be found at 4 | // https://github.com/NikhilK/dynamicrest 5 | // 6 | // This project is licensed under the BSD license. See the License.txt file for 7 | // more information. 8 | // 9 | 10 | namespace DynamicRest { 11 | 12 | public delegate void RestCallback(); 13 | } 14 | -------------------------------------------------------------------------------- /DynamicRest/RestClient.cs: -------------------------------------------------------------------------------- 1 | // RestClient.cs 2 | // DynamicRest provides REST service access using C# 4.0 dynamic programming. 3 | // The latest information and code for the project can be found at 4 | // https://github.com/NikhilK/dynamicrest 5 | // 6 | // This project is licensed under the BSD license. See the License.txt file for 7 | // more information. 8 | // 9 | 10 | using System; 11 | using System.Dynamic; 12 | using System.Net; 13 | using DynamicRest.HTTPInterfaces; 14 | using DynamicRest.HTTPInterfaces.WebWrappers; 15 | using DynamicRest.Json; 16 | 17 | namespace DynamicRest { 18 | 19 | public sealed class RestClient : DynamicObject { 20 | 21 | private readonly string _operationGroup; 22 | private readonly WebHeaderCollection _responseHeaders = new WebHeaderCollection(); 23 | private readonly IBuildRequests _requestBuilder; 24 | private readonly IProcessResponses _responseProcessor; 25 | 26 | public RestClient(IBuildRequests requestBuilder, IProcessResponses responseProcessor) { 27 | _responseProcessor = responseProcessor; 28 | _requestBuilder = requestBuilder; 29 | } 30 | 31 | private RestClient(RestClient restClient, string operationGroup) 32 | : this(restClient._requestBuilder, restClient._responseProcessor) { 33 | _operationGroup = operationGroup; 34 | } 35 | 36 | private RestOperation PerformOperation(string operationName, IProcessResponses responseProcessor, params object[] args) { 37 | JsonObject argsObject = null; 38 | if ((args != null) && (args.Length != 0)) { 39 | argsObject = (JsonObject)args[0]; 40 | } 41 | 42 | var operation = new RestOperation(); 43 | 44 | IHttpRequest webRequest = _requestBuilder.CreateRequest(operationName, argsObject); 45 | 46 | InterpretResponse(responseProcessor, operation, () => webRequest.GetResponse()); 47 | 48 | return operation; 49 | } 50 | 51 | private RestOperation PerformOperationAsync(string operationName, IProcessResponses responseProcessor, params object[] args) { 52 | JsonObject argsObject = null; 53 | if ((args != null) && (args.Length != 0)) { 54 | argsObject = (JsonObject)args[0]; 55 | } 56 | 57 | var operation = new RestOperation(); 58 | 59 | IHttpRequest webRequest = _requestBuilder.CreateRequest(operationName, argsObject); 60 | 61 | webRequest.BeginGetResponse(ar => InterpretResponse(responseProcessor, operation, () => webRequest.EndGetResponse(ar)), null); 62 | 63 | return operation; 64 | } 65 | 66 | private void InterpretResponse(IProcessResponses responseProcessor, RestOperation operation, Func returnsResponse) 67 | { 68 | try { 69 | var webResponse = returnsResponse(); 70 | responseProcessor.Process(webResponse, operation); 71 | _responseHeaders.Add(webResponse.Headers); 72 | } 73 | catch (WebException webException) 74 | { 75 | if (webException.Status != WebExceptionStatus.ProtocolError) 76 | { 77 | throw; 78 | } 79 | var response = (HttpWebResponse) webException.Response; 80 | var responseWrapper = new HttpWebResponseWrapper(response); 81 | responseProcessor.Process(responseWrapper, operation); 82 | _responseHeaders.Add(response.Headers); 83 | } 84 | } 85 | 86 | public override bool TryGetMember(GetMemberBinder binder, out object result) { 87 | object value = _requestBuilder.ParametersStore.GetParameter(binder.Name); 88 | if (value != null) { 89 | result = value; 90 | return true; 91 | } 92 | 93 | string operationGroup = binder.Name; 94 | if (_operationGroup != null) { 95 | operationGroup = _operationGroup + "." + operationGroup; 96 | } 97 | 98 | var operationGroupClient = new RestClient(this, operationGroup); 99 | 100 | result = operationGroupClient; 101 | return true; 102 | } 103 | 104 | public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { 105 | bool async = false; 106 | 107 | string operation = binder.Name; 108 | if (operation.EndsWith("Async", StringComparison.Ordinal)) { 109 | async = true; 110 | operation = operation.Substring(0, operation.Length - 5); 111 | } 112 | 113 | if (_operationGroup != null) { 114 | operation = _operationGroup + "." + operation; 115 | } 116 | 117 | if (async == false) { 118 | result = PerformOperation(operation, _responseProcessor, args); 119 | } 120 | else { 121 | result = PerformOperationAsync(operation, _responseProcessor, args); 122 | } 123 | return true; 124 | } 125 | 126 | public override bool TrySetMember(SetMemberBinder binder, object value) { 127 | _requestBuilder.ParametersStore.SetParameter(binder.Name, value); 128 | return true; 129 | } 130 | 131 | public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) { 132 | if(indexes.Length == 1 && indexes[0].GetType() == typeof(HttpResponseHeader)) { 133 | result = _responseHeaders[(HttpResponseHeader)indexes[0]]; 134 | return true; 135 | } 136 | return base.TryGetIndex(binder, indexes, out result); 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /DynamicRest/RestOperation.cs: -------------------------------------------------------------------------------- 1 | // RestOperation.cs 2 | // DynamicRest provides REST service access using C# 4.0 dynamic programming. 3 | // The latest information and code for the project can be found at 4 | // https://github.com/NikhilK/dynamicrest 5 | // 6 | // This project is licensed under the BSD license. See the License.txt file for 7 | // more information. 8 | // 9 | 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Net; 13 | using System.Threading; 14 | 15 | namespace DynamicRest { 16 | 17 | public class RestOperation { 18 | 19 | private object _result; 20 | private Exception _error; 21 | private HttpStatusCode _statusCode; 22 | private string _statusMessage; 23 | private bool _completed; 24 | private SynchronizationContext _syncContext; 25 | private List _callbacks; 26 | private WebHeaderCollection _responseHeaders; 27 | 28 | protected internal RestOperation() { 29 | _syncContext = SynchronizationContext.Current; 30 | } 31 | 32 | public Exception Error { 33 | get { 34 | return _error; 35 | } 36 | } 37 | 38 | public bool IsCompleted { 39 | get { 40 | return _completed; 41 | } 42 | } 43 | 44 | public dynamic Result { 45 | get { 46 | return _result; 47 | } 48 | } 49 | 50 | public HttpStatusCode StatusCode { 51 | get { 52 | return _statusCode; 53 | } 54 | } 55 | 56 | public string StatusMessage { 57 | get { 58 | return _statusMessage; 59 | } 60 | } 61 | 62 | public string ResponseText { get; set; } 63 | 64 | public void Callback(RestCallback callback) { 65 | if (callback == null) { 66 | throw new ArgumentNullException("callback"); 67 | } 68 | 69 | if (_completed) { 70 | callback(); 71 | } 72 | 73 | if (_callbacks == null) { 74 | _callbacks = new List(); 75 | } 76 | _callbacks.Add(callback); 77 | } 78 | 79 | protected internal void Complete(object result, Exception error, HttpStatusCode statusCode, string statusMessage, WebHeaderCollection headers, string responseText) { 80 | _result = result; 81 | _error = error; 82 | _statusCode = statusCode; 83 | _statusMessage = statusMessage; 84 | _completed = true; 85 | _responseHeaders = headers; 86 | ResponseText = responseText; 87 | 88 | if (_callbacks != null) { 89 | RestCallback[] callbacksCopy = _callbacks.ToArray(); 90 | _callbacks.Clear(); 91 | 92 | if (_syncContext == null) { 93 | InvokeCallbacks(callbacksCopy); 94 | } 95 | else { 96 | _syncContext.Post(InvokeCallbacks, callbacksCopy); 97 | } 98 | } 99 | } 100 | 101 | private static void InvokeCallbacks(object state) { 102 | RestCallback[] callbacks = (RestCallback[])state; 103 | 104 | foreach (RestCallback callback in callbacks) { 105 | callback(); 106 | } 107 | } 108 | 109 | public string GetResponseHeader(HttpResponseHeader headerType) { 110 | return _responseHeaders[headerType]; 111 | } 112 | 113 | public string GetResponseHeader(string headername) 114 | { 115 | return _responseHeaders[headername]; 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /DynamicRest/RestService.cs: -------------------------------------------------------------------------------- 1 | // RestService.cs 2 | // DynamicRest provides REST service access using C# 4.0 dynamic programming. 3 | // The latest information and code for the project can be found at 4 | // https://github.com/NikhilK/dynamicrest 5 | // 6 | // This project is licensed under the BSD license. See the License.txt file for 7 | // more information. 8 | // 9 | 10 | namespace DynamicRest { 11 | 12 | public enum RestService { 13 | Json, 14 | Xml, 15 | Binary, 16 | Text 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /DynamicRest/StandardResultBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Xml.Linq; 3 | using DynamicRest.Json; 4 | using DynamicRest.Xml; 5 | 6 | namespace DynamicRest { 7 | public class BuilderResponse 8 | { 9 | public object Result { get; set; } 10 | public string ResponseText { get; set; } 11 | 12 | public BuilderResponse(object result) 13 | { 14 | Result = result; 15 | } 16 | public BuilderResponse(object result, string responseText) : this(result) 17 | { 18 | ResponseText = responseText; 19 | } 20 | } 21 | 22 | public class StandardResultBuilder : IBuildDynamicResults 23 | { 24 | public StandardResultBuilder(RestService serviceType) { 25 | ServiceType = serviceType; 26 | } 27 | 28 | public RestService ServiceType { get; private set; } 29 | 30 | public object CreateResult(string responseText) 31 | { 32 | object result; 33 | switch (ServiceType) 34 | { 35 | case RestService.Json: 36 | result = GetResultFromJson(responseText); 37 | break; 38 | case RestService.Text: 39 | result = GetResultFromText(responseText); 40 | break; 41 | default: 42 | result = GetResultFromXml(responseText); 43 | break; 44 | } 45 | return result; 46 | } 47 | 48 | public static object GetResultFromText(string responseText) 49 | { 50 | return responseText ?? ""; 51 | } 52 | 53 | public static object GetResultFromXml(string responseText) { 54 | var xmlDocument = XDocument.Parse(responseText); 55 | dynamic result = new XmlNode(xmlDocument.Root); 56 | return result; 57 | } 58 | 59 | public static object GetResultFromJson(string responseText) { 60 | var jsonReader = new JsonReader(responseText); 61 | 62 | if (responseText == string.Empty) return new JsonObject(); 63 | 64 | dynamic result = jsonReader.ReadValue(); 65 | return result; 66 | } 67 | 68 | public BuilderResponse ProcessResponse(Stream responseStream) 69 | { 70 | if (ServiceType == RestService.Binary) { 71 | return new BuilderResponse(responseStream); 72 | } 73 | 74 | try { 75 | var responseText = (new StreamReader(responseStream)).ReadToEnd(); 76 | dynamic result = CreateResult(responseText); 77 | 78 | return new BuilderResponse(result, responseText); 79 | } 80 | catch 81 | { 82 | // ignored 83 | } 84 | 85 | return null; 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /DynamicRest/TemplatedUriBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Net; 5 | using System.Text; 6 | using System.Text.RegularExpressions; 7 | 8 | using DynamicRest.Json; 9 | 10 | namespace DynamicRest { 11 | 12 | internal class TemplatedUriBuilder { 13 | 14 | private static readonly Regex TokenFormatRewriteRegex = 15 | new Regex(@"(?\{)+(?[\w\.\[\]]+)(?:[^}]+)?(?\})+", 16 | RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant); 17 | 18 | private IRestUriTransformer _uriTransformer; 19 | private HashSet _addedParameters; 20 | 21 | public string UriTemplate { get; set; } 22 | 23 | public ParametersStore ParametersStore { private get; set; } 24 | 25 | public Uri CreateRequestUri(string operationName, JsonObject parameters) { 26 | var uriBuilder = new StringBuilder(); 27 | 28 | BuildBaseUri(operationName, uriBuilder); 29 | 30 | AddQueryString(uriBuilder, parameters); 31 | 32 | Uri uri = Transform(uriBuilder); 33 | 34 | return uri; 35 | } 36 | 37 | public void SetUriTransformer(IRestUriTransformer uriTransformer) { 38 | _uriTransformer = uriTransformer; 39 | } 40 | 41 | private void BuildBaseUri(string operationName, StringBuilder uriBuilder) { 42 | var values = new List(); 43 | _addedParameters = null; 44 | 45 | string rewrittenUriFormat = TokenFormatRewriteRegex.Replace(UriTemplate, delegate(Match m) { 46 | Group startGroup = m.Groups["start"]; 47 | Group propertyGroup = m.Groups["property"]; 48 | Group formatGroup = m.Groups["format"]; 49 | Group endGroup = m.Groups["end"]; 50 | 51 | if ((operationName.Length != 0) && String.CompareOrdinal(propertyGroup.Value, "operation") == 0) { 52 | values.Add(operationName); 53 | } 54 | else if (this.ParametersStore != null && this.ParametersStore.Contains(propertyGroup.Value)) { 55 | values.Add(this.ParametersStore.GetParameter(propertyGroup.Value)); 56 | 57 | if (_addedParameters == null) { 58 | _addedParameters = new HashSet(StringComparer.Ordinal); 59 | } 60 | 61 | _addedParameters.Add(propertyGroup.Value); 62 | } 63 | 64 | return new string('{', startGroup.Captures.Count) + (values.Count - 1) + formatGroup.Value + new string('}', endGroup.Captures.Count); 65 | }); 66 | 67 | if (values.Count != 0) { 68 | uriBuilder.AppendFormat(CultureInfo.InvariantCulture, rewrittenUriFormat, values.ToArray()); 69 | } 70 | else if (UriTemplate != rewrittenUriFormat) { 71 | throw new ArgumentException(string.Format("You are missing one or more expected template parameters in the uri: {0}", UriTemplate)); 72 | } 73 | else { 74 | uriBuilder.Append(rewrittenUriFormat); 75 | } 76 | } 77 | 78 | private void AddQueryString(StringBuilder uriBuilder, JsonObject parameters){ 79 | if (parameters != null){ 80 | 81 | if (UriTemplate.IndexOf('?') < 0) { 82 | uriBuilder.Append("?"); 83 | } 84 | 85 | foreach (var param in parameters) { 86 | string name = param.Key; 87 | object value = param.Value; 88 | 89 | if (ConsumedInBaseUri(name)) { 90 | continue; 91 | } 92 | 93 | switch (value) 94 | { 95 | case Delegate _: 96 | // Ignore callbacks in the async scenario. 97 | continue; 98 | case JsonObject jsonObject: 99 | { 100 | // Nested object... use name.subName=value format. 101 | foreach (KeyValuePair nestedParam in jsonObject){ 102 | uriBuilder.AppendFormat("&{0}.{1}={2}", 103 | name, nestedParam.Key, 104 | FormatUriParameter(nestedParam.Value)); 105 | } 106 | 107 | continue; 108 | } 109 | default: 110 | uriBuilder.AppendFormat("&{0}={1}", name, FormatUriParameter(value)); 111 | break; 112 | } 113 | } 114 | } 115 | } 116 | 117 | private bool ConsumedInBaseUri(string name) { 118 | return (_addedParameters != null) && _addedParameters.Contains(name); 119 | } 120 | 121 | private Uri Transform(StringBuilder uriBuilder) { 122 | var uri = new Uri(uriBuilder.ToString(), UriKind.Absolute); 123 | if (_uriTransformer != null){ 124 | uri = _uriTransformer.TransformUri(uri); 125 | } 126 | return uri; 127 | } 128 | 129 | internal string FormatUriParameter(object value) { 130 | if (value is IEnumerable enumerable) 131 | { 132 | return string.Join("+", enumerable); 133 | } 134 | return WebUtility.UrlEncode(string.Format(CultureInfo.InvariantCulture, "{0}", value)); 135 | } 136 | 137 | } 138 | } 139 | 140 | -------------------------------------------------------------------------------- /DynamicRest/TemplatedUriRequestBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using DynamicRest.HTTPInterfaces; 4 | using DynamicRest.Json; 5 | 6 | namespace DynamicRest { 7 | 8 | public class TemplatedUriRequestBuilder : IBuildRequests { 9 | 10 | private readonly IHttpRequestFactory _requestFactory; 11 | private readonly TemplatedUriBuilder _uriBuilder = new TemplatedUriBuilder(); 12 | private readonly WebHeaderCollection _headers = new WebHeaderCollection(); 13 | private IRestUriTransformer _uriTransformer; 14 | private bool _allowAutoRedirect; 15 | DateTime? _ifModifiedSince; 16 | 17 | public string Uri { private get; set; } 18 | public string ContentType { get; set; } 19 | public string Body { get; set; } 20 | public string AcceptHeader { get; set; } 21 | public ParametersStore ParametersStore { get; set; } 22 | public ICredentials Credentials { private get; set; } 23 | public bool AllowAutoRedirect { get; set; } 24 | public string UserAgent { get; set; } 25 | public int Timeout { get; set; } 26 | public IWebProxy Proxy { get; set; } 27 | 28 | public TemplatedUriRequestBuilder(IHttpRequestFactory requestFactory) 29 | { 30 | _requestFactory = requestFactory; 31 | 32 | ParametersStore = new ParametersStore(); 33 | } 34 | 35 | public IHttpRequest CreateRequest(string operationName, JsonObject parameters) { 36 | var uri = BuildUri(operationName, parameters); 37 | 38 | var webRequest = _requestFactory.Create(uri); 39 | 40 | webRequest.AddHeaders(_headers); 41 | webRequest.AddCredentials(Credentials); 42 | webRequest.Accept = AcceptHeader; 43 | webRequest.AddRequestBody(ContentType, Body); 44 | webRequest.AllowAutoRedirect = _allowAutoRedirect; 45 | webRequest.Timeout = Timeout; 46 | 47 | if(_ifModifiedSince.HasValue) 48 | { 49 | ((HttpWebRequest)webRequest).IfModifiedSince = _ifModifiedSince.Value; 50 | } 51 | 52 | return webRequest; 53 | } 54 | 55 | public void AddHeader(HttpRequestHeader headerType, string value) { 56 | _headers.Add(headerType, value); 57 | } 58 | 59 | public void AddCustomHeader(string headerKey, string value) 60 | { 61 | _headers.Add(headerKey, value); 62 | } 63 | 64 | public void SetOAuth2AuthorizationHeader(string oAuth2Token) { 65 | _headers.Add(HttpRequestHeader.Authorization, string.Format("Bearer {0}", oAuth2Token)); 66 | } 67 | 68 | public void IfModifiedSince(DateTime ifModifiedSince) 69 | { 70 | _ifModifiedSince = ifModifiedSince; 71 | } 72 | 73 | public void SetUriTransformer(IRestUriTransformer uriTransformer) { 74 | _uriTransformer = uriTransformer; 75 | } 76 | 77 | private Uri BuildUri(string operationName, JsonObject parameters) { 78 | _uriBuilder.ParametersStore = ParametersStore; 79 | _uriBuilder.UriTemplate = this.Uri; 80 | _uriBuilder.SetUriTransformer(_uriTransformer); 81 | return _uriBuilder.CreateRequestUri(operationName, parameters); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /DynamicRest/Xml/XmlNode.cs: -------------------------------------------------------------------------------- 1 | // XmlNode.cs 2 | // DynamicRest provides REST service access using C# 4.0 dynamic programming. 3 | // The latest information and code for the project can be found at 4 | // https://github.com/NikhilK/dynamicrest 5 | // 6 | // This project is licensed under the BSD license. See the License.txt file for 7 | // more information. 8 | // 9 | 10 | using System; 11 | using System.Collections.Generic; 12 | using System.IO; 13 | using System.Linq; 14 | using System.Xml; 15 | using System.Xml.Linq; 16 | using System.Dynamic; 17 | 18 | namespace DynamicRest.Xml { 19 | 20 | public sealed class XmlNode : DynamicObject { 21 | 22 | private XElement _element; 23 | 24 | public XmlNode(string name) 25 | : this(new XElement(name)) { 26 | } 27 | 28 | public XmlNode(XElement element) { 29 | _element = element; 30 | } 31 | 32 | public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) { 33 | if (_element.HasElements) { 34 | new XmlNodeList(new[] { _element }).TryGetIndex(binder, indexes, out result); 35 | } 36 | else { 37 | result = _element.Value; 38 | } 39 | return true; 40 | } 41 | 42 | public override bool TryGetMember(GetMemberBinder binder, out object result) { 43 | string name = binder.Name; 44 | 45 | if (String.CompareOrdinal(name, "Name") == 0) { 46 | result = _element.Name.LocalName; 47 | return true; 48 | } 49 | else if (String.CompareOrdinal(name, "Parent") == 0) { 50 | XElement parent = _element.Parent; 51 | if (parent != null) { 52 | result = new XmlNode(parent); 53 | return true; 54 | } 55 | result = null; 56 | return false; 57 | } 58 | else if (String.CompareOrdinal(name, "Value") == 0) { 59 | result = _element.Value; 60 | return true; 61 | } 62 | else if (String.CompareOrdinal(name, "Count") == 0) { 63 | result = _element.Elements().Count(); 64 | return true; 65 | } 66 | else if (String.CompareOrdinal(name, "Nodes") == 0) { 67 | result = new XmlNodeList(_element.Elements()); 68 | return true; 69 | } 70 | else if (String.CompareOrdinal(name, "XElement") == 0) 71 | { 72 | result = _element; 73 | return true; 74 | } 75 | else if (String.CompareOrdinal(name, "Xml") == 0) { 76 | StringWriter sw = new StringWriter(); 77 | _element.Save(sw, SaveOptions.None); 78 | 79 | result = sw.ToString(); 80 | return true; 81 | } 82 | else { 83 | XAttribute attribute = _element.Attributes().SingleOrDefault(a => a.Name.LocalName == name); 84 | if (attribute != null) { 85 | result = attribute.Value; 86 | return true; 87 | } 88 | try { 89 | XElement childNode = _element.Elements().SingleOrDefault(a => a.Name.LocalName == name); 90 | if (childNode != null) 91 | { 92 | if (childNode.HasElements == false) 93 | { 94 | result = new XmlString(childNode.Value); 95 | return true; 96 | } 97 | result = new XmlNode(childNode); 98 | return true; 99 | } 100 | } 101 | catch (InvalidOperationException) 102 | { 103 | 104 | result = new XmlNodeList(_element.Elements().Where(a => a.Name.LocalName == name)); 105 | return true; 106 | } 107 | } 108 | 109 | var memberExists = base.TryGetMember(binder, out result); 110 | if (result == null) { 111 | throw new XmlException(string.Format("No element or attribute named '{0}' found in the response.", name)); 112 | } 113 | return memberExists; 114 | } 115 | 116 | public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { 117 | string name = binder.Name; 118 | 119 | if (String.CompareOrdinal(name, "SelectAll") == 0) { 120 | IEnumerable selectedElements = null; 121 | 122 | if (args.Length == 0) { 123 | selectedElements = _element.Descendants(); 124 | } 125 | else if (args.Length == 1) { 126 | selectedElements = _element.Descendants().Where(d => d.Name.LocalName == args[0].ToString()); 127 | } 128 | else { 129 | result = false; 130 | return false; 131 | } 132 | result = new XmlNodeList(selectedElements); 133 | return true; 134 | } 135 | else if (String.CompareOrdinal(name, "SelectChildren") == 0) { 136 | IEnumerable selectedElements = null; 137 | 138 | if (args.Length == 0) { 139 | selectedElements = _element.Elements(); 140 | } 141 | else if (args.Length == 1) { 142 | selectedElements = _element.Elements().Where(d => d.Name.LocalName == args[0].ToString()); 143 | } 144 | else { 145 | result = false; 146 | return false; 147 | } 148 | result = new XmlNodeList(selectedElements); 149 | return true; 150 | } 151 | 152 | return base.TryInvokeMember(binder, args, out result); 153 | } 154 | 155 | public override bool TrySetMember(SetMemberBinder binder, object value) { 156 | string name = binder.Name; 157 | 158 | if (String.CompareOrdinal(name, "Value") == 0) { 159 | _element.Value = (value != null) ? value.ToString() : String.Empty; 160 | return true; 161 | } 162 | 163 | return base.TrySetMember(binder, value); 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /DynamicRest/Xml/XmlNodeList.cs: -------------------------------------------------------------------------------- 1 | // XmlNodeList.cs 2 | // DynamicRest provides REST service access using C# 4.0 dynamic programming. 3 | // The latest information and code for the project can be found at 4 | // https://github.com/NikhilK/dynamicrest 5 | // 6 | // This project is licensed under the BSD license. See the License.txt file for 7 | // more information. 8 | // 9 | 10 | using System; 11 | using System.Collections; 12 | using System.Collections.Generic; 13 | using System.Xml.Linq; 14 | using System.Dynamic; 15 | 16 | namespace DynamicRest.Xml { 17 | 18 | public sealed class XmlNodeList : DynamicObject, IEnumerable { 19 | 20 | private List _elements; 21 | 22 | internal XmlNodeList(IEnumerable elements) 23 | : base() { 24 | _elements = new List(elements); 25 | } 26 | 27 | public override bool TryConvert(ConvertBinder binder, out object result) { 28 | Type targetType = binder.Type; 29 | if (targetType == typeof(IEnumerable)) { 30 | result = this; 31 | return true; 32 | } 33 | return base.TryConvert(binder, out result); 34 | } 35 | 36 | public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) { 37 | if (indexes.Length == 1) { 38 | XElement element = _elements[Convert.ToInt32(indexes[0])]; 39 | result = new XmlNode(element); 40 | return true; 41 | } 42 | 43 | return base.TryGetIndex(binder, indexes, out result); 44 | } 45 | 46 | public override bool TryGetMember(GetMemberBinder binder, out object result) { 47 | if (String.Compare("Length", binder.Name, StringComparison.Ordinal) == 0) { 48 | result = _elements.Count; 49 | return true; 50 | } 51 | 52 | return base.TryGetMember(binder, out result); 53 | } 54 | 55 | #region Implementation of IEnumerable 56 | IEnumerator IEnumerable.GetEnumerator() { 57 | return new NodeEnumerator(_elements.GetEnumerator()); 58 | } 59 | #endregion 60 | 61 | 62 | private sealed class NodeEnumerator : IEnumerator { 63 | 64 | private IEnumerator _elementEnumerator; 65 | 66 | public NodeEnumerator(IEnumerator elementEnumerator) { 67 | _elementEnumerator = elementEnumerator; 68 | } 69 | 70 | public object Current { 71 | get { 72 | XElement element = _elementEnumerator.Current; 73 | return new XmlNode(element); 74 | } 75 | } 76 | 77 | public bool MoveNext() { 78 | return _elementEnumerator.MoveNext(); 79 | } 80 | 81 | public void Reset() { 82 | _elementEnumerator.Reset(); 83 | } 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /DynamicRest/Xml/XmlString.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Dynamic; 3 | using System.Globalization; 4 | 5 | namespace DynamicRest.Xml { 6 | 7 | public class XmlString : DynamicObject { 8 | 9 | readonly string value; 10 | private const string isoDateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"; 11 | 12 | public XmlString(string value) { 13 | this.value = value; 14 | } 15 | 16 | public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) { 17 | result = value; 18 | return true; 19 | } 20 | 21 | public override bool TryGetMember(GetMemberBinder binder, out object result) { 22 | result = value; 23 | return true; 24 | } 25 | 26 | public override bool TryConvert(ConvertBinder binder, out object result) { 27 | result = value; 28 | return true; 29 | } 30 | 31 | public override bool TryCreateInstance(CreateInstanceBinder binder, object[] args, out object result) { 32 | result = value; 33 | return true; 34 | } 35 | 36 | public static implicit operator DateTime(XmlString input) 37 | { 38 | return DateTime.Parse(input.value); 39 | } 40 | 41 | public static implicit operator long(XmlString input) 42 | { 43 | return long.Parse(input.value); 44 | } 45 | 46 | public static implicit operator int(XmlString input) 47 | { 48 | return int.Parse(input.value); 49 | } 50 | 51 | public static implicit operator bool(XmlString input) 52 | { 53 | if (input.value == "0" || input.value == "-1") return false; 54 | if (input.value == "1") return true; 55 | return bool.Parse(input.value); 56 | } 57 | 58 | public static implicit operator string(XmlString input) 59 | { 60 | return input.value; 61 | } 62 | 63 | } 64 | } -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008 - 2011, Nikhil Kothari. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | - Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | - Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | - Neither the name of the DynamicRest project nor the names of its 13 | contributors may be used to endorse or promote products derived from this 14 | software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 25 | USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Dynamic REST 2 | 3 | [![Build status](https://ci.appveyor.com/api/projects/status/xt1dr93om1ukgrbw/branch/master?svg=true)](https://ci.appveyor.com/project/Huddle/dynamicrest/branch/master) 4 | 5 | This is a small project demonstrating REST service access, 6 | working against JSON data and XML data using late-bound 7 | dynamic code using the new dynamic programming features being 8 | added to c# 4.0. 9 | 10 | JSON data 11 | http://www.nikhilk.net/CSharp-Dynamic-Programming-JSON.aspx 12 | 13 | REST client 14 | http://www.nikhilk.net/CSharp-Dynamic-Programming-REST-Services.aspx 15 | 16 | ## Basic usage 17 | 18 | * To get an instance of RestClient use the RestClientBuilder fluent interface: 19 | 20 | var client = new RestClientBuilder() 21 | .WithAcceptHeader("application/json") 22 | .WithUri("http://some.uri") 23 | .WithOAuth2Token("token") 24 | .Build(); 25 | * Issue a GET 26 | 27 | var response = client.Get(); 28 | 29 | * Navigate the response 30 | 31 | Given this response: 32 | 33 | { 34 | article:{ 35 | images:[ 36 | { src:'http://some.uri/image1.png' }, 37 | { src:'http://some.uri/image2.png' } 38 | ] 39 | } 40 | } 41 | 42 | You can navigate using the following dynamic syntax: 43 | 44 | var image2src = response.Result.article.images[1].src; 45 | 46 | -------------------------------------------------------------------------------- /Samples/AmazonSample.cs: -------------------------------------------------------------------------------- 1 | // AmazonSample.cs 2 | // DynamicRest provides REST service access using C# 4.0 dynamic programming. 3 | // The latest information and code for the project can be found at 4 | // https://github.com/NikhilK/dynamicrest 5 | // 6 | // This project is licensed under the BSD license. See the License.txt file for 7 | // more information. 8 | // 9 | 10 | using System; 11 | using DynamicRest; 12 | using DynamicRest.HTTPInterfaces.WebWrappers; 13 | using DynamicRest.Json; 14 | 15 | namespace Application { 16 | 17 | internal static class AmazonSample { 18 | 19 | public static void Run() { 20 | 21 | AmazonUriSigner signer = new AmazonUriSigner(Services.AmazonAccessKey, Services.AmazonSecretKey); 22 | 23 | var templatedUriRequestBuilder = new TemplatedUriRequestBuilder(new RequestFactory()); 24 | templatedUriRequestBuilder.Uri = Services.AmazonUri; 25 | templatedUriRequestBuilder.SetUriTransformer(signer); 26 | 27 | dynamic amazon = new RestClient( 28 | templatedUriRequestBuilder, 29 | new ResponseProcessor(new StandardResultBuilder(RestService.Xml))); 30 | 31 | dynamic searchOptions = new JsonObject(); 32 | searchOptions.SearchIndex = "Books"; 33 | searchOptions.Keywords = "Dynamic Programming"; 34 | 35 | dynamic search = amazon.ItemSearch(searchOptions); 36 | dynamic bookList = search.Result; 37 | 38 | foreach (dynamic book in bookList.SelectAll("Item")) { 39 | Console.WriteLine(book.ASIN + " : " + book.ItemAttributes.Title); 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Samples/AmazonUriSigner.cs: -------------------------------------------------------------------------------- 1 | // AmazonUriSigner.cs 2 | // DynamicRest provides REST service access using C# 4.0 dynamic programming. 3 | // The latest information and code for the project can be found at 4 | // https://github.com/NikhilK/dynamicrest 5 | // 6 | // This project is licensed under the BSD license. See the License.txt file for 7 | // more information. 8 | // 9 | 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Diagnostics; 13 | using System.Globalization; 14 | using System.Security.Cryptography; 15 | using System.Text; 16 | using System.Web; 17 | using DynamicRest; 18 | 19 | namespace Application { 20 | 21 | internal sealed class AmazonUriSigner : IRestUriTransformer { 22 | 23 | private string _appKey; 24 | private byte[] _secret; 25 | private HMAC _hmac; 26 | 27 | public AmazonUriSigner(string appKey, string secretKey) { 28 | _appKey = appKey; 29 | _secret = Encoding.UTF8.GetBytes(secretKey); 30 | _hmac = new HMACSHA256(_secret); 31 | } 32 | 33 | private string ConstructQueryString(SortedDictionary parameters) { 34 | if (parameters.Count == 0) { 35 | return String.Empty; 36 | } 37 | 38 | StringBuilder queryStringBuilder = new StringBuilder(); 39 | int parameter = 0; 40 | foreach (KeyValuePair kvp in parameters) { 41 | if (parameter != 0) { 42 | queryStringBuilder.Append("&"); 43 | } 44 | 45 | queryStringBuilder.Append(kvp.Key); 46 | queryStringBuilder.Append("="); 47 | queryStringBuilder.Append(PercentEncodeRfc3986(kvp.Value)); 48 | 49 | parameter++; 50 | } 51 | 52 | return queryStringBuilder.ToString(); 53 | } 54 | 55 | private SortedDictionary ParseQueryString(string queryString) { 56 | // Use a SortedDictionary to get the parameters in natural byte order 57 | SortedDictionary parameters = 58 | new SortedDictionary(new ParameterComparer()); 59 | 60 | char[] queryStringSeparator = new char[] { '&' }; 61 | char[] parameterSeparator = new char[] { '=' }; 62 | 63 | string[] queryStringParts = queryString.Split(queryStringSeparator, StringSplitOptions.RemoveEmptyEntries); 64 | 65 | for (int i = 0; i < queryStringParts.Length; i++) { 66 | string[] nameValueParts = queryStringParts[i].Split(parameterSeparator); 67 | parameters[nameValueParts[0]] = System.Net.WebUtility.UrlDecode(nameValueParts[1]); 68 | } 69 | 70 | return parameters; 71 | } 72 | 73 | private string PercentEncodeRfc3986(string s) { 74 | // Percent-encode (URL Encode) according to RFC 3986. 75 | // This is necessary because .NET's HttpUtility.UrlEncode does not encode 76 | // according to the above standard. 77 | 78 | s = System.Net.WebUtility.UrlEncode(s)?. 79 | Replace("'", "%27"). 80 | Replace("(", "%28"). 81 | Replace(")", "%29"). 82 | Replace("*", "%2A"). 83 | Replace("!", "%21"). 84 | Replace("%7e", "~"). 85 | Replace("+", "%20"); 86 | 87 | StringBuilder sb = new StringBuilder(s); 88 | for (int i = 0; i < sb.Length; i++) { 89 | if (sb[i] == '%') { 90 | if (Char.IsDigit(sb[i + 1]) && Char.IsLetter(sb[i + 2])) { 91 | sb[i + 2] = Char.ToUpper(sb[i + 2], CultureInfo.InvariantCulture); 92 | } 93 | } 94 | } 95 | 96 | return sb.ToString(); 97 | } 98 | 99 | private string SignCore(string endPoint, string requestPath, SortedDictionary parameters) { 100 | // Add the AWSAccessKeyId and Timestamp (in IS0 8601 format) to the requests. 101 | parameters["AWSAccessKeyId"] = _appKey; 102 | parameters["Timestamp"] = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ"); 103 | 104 | string queryString = ConstructQueryString(parameters); 105 | 106 | // Derive the bytes that need to be signed 107 | // (request method, domain, path and the query string) 108 | StringBuilder dataBuilder = new StringBuilder(); 109 | dataBuilder.Append("GET"). 110 | Append("\n"). 111 | Append(endPoint). 112 | Append("\n"). 113 | Append(requestPath). 114 | Append("\n"). 115 | Append(queryString); 116 | 117 | byte[] requestBytes = Encoding.UTF8.GetBytes(dataBuilder.ToString()); 118 | 119 | // Compute the signature and convert to Base64. 120 | byte[] signedRequestBytes = _hmac.ComputeHash(requestBytes); 121 | string signature = Convert.ToBase64String(signedRequestBytes); 122 | 123 | // Construct the complete URL 124 | StringBuilder urlBuilder = new StringBuilder(); 125 | urlBuilder.Append("http://"). 126 | Append(endPoint). 127 | Append(requestPath). 128 | Append("?"). 129 | Append(queryString). 130 | Append("&Signature="). 131 | Append(PercentEncodeRfc3986(signature)); 132 | 133 | return urlBuilder.ToString(); 134 | } 135 | 136 | #region Implementation of IRestUriTransformer 137 | Uri IRestUriTransformer.TransformUri(Uri uri) { 138 | string endPoint = uri.Host.ToLowerInvariant(); 139 | string requestPath = uri.AbsolutePath; 140 | string queryString = uri.Query; 141 | if (queryString.Length > 1) { 142 | Debug.Assert(queryString.StartsWith("?")); 143 | queryString = queryString.Substring(1); 144 | } 145 | 146 | SortedDictionary query = ParseQueryString(queryString); 147 | 148 | string signedUrl = SignCore(endPoint, requestPath, query); 149 | return new Uri(signedUrl, UriKind.Absolute); 150 | } 151 | #endregion 152 | 153 | 154 | private sealed class ParameterComparer : IComparer { 155 | 156 | public int Compare(string p1, string p2) { 157 | return String.CompareOrdinal(p1, p2); 158 | } 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /Samples/BingSearchSample.cs: -------------------------------------------------------------------------------- 1 | // BingSearchSample.cs 2 | // DynamicRest provides REST service access using C# 4.0 dynamic programming. 3 | // The latest information and code for the project can be found at 4 | // https://github.com/NikhilK/dynamicrest 5 | // 6 | // This project is licensed under the BSD license. See the License.txt file for 7 | // more information. 8 | // 9 | 10 | using System; 11 | using DynamicRest; 12 | using DynamicRest.HTTPInterfaces.WebWrappers; 13 | using DynamicRest.Json; 14 | 15 | namespace Application { 16 | 17 | internal static class BingSearchSample { 18 | 19 | public static void Run() { 20 | 21 | var templatedUriRequestBuilder = new TemplatedUriRequestBuilder(new RequestFactory()); 22 | templatedUriRequestBuilder.Uri = Services.BingSearchUri; 23 | 24 | dynamic bingSearch = new RestClient(templatedUriRequestBuilder, new ResponseProcessor(new StandardResultBuilder(RestService.Json))); 25 | bingSearch.appID = Services.BingApiKey; 26 | 27 | Console.WriteLine("Searching Bing for 'seattle'..."); 28 | 29 | dynamic searchOptions = new JsonObject(); 30 | searchOptions.Query = "seattle"; 31 | searchOptions.Sources = new string[] { "Web", "Image" }; 32 | searchOptions.Web = new JsonObject("Count", 4); 33 | searchOptions.Image = new JsonObject("Count", 2); 34 | 35 | dynamic search = bingSearch.invoke(searchOptions); 36 | dynamic searchResponse = search.Result.SearchResponse; 37 | 38 | foreach (dynamic item in searchResponse.Web.Results) { 39 | Console.WriteLine(item.Title); 40 | Console.WriteLine(item.DisplayUrl); 41 | Console.WriteLine(); 42 | } 43 | foreach (dynamic item in searchResponse.Image.Results) { 44 | Console.WriteLine(item.Title); 45 | Console.WriteLine(item.MediaUrl); 46 | Console.WriteLine(); 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Samples/GoogleSearchSample.cs: -------------------------------------------------------------------------------- 1 | // GoogleSearchSample.cs 2 | // DynamicRest provides REST service access using C# 4.0 dynamic programming. 3 | // The latest information and code for the project can be found at 4 | // https://github.com/NikhilK/dynamicrest 5 | // 6 | // This project is licensed under the BSD license. See the License.txt file for 7 | // more information. 8 | // 9 | 10 | using System; 11 | using DynamicRest; 12 | using System.Threading; 13 | using DynamicRest.HTTPInterfaces.WebWrappers; 14 | using DynamicRest.Json; 15 | 16 | namespace Application { 17 | 18 | internal static class GoogleSearchSample { 19 | 20 | public static void Run() { 21 | var templatedUriRequestBuilder = new TemplatedUriRequestBuilder(new RequestFactory()); 22 | templatedUriRequestBuilder.Uri = Services.GoogleSearchUri; 23 | 24 | dynamic googleSearch = new RestClient(templatedUriRequestBuilder, new ResponseProcessor(new StandardResultBuilder(RestService.Json))); 25 | 26 | Console.WriteLine("Searching Google for 'seattle'..."); 27 | 28 | dynamic searchOptions = new JsonObject(); 29 | searchOptions.q = "seattle"; 30 | 31 | dynamic search = googleSearch.invokeAsync(searchOptions); 32 | search.Callback((RestCallback)delegate() 33 | { 34 | dynamic results = search.Result.responseData.results; 35 | foreach (dynamic item in results) 36 | { 37 | Console.WriteLine(item.titleNoFormatting); 38 | Console.WriteLine(item.url); 39 | Console.WriteLine(); 40 | } 41 | }); 42 | 43 | 44 | while (search.IsCompleted == false) 45 | { 46 | Console.WriteLine("."); 47 | Thread.Sleep(100); 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Samples/JsonSample.cs: -------------------------------------------------------------------------------- 1 | // JsonSample.cs 2 | // DynamicRest provides REST service access using C# 4.0 dynamic programming. 3 | // The latest information and code for the project can be found at 4 | // https://github.com/NikhilK/dynamicrest 5 | // 6 | // This project is licensed under the BSD license. See the License.txt file for 7 | // more information. 8 | // 9 | 10 | using System; 11 | using DynamicRest.Json; 12 | 13 | namespace Application { 14 | 15 | internal static class JsonSample { 16 | 17 | public static void Run() { 18 | string jsonText = "{ xyz: 123, items: [ 10, 100, 1000 ], numbers: [ 0.123, .456 ], bools: [ true, false ], text: [ \"hello\", 'world\n!', \"\\\"s\\\"\", \"'s'\" ] }"; 19 | 20 | JsonReader jsonReader = new JsonReader(jsonText); 21 | dynamic jsonObject = jsonReader.ReadValue(); 22 | dynamic items = jsonObject.items; 23 | 24 | items[2] = 1001; 25 | 26 | dynamic bar = new JsonObject(); 27 | bar.name = "c#"; 28 | 29 | jsonObject.bar = bar; 30 | 31 | JsonWriter writer = new JsonWriter(); 32 | writer.WriteValue((object)jsonObject); 33 | 34 | string newJsonText = writer.Json; 35 | 36 | Console.WriteLine(newJsonText); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Samples/Program.cs: -------------------------------------------------------------------------------- 1 | // Program.cs 2 | // DynamicRest provides REST service access using C# 4.0 dynamic programming. 3 | // The latest information and code for the project can be found at 4 | // https://github.com/NikhilK/dynamicrest 5 | // 6 | // This project is licensed under the BSD license. See the License.txt file for 7 | // more information. 8 | // 9 | 10 | using System; 11 | 12 | namespace Application { 13 | 14 | public static class Program { 15 | 16 | private static readonly string HeaderBar = new String('=', 79); 17 | private static readonly string Separator = new String('-', 79); 18 | 19 | public static void Main(string[] args) { 20 | // NOTE: To run the samples, you'll need a few API keys 21 | // that you need to specify in Services.cs. 22 | 23 | // JSON Sample 24 | Console.WriteLine("JSON Sample"); 25 | Console.WriteLine(HeaderBar); 26 | JsonSample.Run(); 27 | Console.WriteLine(Separator); 28 | Console.WriteLine(Environment.NewLine); 29 | 30 | // Amazon Sample 31 | Console.WriteLine("Amazon Sample"); 32 | Console.WriteLine(HeaderBar); 33 | AmazonSample.Run(); 34 | Console.WriteLine(Separator); 35 | Console.WriteLine(Environment.NewLine); 36 | 37 | // Bing Search 38 | Console.WriteLine("Bing Sample"); 39 | Console.WriteLine(HeaderBar); 40 | BingSearchSample.Run(); 41 | Console.WriteLine(Separator); 42 | Console.WriteLine(Environment.NewLine); 43 | 44 | // Google Search 45 | Console.WriteLine("Google Search Sample"); 46 | Console.WriteLine(HeaderBar); 47 | GoogleSearchSample.Run(); 48 | Console.WriteLine(Separator); 49 | 50 | Console.ReadLine(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Samples/Samples.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net452 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Samples/Services.cs: -------------------------------------------------------------------------------- 1 | // Services.cs 2 | // DynamicRest provides REST service access using C# 4.0 dynamic programming. 3 | // The latest information and code for the project can be found at 4 | // https://github.com/NikhilK/dynamicrest 5 | // 6 | // This project is licensed under the BSD license. See the License.txt file for 7 | // more information. 8 | // 9 | 10 | namespace Application { 11 | 12 | internal static class Services { 13 | 14 | public const string FlickrUri = "http://api.flickr.com/services/rest/?method=flickr.{operation}&api_key={apiKey}&format=json&nojsoncallback=1"; 15 | public const string FlickrApiKey = "???"; 16 | 17 | public const string AmazonUri = "http://ecs.amazonaws.com/onca/xml?Service=AWSECommerceService&Version=2009-03-31&Operation={operation}&AssociateTag=myamzn-20"; 18 | public const string AmazonAccessKey = "???"; 19 | public const string AmazonSecretKey = "???"; 20 | 21 | 22 | public const string BingSearchUri = "http://api.bing.net/json.aspx?AppId={appID}&Version=2.2&Market=en-US"; 23 | public const string BingApiKey = "???"; 24 | 25 | public const string GoogleSearchUri = "http://ajax.googleapis.com/ajax/services/search/web?v=1.0"; 26 | } 27 | } 28 | --------------------------------------------------------------------------------