{"id":13431124,"url":"https://github.com/justeattakeaway/httpclient-interception","last_synced_at":"2025-05-14T13:05:59.863Z","repository":{"id":25733471,"uuid":"95348422","full_name":"justeattakeaway/httpclient-interception","owner":"justeattakeaway","description":"A .NET library for intercepting server-side HTTP requests","archived":false,"fork":false,"pushed_at":"2025-05-13T19:01:31.000Z","size":1937,"stargazers_count":369,"open_issues_count":6,"forks_count":26,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-05-13T20:27:54.050Z","etag":null,"topics":["c-sharp","dotnet","dotnet-core","httpclient","netstandard"],"latest_commit_sha":null,"homepage":"https://web.archive.org/web/20240622022707/https://tech.justeattakeaway.com/2017/10/02/reliably-testing-http-integrations-in-a-dotnet-application/","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/justeattakeaway.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2017-06-25T08:47:16.000Z","updated_at":"2025-05-07T14:24:11.000Z","dependencies_parsed_at":"2023-01-16T22:02:24.300Z","dependency_job_id":"d4c666a9-874c-4106-83cc-4df33f38621b","html_url":"https://github.com/justeattakeaway/httpclient-interception","commit_stats":{"total_commits":1256,"total_committers":16,"mean_commits":78.5,"dds":0.5429936305732483,"last_synced_commit":"b54e78802faac6dc6d96c0b0284e461753941b0b"},"previous_names":["justeat/httpclient-interception"],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justeattakeaway%2Fhttpclient-interception","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justeattakeaway%2Fhttpclient-interception/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justeattakeaway%2Fhttpclient-interception/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justeattakeaway%2Fhttpclient-interception/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/justeattakeaway","download_url":"https://codeload.github.com/justeattakeaway/httpclient-interception/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254030727,"owners_count":22002642,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c-sharp","dotnet","dotnet-core","httpclient","netstandard"],"created_at":"2024-07-31T02:01:00.694Z","updated_at":"2025-05-14T13:05:59.839Z","avatar_url":"https://github.com/justeattakeaway.png","language":"C#","readme":"# HttpClient Interception\n\nA .NET Standard library for intercepting server-side HTTP dependencies.\n\n[![NuGet version](https://img.shields.io/nuget/v/JustEat.HttpClientInterception?logo=nuget\u0026label=NuGet\u0026color=blue)](https://www.nuget.org/packages/JustEat.HttpClientInterception/)\n[![Build status](https://github.com/justeattakeaway/httpclient-interception/actions/workflows/build.yml/badge.svg?branch=main\u0026event=push)](https://github.com/justeattakeaway/httpclient-interception/actions/workflows/build.yml?query=branch%3Amain+event%3Apush)\n\n[![codecov](https://codecov.io/gh/justeattakeaway/httpclient-interception/branch/main/graph/badge.svg)](https://codecov.io/gh/justeattakeaway/httpclient-interception)\n[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/justeattakeaway/httpclient-interception/badge)](https://securityscorecards.dev/viewer/?uri=github.com/justeattakeaway/httpclient-interception)\n\n## Introduction\n\nThis library provides functionality for intercepting HTTP requests made using the `HttpClient` class in code targeting .NET Standard 2.0 (and later), and .NET Framework 4.7.2.\n\nThe primary use-case is for providing stub responses for use in tests for applications, such as an ASP.NET Core application, to drive your functional test scenarios.\n\nThe library is based around an implementation of [`DelegatingHandler`](https://learn.microsoft.com/dotnet/api/system.net.http.delegatinghandler \"DelegatingHandler documentation\"), which can either be used directly as an implementation of `HttpMessageHandler`, or can be provided to instances of `HttpClient`. This also allows it to be registered via Dependency Injection to make it available for use in code under test without the application itself requiring any references to `JustEat.HttpClientInterception` or any custom abstractions of `HttpClient`.\n\nThis design means that no HTTP server needs to be hosted to proxy traffic to/from, so does not consume any additional system resources, such as needing to bind a port for HTTP traffic, making it lightweight to use.\n\n### Installation\n\nTo install the library from [NuGet](https://www.nuget.org/packages/JustEat.HttpClientInterception/ \"JustEat.HttpClientInterception on NuGet.org\") using the .NET SDK run:\n\n```text\ndotnet add package JustEat.HttpClientInterception\n```\n\n### Basic Examples\n\n#### Request Interception\n\n##### Fluent API\n\nBelow is a minimal example of intercepting an HTTP GET request to an API for a JSON resource to return a custom response using the fluent API:\n\n\u003c!-- markdownlint-disable MD031 --\u003e\n\n\u003c!-- snippet: minimal-example --\u003e\n\u003ca id='snippet-minimal-example'\u003e\u003c/a\u003e\n```cs\n// Arrange\nvar options = new HttpClientInterceptorOptions();\nvar builder = new HttpRequestInterceptionBuilder();\n\nbuilder\n    .Requests()\n    .ForGet()\n    .ForHttps()\n    .ForHost(\"public.je-apis.com\")\n    .ForPath(\"terms\")\n    .Responds()\n    .WithJsonContent(new { Id = 1, Link = \"https://www.just-eat.co.uk/privacy-policy\" })\n    .RegisterWith(options);\n\nusing var client = options.CreateHttpClient();\n\n// Act\n// The value of json will be: {\"Id\":1, \"Link\":\"https://www.just-eat.co.uk/privacy-policy\"}\nstring json = await client.GetStringAsync(\"https://public.je-apis.com/terms\", TestContext.Current.CancellationToken);\n```\n\u003csup\u003e\u003ca href='/tests/HttpClientInterception.Tests/Examples.cs#L46-L68' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-minimal-example' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n`HttpRequestInterceptionBuilder` objects are mutable, so properties can be freely changed once a particular setup has been registered with an instance of `HttpClientInterceptorOptions` as the state is captured at the point of registration. This allows multiple responses and paths to be configured from a single `HttpRequestInterceptionBuilder` instance where multiple registrations against a common hostname.\n\n##### _HTTP Bundle_ Files\n\nHTTP requests to intercept can also be configured in an _\"HTTP bundle\"_ file, which can be used to store the HTTP requests to intercept and their corresponding responses as JSON.\n\nThis functionality is analogous to our [_Shock_](https://github.com/justeat/Shock \"Shock\") pod for iOS.\n\n###### JSON\n\nBelow is an example bundle file, which can return content in formats such as a string, JSON and base64-encoded data.\n\nThe full JSON schema for HTTP bundle files can be found [here](https://raw.githubusercontent.com/justeattakeaway/httpclient-interception/main/src/HttpClientInterception/Bundles/http-request-bundle-schema.json \"JSON Schema for HTTP request interception bundles for use with JustEat.HttpClientInterception.\").\n\n\u003c!-- snippet: sample-bundle.json --\u003e\n\u003ca id='snippet-sample-bundle.json'\u003e\u003c/a\u003e\n```json\n{\n  \"$schema\": \"https://raw.githubusercontent.com/justeattakeaway/httpclient-interception/main/src/HttpClientInterception/Bundles/http-request-bundle-schema.json\",\n  \"id\": \"my-bundle\",\n  \"comment\": \"A bundle of HTTP requests\",\n  \"items\": [\n    {\n      \"id\": \"home\",\n      \"comment\": \"Returns the home page\",\n      \"uri\": \"https://www.just-eat.co.uk\",\n      \"contentString\": \"\u003chtml\u003e\u003chead\u003e\u003ctitle\u003eJust Eat\u003c/title\u003e\u003c/head\u003e\u003c/html\u003e\"\n    },\n    {\n      \"id\": \"terms\",\n      \"comment\": \"Returns the Ts \u0026 Cs\",\n      \"uri\": \"https://public.je-apis.com/terms\",\n      \"contentFormat\": \"json\",\n      \"contentJson\": {\n        \"Id\": 1,\n        \"Link\": \"https://www.just-eat.co.uk/privacy-policy\"\n      }\n    }\n  ]\n}\n```\n\u003csup\u003e\u003ca href='/tests/HttpClientInterception.Tests/Bundles/sample-bundle.json#L1-L23' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-sample-bundle.json' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n###### Code\n\n```cs\n// using JustEat.HttpClientInterception;\n\nvar options = new HttpClientInterceptorOptions().RegisterBundle(\"my-bundle.json\");\n\nvar client = options.CreateHttpClient();\n\n// The value of html will be \"\u003chtml\u003e\u003chead\u003e\u003ctitle\u003eJust Eat\u003c/title\u003e\u003c/head\u003e\u003c/html\u003e\"\nvar html = await client.GetStringAsync(\"https://www.just-eat.co.uk\");\n\n// The value of json will be \"{\\\"Id\\\":1,\\\"Link\\\":\\\"https://www.just-eat.co.uk/privacy-policy\\\"}\"\nvar json = await client.GetStringAsync(\"https://public.je-apis.com/terms\");\n```\n\nFurther examples of using HTTP bundles can be found in the [tests](https://github.com/justeattakeaway/httpclient-interception/blob/main/tests/HttpClientInterception.Tests/Bundles/BundleExtensionsTests.cs \"BundleExtensionsTests.cs\"), such as for changing the response code, the HTTP method, and matching to HTTP requests based on the request headers.\n\n#### Fault Injection\n\nBelow is a minimal example of intercepting a request to inject an HTTP fault:\n\n\u003c!-- snippet: fault-injection --\u003e\n\u003ca id='snippet-fault-injection'\u003e\u003c/a\u003e\n```cs\nvar options = new HttpClientInterceptorOptions();\n\nvar builder = new HttpRequestInterceptionBuilder()\n    .Requests()\n    .ForHost(\"public.je-apis.com\")\n    .WithStatus(HttpStatusCode.InternalServerError)\n    .RegisterWith(options);\n\nvar client = options.CreateHttpClient();\n\n// Throws an HttpRequestException\nawait Assert.ThrowsAsync\u003cHttpRequestException\u003e(\n    () =\u003e client.GetStringAsync(\"http://public.je-apis.com\", TestContext.Current.CancellationToken));\n```\n\u003csup\u003e\u003ca href='/tests/HttpClientInterception.Tests/Examples.cs#L25-L40' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-fault-injection' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n#### Registering Request Interception When Using IHttpClientFactory\n\nIf you are using [`IHttpClientFactory`](https://learn.microsoft.com/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests \"Use IHttpClientFactory to implement resilient HTTP requests\") to register `HttpClient` for Dependency Injection in a .NET application (or later), you can implement a custom [`IHttpMessageHandlerBuilderFilter`](https://learn.microsoft.com/dotnet/api/microsoft.extensions.http.ihttpmessagehandlerbuilderfilter \"IHttpMessageHandlerBuilderFilter Interface\") to register during test setup, which makes an instance of `HttpClientInterceptorOptions` available to inject an HTTP handler.\n\nA working example of this approach can be found in the [sample application](https://github.com/justeattakeaway/httpclient-interception/blob/main/samples/README.md \"Sample application that uses JustEat.HttpClientInterception\").\n\n\u003c!-- snippet: interception-filter --\u003e\n\u003ca id='snippet-interception-filter'\u003e\u003c/a\u003e\n```cs\n/// \u003csummary\u003e\n/// A class that registers an intercepting HTTP message handler at the end of\n/// the message handler pipeline when an \u003csee cref=\"HttpClient\"/\u003e is created.\n/// \u003c/summary\u003e\npublic sealed class HttpClientInterceptionFilter(HttpClientInterceptorOptions options) : IHttpMessageHandlerBuilderFilter\n{\n    /// \u003cinheritdoc/\u003e\n    public Action\u003cHttpMessageHandlerBuilder\u003e Configure(Action\u003cHttpMessageHandlerBuilder\u003e next)\n    {\n        return (builder) =\u003e\n        {\n            // Run any actions the application has configured for itself\n            next(builder);\n\n            // Add the interceptor as the last message handler\n            builder.AdditionalHandlers.Add(options.CreateHttpMessageHandler());\n        };\n    }\n}\n```\n\u003csup\u003e\u003ca href='/samples/SampleApp.Tests/HttpClientInterceptionFilter.cs#L9-L31' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-interception-filter' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n#### Setting Up HttpClient for Dependency Injection Manually\n\nBelow is an example of setting up `IServiceCollection` to register `HttpClient` for Dependency Injection in a manner that allows tests to use `HttpClientInterceptorOptions` to intercept HTTP requests. Similar approaches can be used with other IoC containers.\n\nYou may wish to consider registering `HttpClient` as a singleton, rather than as transient, if you do not use properties such as `BaseAddress` on instances of `HttpClient`. This allows the same instance to be used throughout the application, which improves performance and resource utilisation under heavy server load. If using a singleton instance, ensure that you manage the lifetime of your message handlers appropriately so they are not disposed of incorrectly and update the registration for your `HttpClient` instance appropriately.\n\n```csharp\nservices.AddTransient(\n    (serviceProvider) =\u003e\n    {\n        // Create a handler that makes actual HTTP calls\n        HttpMessageHandler handler = new HttpClientHandler();\n\n        // Have any delegating handlers been registered?\n        var handlers = serviceProvider\n            .GetServices\u003cDelegatingHandler\u003e()\n            .ToList();\n\n        if (handlers.Count \u003e 0)\n        {\n            // Attach the initial handler to the first delegating handler\n            DelegatingHandler previous = handlers.First();\n            previous.InnerHandler = handler;\n\n            // Chain any remaining handlers to each other\n            foreach (DelegatingHandler next in handlers.Skip(1))\n            {\n                next.InnerHandler = previous;\n                previous = next;\n            }\n\n            // Replace the initial handler with the last delegating handler\n            handler = previous;\n        }\n\n        // Create the HttpClient using the inner HttpMessageHandler\n        return new HttpClient(handler);\n    });\n```\n\nThen in the test project register `HttpClientInterceptorOptions` to provide an implementation of `DelegatingHandler`. If using a singleton for `HttpClient` as described above, update the registration for the tests appropriately so that the message handler is shared.\n\n```csharp\nvar options = new HttpClientInterceptorOptions();\n\nvar server = new WebHostBuilder()\n    .UseStartup\u003cStartup\u003e()\n    .ConfigureServices(\n        (services) =\u003e services.AddTransient((_) =\u003e options.CreateHttpMessageHandler()))\n    .Build();\n\nserver.Start();\n```\n\n### Further Examples\n\nFurther examples of using the library can be found by following the links below:\n\n  1. [Example tests](https://github.com/justeattakeaway/httpclient-interception/blob/main/tests/HttpClientInterception.Tests/Examples.cs \"Sample tests using JustEat.HttpClientInterception\")\n  1. [Sample application with tests](https://github.com/justeattakeaway/httpclient-interception/blob/main/samples/README.md \"Sample application that uses JustEat.HttpClientInterception\")\n  1. This library's [own tests](https://github.com/justeattakeaway/httpclient-interception/blob/main/tests/HttpClientInterception.Tests/HttpClientInterceptorOptionsTests.cs \"Tests for JustEat.HttpClientInterception itself\")\n\n### Benchmarks\n\nGenerated with the [Benchmarks project](https://github.com/justeattakeaway/httpclient-interception/blob/main/tests/HttpClientInterception.Benchmarks/InterceptionBenchmarks.cs \"JustEat.HttpClientInterception benchmark code\") using [BenchmarkDotNet](https://github.com/dotnet/BenchmarkDotNet \"BenchmarkDotNet on GitHub.com\") using commit [ef93cb5](https://github.com/justeattakeaway/httpclient-interception/commit/ef93cb5a54b518fe488de63be962ddf15644ef42 \"Benchmark commit\") on 04/02/2024.\n\n\u003c!-- markdownlint-disable MD058 --\u003e\n\n```text\n\nBenchmarkDotNet v0.13.12, Windows 11 (10.0.22621.3007/22H2/2022Update/SunValley2)\n12th Gen Intel Core i7-1270P, 1 CPU, 16 logical and 12 physical cores\n.NET SDK 8.0.101\n  [Host]     : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2\n  Job-IMNGZO : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2\n\nArguments=/p:UseArtifactsOutput=false\n\n```\n| Method                       | Mean       | Error     | StdDev     | Median     | Gen0   | Allocated |\n|----------------------------- |-----------:|----------:|-----------:|-----------:|-------:|----------:|\n| GetBytes                     |   1.708 μs | 0.0338 μs |  0.0898 μs |   1.698 μs | 0.3223 |   2.98 KB |\n| GetHtml                      |   1.618 μs | 0.0324 μs |  0.0703 μs |   1.614 μs | 0.3319 |   3.06 KB |\n| GetJsonDocument              |   1.942 μs | 0.0381 μs |  0.0604 μs |   1.921 μs | 0.3071 |   2.84 KB |\n| GetJsonObject                |   2.460 μs | 0.0778 μs |  0.2195 μs |   2.413 μs | 0.3281 |   3.05 KB |\n| GetJsonObjectSourceGenerator |   2.345 μs | 0.0421 μs |  0.1102 μs |   2.311 μs | 0.3281 |   3.05 KB |\n| GetJsonObjectWithRefit       |   5.169 μs | 0.1025 μs |  0.2117 μs |   5.120 μs | 0.6714 |   6.35 KB |\n| GetStream                    | 209.607 μs | 4.5593 μs | 13.3717 μs | 210.077 μs | 0.2441 |   3.16 KB |\n\n\u003c!-- markdownlint-enable MD058 --\u003e\n\n## Feedback\n\nAny feedback or issues can be added to the issues for this project in [GitHub](https://github.com/justeattakeaway/httpclient-interception/issues \"This project's issues on GitHub.com\").\n\n## Repository\n\nThe repository is hosted in [GitHub](https://github.com/justeattakeaway/httpclient-interception \"This project on GitHub.com\"): \u003chttps://github.com/justeattakeaway/httpclient-interception.git\u003e\n\n## Building and Testing\n\nCompiling the library yourself requires Git and the [.NET SDK](https://www.microsoft.com/net/download/core \"Download the .NET SDK\") to be installed (version 7.0.100 or later).\n\nTo build and test the library locally from a terminal/command-line, run one of the following set of commands:\n\n```powershell\ngit clone https://github.com/justeattakeaway/httpclient-interception.git\ncd httpclient-interception\n./build.ps1\n```\n\n## License\n\nThis project is licensed under the [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0.html \"The Apache 2.0 license\") license.\n","funding_links":[],"categories":["Frameworks, Libraries and Tools","框架, 库和工具"],"sub_categories":["Misc","大杂烩"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjusteattakeaway%2Fhttpclient-interception","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjusteattakeaway%2Fhttpclient-interception","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjusteattakeaway%2Fhttpclient-interception/lists"}