创建自定义插件

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

先决条件

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

创建新插件

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

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

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

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

  4. 添加作为 dev-proxy-abstractions.dll 对项目 DevProxyCustomPlugin.csproj 文件的引用。

    <ItemGroup>
      <Reference Include="dev-proxy-abstractions">
        <HintPath>.\dev-proxy-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. 创建实现 BaseProxyPlugin 接口的新类。

    using Microsoft.DevProxy.Abstractions;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.Logging;
    
    namespace MyCustomPlugin;
    
    public class CatchApiCalls(IPluginEvents pluginEvents, IProxyContext context, ILogger logger, ISet<UrlToWatch> UrlsToWatch, IConfigurationSection? configSection = null) : BaseProxyPlugin(pluginEvents, context, logger, UrlsToWatch, configSection)
    {
      public override string Name => nameof(CatchApiCalls);
    
      public override async Task RegisterAsync()
      {
        await base.RegisterAsync();
    
        PluginEvents.BeforeRequest += BeforeRequestAsync;
      }
    
      private Task BeforeRequestAsync(object sender, ProxyRequestArgs e)
      {
        if (UrlsToWatch is null ||
          !e.HasRequestUrlMatch(UrlsToWatch))
        {
          // No match for the URL, so we don't need to do anything
          Logger.LogRequest("URL not matched", MessageType.Skipped, new LoggingContext(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;
        }
    
        return Task.CompletedTask;
      }
    }
    
  8. 生成项目。

    dotnet build
    

使用自定义插件

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

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

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

    devproxy
    

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

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

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

  1. _configuration添加新对象并在方法中Register绑定它。

    using Microsoft.DevProxy.Abstractions;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.Logging;
    
    namespace MyCustomPlugin;
    
    public class CatchApiCallsConfiguration
    {
      public string? RequiredHeader { get; set; }
    }
    
    public class CatchApiCalls(IPluginEvents pluginEvents, IProxyContext context, ILogger logger, ISet<UrlToWatch> UrlsToWatch, IConfigurationSection? configSection = null) : BaseProxyPlugin(pluginEvents, context, logger, UrlsToWatch, configSection)
    {
      public override string Name => nameof(CatchApiCalls);
    
      // Define you custom configuration
      private readonly CatchApiCallsConfiguration _configuration = new();
    
      public override async Task RegisterAsync()
      {
        await base.RegisterAsync();
    
        // Bind your plugin configuration
        configSection?.Bind(_configuration);
    
        // Register your event handlers
        PluginEvents.BeforeRequest += BeforeRequestAsync;
      }
    
      private Task BeforeRequestAsync(object sender, ProxyRequestArgs e)
      {
        if (UrlsToWatch is null ||
          !e.HasRequestUrlMatch(UrlsToWatch))
        {
          // No match for the URL, so we don't need to do anything
          Logger.LogRequest("URL not matched", MessageType.Skipped, new LoggingContext(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;
        }
    
        return Task.CompletedTask;
      }
    }
    
  2. 生成项目。

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

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

    devproxy