创建自定义插件

概览
目标: 生成自定义开发代理插件
时间: 30 分钟
插件: 自定义插件
先决条件:设置开发代理.NET 9 SDK

本文介绍如何为开发代理创建自定义插件。 通过为开发代理创建插件,可以扩展其功能并添加自定义功能以满足你的需求。

先决条件

在开始创建自定义插件之前,请确保满足以下先决条件:

创建新插件

按照后续步骤创建新项目:

  1. 使用 dotnet new classlib 命令创建新的类库项目。

    dotnet new classlib -n MyCustomPlugin
    
  2. 在 Visual Studio Code 中打开新创建的项目。

    code MyCustomPlugin
    
  3. 将开发代理抽象 DLL (DevProxy.Abstractions.dll) 添加到项目文件夹。

  4. 添加作为 DevProxy.Abstractions.dll 对项目 DevProxyCustomPlugin.csproj 文件的引用。

    <ItemGroup>
      <Reference Include="DevProxy.Abstractions">
        <HintPath>.\DevProxy.Abstractions.dll</HintPath>
        <Private>false</Private>
        <ExcludeAssets>runtime</ExcludeAssets>
      </Reference>
    </ItemGroup>
    
  5. 添加项目所需的 NuGet 包。

    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.Proxy
    
  6. 通过在文件中添加标记 ExcludeAssetsPackageReferenceDevProxyCustomPlugin.csproj ,从生成输出中排除依赖项 DLL。

    <ExcludeAssets>runtime</ExcludeAssets>
    
  7. 创建继承自 BaseProxy 该类的新类。

    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;
        }
    }
    
  8. 生成项目。

    dotnet build
    

使用自定义插件

若要使用自定义插件,需要将其添加到开发代理配置文件:

  1. 在文件中添加新插件配置 devproxyrc.json

    文件: devproxyrc.json

    {
      "plugins": [{
        "name": "CatchApiCallsPlugin",
        "enabled": true,
        "pluginPath": "./bin/Debug/net9.0/MyCustomPlugin.dll",
      }]
    }
    
  2. 运行开发代理。

    devproxy
    

示例插件检查所有匹配 URL 是否包含所需的 Authorization 标头。 如果标头不存在,则会显示警告消息。

将自定义配置添加到插件(可选)

可以通过添加自定义配置来扩展插件的逻辑:

  1. 继承自 BasePlugin<TConfiguration> 类。 开发代理在运行时通过 Configuration 属性公开解析后的插件配置。

    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(
        ILogger<CatchApiCallsPlugin> logger,
        ISet<UrlToWatch> urlsToWatch,
        IProxyConfiguration proxyConfiguration,
        IConfigurationSection pluginConfigurationSection) :
        BasePlugin<CatchApiCallsConfiguration>(
            logger,
            urlsToWatch,
            proxyConfiguration,
            pluginConfigurationSection)
    {
        public override string Name => nameof(CatchApiCallsPlugin);
    
        public override Task BeforeRequestAsync(ProxyRequestArgs e)
        {
            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;
        }
    }
    
  2. 生成项目。

    dotnet build
    
  3. devproxyrc.json更新文件以包含新配置。

    文件: devproxyrc.json

    {
      "plugins": [{
        "name": "CatchApiCallsPlugin",
        "enabled": true,
        "pluginPath": "./bin/Debug/net9.0/MyCustomPlugin.dll",
        "configSection": "catchApiCalls"
      }],
      "catchApiCalls": {
        "requiredHeader": "Authorization" // Example configuration
      }
    }
    
  4. 运行开发代理。

    devproxy
    

另请参阅