Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
At a glance
Goal: Build a custom Dev Proxy plugin
Time: 30 minutes
Plugins: Custom plugin
Prerequisites: Set up Dev Proxy, .NET 10 SDK
In this article, you learn how to create a custom plugin for the Dev Proxy. By creating plugins for Dev Proxy, you can extend its functionality and add custom features to fit your needs.
HTTP plugins vs. stdio plugins
Dev Proxy supports two types of plugins depending on the traffic you want to intercept:
HTTP plugins intercept HTTP(S) requests and responses between your app and APIs. They inherit from
BasePluginand override methods likeBeforeRequestAsyncandBeforeResponseAsync. Use HTTP plugins when you want to simulate API errors, add mock responses, validate request headers, or otherwise inspect and modify HTTP traffic.Stdio plugins intercept messages sent over standard input/output (stdin, stdout, stderr) between a parent process and a child process. They implement the
IStdioPlugininterface (whichBasePluginalso implements) and override methods likeBeforeStdinAsync,AfterStdoutAsync, andAfterStderrAsync. Use stdio plugins when working with tools that communicate over stdio, such as Model Context Protocol (MCP) servers.
A single plugin class can handle both HTTP and stdio traffic by overriding methods from both sets.
Prerequisites
Before you start creating a custom plugin, make sure you have the following prerequisites:
- .NET v10 Core SDK
- The latest version of the Dev Proxy Abstractions DLL, which you can find on the Dev Proxy GitHub releases page
Create a new plugin
Follow the next steps to create a new project:
Create a new class library project using the
dotnet new classlibcommand.dotnet new classlib -n MyCustomPluginOpen the newly created project in Visual Studio Code.
code MyCustomPluginAdd the Dev Proxy Abstractions DLL (
DevProxy.Abstractions.dll) to the project folder.Add the
DevProxy.Abstractions.dllas a reference to your projectDevProxyCustomPlugin.csprojfile.<ItemGroup> <Reference Include="DevProxy.Abstractions"> <HintPath>.\DevProxy.Abstractions.dll</HintPath> <Private>false</Private> <ExcludeAssets>runtime</ExcludeAssets> </Reference> </ItemGroup>Add the NuGet packages required for your project.
dotnet add package Microsoft.Extensions.Configuration dotnet add package Microsoft.Extensions.Configuration.Binder dotnet add package Microsoft.Extensions.Logging.Abstractions dotnet add package Unobtanium.Web.ProxyExclude the dependency dynamic-link libraries (DLLs) from the build output by adding a
ExcludeAssetstag perPackageReferencein theDevProxyCustomPlugin.csprojfile.<ExcludeAssets>runtime</ExcludeAssets>Create a new class that inherits from the
BaseProxyclass.using DevProxy.Abstractions.Plugins; using DevProxy.Abstractions.Proxy; using Microsoft.Extensions.Logging; namespace MyCustomPlugin; public sealed class CatchApiCallsPlugin( ILogger<CatchApiCallsPlugin> logger, ISet<UrlToWatch> urlsToWatch) : BasePlugin(logger, urlsToWatch) { public override string Name => nameof(CatchApiCallsPlugin); public override Task BeforeRequestAsync(ProxyRequestArgs e, CancellationToken cancellationToken) { Logger.LogTrace("{Method} called", nameof(BeforeRequestAsync)); ArgumentNullException.ThrowIfNull(e); if (!e.HasRequestUrlMatch(UrlsToWatch)) { Logger.LogRequest("URL not matched", MessageType.Skipped, new(e.Session)); return Task.CompletedTask; } var headers = e.Session.HttpClient.Request.Headers; var header = headers.Where(h => h.Name == "Authorization").FirstOrDefault(); if (header is null) { Logger.LogRequest($"Does not contain the Authorization header", MessageType.Warning, new LoggingContext(e.Session)); return Task.CompletedTask; } Logger.LogTrace("Left {Name}", nameof(BeforeRequestAsync)); return Task.CompletedTask; } }Build your project.
dotnet build
Use your custom plugin
To use your custom plugin, you need to add it to the Dev Proxy configuration file:
Add the new plugin configuration in the
devproxyrc.jsonfile.File: devproxyrc.json
{ "plugins": [{ "name": "CatchApiCallsPlugin", "enabled": true, "pluginPath": "./bin/Debug/net10.0/MyCustomPlugin.dll", }] }Run the Dev Proxy.
devproxy
The example plugin checks all matching URLs for the required Authorization header. If the header isn't present, it shows a warning message.
Adding custom configuration to your plugin (optional)
You can extend your plugin's logic by adding custom configuration:
Inherit from the
BasePlugin<TConfiguration>class. Dev Proxy exposes at runtime parsed plugin configuration through theConfigurationproperty.using DevProxy.Abstractions.Plugins; using DevProxy.Abstractions.Proxy; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; namespace MyCustomPlugin; public sealed class CatchApiCallsConfiguration { public string? RequiredHeader { get; set; } } public sealed class CatchApiCallsPlugin( HttpClient httpClient, ILogger<CatchApiCallsPlugin> logger, ISet<UrlToWatch> urlsToWatch, IProxyConfiguration proxyConfiguration, IConfigurationSection pluginConfigurationSection) : BasePlugin<CatchApiCallsConfiguration>( httpClient, logger, urlsToWatch, proxyConfiguration, pluginConfigurationSection) { public override string Name => nameof(CatchApiCallsPlugin); public override Task BeforeRequestAsync(ProxyRequestArgs e, CancellationToken cancellationToken) { Logger.LogTrace("{Method} called", nameof(BeforeRequestAsync)); ArgumentNullException.ThrowIfNull(e); if (!e.HasRequestUrlMatch(UrlsToWatch)) { Logger.LogRequest("URL not matched", MessageType.Skipped, new(e.Session)); return Task.CompletedTask; } // Start using your custom configuration var requiredHeader = Configuration.RequiredHeader ?? string.Empty; if (string.IsNullOrEmpty(requiredHeader)) { // Required header is not set, so we don't need to do anything Logger.LogRequest("Required header not set", MessageType.Skipped, new LoggingContext(e.Session)); return Task.CompletedTask; } var headers = e.Session.HttpClient.Request.Headers; var header = headers.Where(h => h.Name == requiredHeader).FirstOrDefault(); if (header is null) { Logger.LogRequest($"Does not contain the {requiredHeader} header", MessageType.Warning, new LoggingContext(e.Session)); return Task.CompletedTask; } Logger.LogTrace("Left {Name}", nameof(BeforeRequestAsync)); return Task.CompletedTask; } }Build your project.
dotnet buildUpdate your
devproxyrc.jsonfile to include the new configuration.File: devproxyrc.json
{ "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v2.1.0/rc.schema.json", "plugins": [{ "name": "CatchApiCallsPlugin", "enabled": true, "pluginPath": "./bin/Debug/net10.0/MyCustomPlugin.dll", "configSection": "catchApiCalls" }], "catchApiCalls": { "requiredHeader": "Authorization" } }Run the Dev Proxy.
devproxy