You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Azure Functions添加中间件/格式化器:实现ASP.NET MVC InputFormatters功能遇阻

在Azure Functions中实现类似ASP.NET MVC InputFormatters的输入格式化逻辑

我完全懂你想要的效果——不管Azure Function的触发源是ServiceBus、Http还是其他类型,都能像ASP.NET MVC的InputFormatters那样,统一接管输入的解析和格式化逻辑。之前尝试添加Message => OpenType、String => OpenType这类开放转换器没起效,还碰到过时文档的问题,确实挺闹心的,我之前也踩过类似的坑,给你分享下现在有效的解决方案:

核心思路:用自定义输入转换器(IConverter)替代旧版开放转换器

新版Azure Functions(尤其是隔离进程模型)已经弃用了旧的开放转换器机制,现在统一用IConverter<TInput, TOutput>接口来实现自定义输入转换,这个方式对所有触发源都适用。

1. 实现自定义转换器

针对你需要的转换类型,分别实现IConverter接口:

示例1:String → OpenType 转换器

public class StringToOpenTypeConverter : IConverter<string, OpenType>
{
    public OpenType Convert(string input, ConverterContext context)
    {
        // 这里写你的自定义格式化逻辑:比如自定义反序列化、特殊字符串解析等
        var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
        return JsonSerializer.Deserialize<OpenType>(input, options) ?? throw new InvalidOperationException("无法解析输入为OpenType");
    }
}

示例2:ServiceBus Message → OpenType 转换器

public class ServiceBusMessageToOpenTypeConverter : IConverter<Message, OpenType>
{
    public OpenType Convert(Message input, ConverterContext context)
    {
        // 读取ServiceBus消息内容,自定义解析
        var messageBody = Encoding.UTF8.GetString(input.Body);
        return new StringToOpenTypeConverter().Convert(messageBody, context);
    }
}

2. 注册自定义转换器

根据你的函数模型(隔离进程/进程内),将转换器注册到依赖注入容器:

隔离进程模型(Program.cs)

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults(builder =>
    {
        // 注册所有自定义转换器
        builder.Services.AddSingleton<IConverter<string, OpenType>, StringToOpenTypeConverter>();
        builder.Services.AddSingleton<IConverter<Message, OpenType>, ServiceBusMessageToOpenTypeConverter>();
    })
    .Build();

host.Run();

进程内模型(Startup.cs)

public class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        builder.Services.AddSingleton<IConverter<string, OpenType>, StringToOpenTypeConverter>();
        builder.Services.AddSingleton<IConverter<Message, OpenType>, ServiceBusMessageToOpenTypeConverter>();
    }
}

3. 在函数中直接使用

注册完成后,你只需要在函数绑定中直接声明目标类型OpenType,转换器会自动生效:

Http触发示例

[Function("HttpOpenTypeTrigger")]
public async Task<HttpResponseData> Run(
    [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req,
    // 直接绑定到OpenType,转换器自动处理输入解析
    [FromBody] OpenType inputModel,
    FunctionContext context)
{
    // 直接使用解析后的inputModel即可
    var response = req.CreateResponse(HttpStatusCode.OK);
    await response.WriteAsJsonAsync(new { Message = "解析成功", Data = inputModel });
    return response;
}

ServiceBus触发示例

[Function("ServiceBusOpenTypeTrigger")]
public void Run(
    [ServiceBusTrigger("myqueue", Connection = "ServiceBusConnection")] OpenType inputModel,
    FunctionContext context)
{
    // 直接使用解析后的inputModel处理业务逻辑
    Console.WriteLine($"Received model: {JsonSerializer.Serialize(inputModel)}");
}

Http触发的额外优化:用中间件做全局处理

如果是Http触发的函数,你还可以通过中间件实现更全局的输入拦截和格式化,和MVC的中间件逻辑类似:

实现自定义Http中间件

public class CustomInputMiddleware : IFunctionsWorkerMiddleware
{
    public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
    {
        // 判断当前函数是否是Http触发
        if (context.FunctionDefinition.InputBindings.Values.Any(b => b.Type.Equals("httpTrigger", StringComparison.OrdinalIgnoreCase)))
        {
            var req = await context.GetHttpRequestDataAsync();
            if (req != null)
            {
                // 读取原始请求内容,自定义格式化
                req.Body.Position = 0; // 重置流位置,避免后续读取失败
                var rawContent = await new StreamReader(req.Body).ReadToEndAsync();
                var formattedModel = YourCustomGlobalParsingLogic(rawContent);
                
                // 将格式化后的模型存入上下文,供函数读取
                context.Items["GlobalFormattedModel"] = formattedModel;
                
                // 重置流位置(如果后续还需要读取原始内容)
                req.Body.Position = 0;
            }
        }
        
        await next(context);
    }
    
    private OpenType YourCustomGlobalParsingLogic(string rawContent)
    {
        // 这里写全局统一的解析逻辑
        return JsonSerializer.Deserialize<OpenType>(rawContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true })!;
    }
}

注册中间件

在Program.cs中添加中间件注册:

.ConfigureFunctionsWorkerDefaults(builder =>
{
    builder.UseMiddleware<CustomInputMiddleware>();
    // 其他注册逻辑...
})

在函数中使用中间件处理后的模型

[Function("HttpMiddlewareTrigger")]
public async Task<HttpResponseData> Run(
    [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req,
    FunctionContext context)
{
    var formattedModel = context.Items["GlobalFormattedModel"] as OpenType;
    // 处理逻辑...
}

避坑指南

  • 过时文档坑:旧版文档提到的OpenConverter等API已经被废弃,现在必须用IConverter接口,别再浪费时间在旧方法上了。
  • 触发源差异:不同触发源的输入原始类型不同(比如ServiceBus是Message,Http隔离进程是HttpRequestData),要针对对应的原始类型实现转换器。
  • 流位置重置:在Http中间件中读取请求流后,一定要重置流位置,否则后续绑定或函数代码读取流会失败。

内容的提问来源于stack exchange,提问作者Ayman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:49:14