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

.NET 4.6 Windows服务MTOM传输下修改Content-Type HTTP头的方法

问题描述

我在基于.NET 4.6的Windows服务中,使用VS2022为某个WSDL服务创建了服务引用,原本运行正常。现在需要在HTTP请求头的Content-Type中添加一个参数,但因为使用了MTOM传输,尝试修改Content-Type头时遇到了问题。

我试过实现继承自IEndpointBehavior的自定义行为,以及对应的IClientMessageInspector自定义检查器,在BeforeSendRequest方法中操作,但发现此时无法读取到完整的Content-Type头——因为MTOM消息的boundary元素是在这个调用之后才生成的。

当前的Content-Type头:

multipart/related;type="application/xop+xml";charset=UTF-8;start-info="application/soap+xml";boundary="uuid:00361b85-692d-43a1-9f17-57d70bbe26df+id=1"

需要添加的内容:

action="{action}";

这种需求是否可行?


现有代码片段

绑定与客户端初始化

WSHttpBinding wsHttpBinding = new WSHttpBinding();
wsHttpBinding.Security.Mode = SecurityMode.Transport;
wsHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
wsHttpBinding.MessageEncoding = WSMessageEncoding.Mtom;

Client = new ServiceClient(wsHttpBinding, new EndpointAddress(EndPoint));

添加自定义行为

Client.Endpoint.Behaviors.Add(new CustomBehavior(action));

自定义行为与检查器实现

public class CustomBehavior : IEndpointBehavior
{
    private string action = null;
    
    public CustomBehavior(string action, byte[] file)
    {
        this.action = action;
    }

    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { }

    public void Validate(ServiceEndpoint endpoint) { }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        clientRuntime.MessageInspectors.Add(new CustomInspector(this.action));
    }
}

private class CustomInspector : IClientMessageInspector
{
    private string action;

    public CustomInspector(string action)
    {
        this.action = action;
    }

    public void AfterReceiveReply(ref Message reply, object correlationState)
    {
    }

    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    {
       HttpRequestMessageProperty reqProps = request.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;

        if (reqProps != null)
        {
            reqProps = new HttpRequestMessageProperty();
        }

        reqProps.Headers.Add("Content-Type", $"multipart/related;type=\"application/xop+xml\";charset=UTF-8;start-info=\"application/soap+xml\";action=\"{action}\";boundary=\"uuid:00361b85-692d-43a1-9f17-57d70bbe26df+id=1\";\");
        request.Properties[HttpRequestMessageProperty.Name] = reqProps;

        return null;
    }
}

服务调用代码

using (OperationContextScope scope = new OperationContextScope((Client as DocumentRepository_PortTypeClient).InnerChannel))
{
    // 尝试在此读取头但为空
    var obj = OperationContext.Current.OutgoingMessageProperties;

    response = Client.call(request);
}

解决方案

可行,但不能直接在IClientMessageInspector的BeforeSendRequest中修改Content-Type——因为MTOM的boundary确实是在这个阶段之后由绑定组件生成的,手动指定boundary会导致和实际生成的不一致,引发请求错误。

正确的做法是通过自定义绑定元素拦截MTOM消息的Content-Type生成过程,具体实现步骤如下:

  1. 创建自定义MTOM绑定元素
public class CustomMtomBindingElement : MtomMessageEncodingBindingElement
{
    private string _action;

    public CustomMtomBindingElement(string action)
    {
        _action = action;
    }

    public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
    {
        context.BindingParameters.Add(new ActionHeaderSetting(_action));
        return base.BuildChannelFactory<TChannel>(context);
    }

    public override BindingElement Clone()
    {
        return new CustomMtomBindingElement(_action);
    }

    public override T GetProperty<T>(BindingContext context)
    {
        return context.GetInnerProperty<T>();
    }
}

public class ActionHeaderSetting
{
    public string Action { get; }

    public ActionHeaderSetting(string action)
    {
        Action = action;
    }
}
  1. 创建自定义MTOM消息编码器
public class CustomMtomMessageEncoder : MtomMessageEncoder
{
    private string _action;

    public CustomMtomMessageEncoder(MtomMessageEncoder innerEncoder, string action) : base(innerEncoder.ContentType, innerEncoder.MediaType, innerEncoder.MessageVersion)
    {
        _action = action;
    }

    public override void WriteMessage(Message message, Stream stream)
    {
        // 先执行原逻辑生成包含正确boundary的Content-Type
        base.WriteMessage(message, stream);
        
        // 给已生成的Content-Type添加action参数
        if (!this.ContentType.Contains("action="))
        {
            this.ContentType = $"{this.ContentType};action=\"{_action}\"";
        }
    }
}
  1. 创建自定义编码器工厂
public class CustomMtomEncoderFactory : MessageEncoderFactory
{
    private MessageEncoderFactory _innerFactory;
    private string _action;

    public CustomMtomEncoderFactory(MessageEncoderFactory innerFactory, string action)
    {
        _innerFactory = innerFactory;
        _action = action;
    }

    public override MessageEncoder Encoder => new CustomMtomMessageEncoder((MtomMessageEncoder)_innerFactory.Encoder, _action);

    public override MessageVersion MessageVersion => _innerFactory.MessageVersion;
}
  1. 修改客户端绑定初始化逻辑
// 创建自定义MTOM绑定元素并复制原配置
var customMtomBindingElement = new CustomMtomBindingElement(action);
customMtomBindingElement.MaxReadPoolSize = wsHttpBinding.MaxReceivedMessageSize;
customMtomBindingElement.MaxWritePoolSize = wsHttpBinding.MaxBufferPoolSize;
customMtomBindingElement.MessageVersion = wsHttpBinding.MessageVersion;
customMtomBindingElement.WriteEncoding = Encoding.UTF8;

// 构建自定义绑定
var customBinding = new CustomBinding();
customBinding.Elements.Add(customMtomBindingElement);
// 添加原绑定的HTTPS传输元素
customBinding.Elements.Add(wsHttpBinding.CreateBindingElements().First(b => b is HttpsTransportBindingElement));

// 使用自定义绑定创建客户端
Client = new ServiceClient(customBinding, new EndpointAddress(EndPoint));

这种方式会在MTOM自动生成正确的boundary之后,动态将action参数添加到Content-Type头中,既保证了boundary的正确性,又满足了需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 19:22:09