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

如何移除
和以生成合法XML?WCF服务问题咨询

解决WCF服务返回非法XML字符的方案

你遇到的问题是WCF响应中包含XML规范不允许的控制字符(比如这类),逐个字段处理确实低效且容易遗漏,以下是几种现成的全局处理方案:

1. 使用WCF消息检查器全局清理响应

消息检查器可以在WCF发送响应前统一拦截并修改内容,无需修改业务代码,侵入性极低。

实现步骤:

第一步:编写消息检查器类

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Xml;

public class XmlCleanupMessageInspector : IDispatchMessageInspector
{
    // 处理请求(此处不需要,返回null)
    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        return null;
    }

    // 发送响应前统一清理XML内容
    public void BeforeSendReply(ref Message reply, object correlationState)
    {
        if (reply == null) return;

        // 将响应消息读取为XML文档
        XmlDocument doc = new XmlDocument();
        using (MemoryStream ms = new MemoryStream())
        {
            XmlWriter writer = XmlWriter.Create(ms);
            reply.WriteMessage(writer);
            writer.Flush();
            ms.Position = 0;
            doc.Load(ms);
        }

        // 递归清理所有文本节点的非法字符
        CleanXmlNodes(doc.DocumentElement);

        // 重新生成清理后的响应消息
        using (MemoryStream newMs = new MemoryStream())
        {
            doc.Save(newMs);
            newMs.Position = 0;
            XmlReader reader = XmlReader.Create(newMs);
            reply = Message.CreateMessage(reader, int.MaxValue, reply.Version);
        }
    }

    // 递归遍历并处理XML节点
    private void CleanXmlNodes(XmlNode node)
    {
        if (node.NodeType is XmlNodeType.Text or XmlNodeType.CDATA)
        {
            node.Value = RemoveInvalidXmlCharacters(node.Value);
        }

        foreach (XmlNode childNode in node.ChildNodes)
        {
            CleanXmlNodes(childNode);
        }
    }

    // 严格按照XML规范过滤非法字符
    private string RemoveInvalidXmlCharacters(string input)
    {
        if (string.IsNullOrEmpty(input)) return input;

        return new string(input.Where(c =>
            c == '\t' || c == '\n' || c == '\r' ||
            (c >= '\x20' && c <= '\xD7FF') ||
            (c >= '\xE000' && c <= '\xFFFD') ||
            (c >= '\x10000' && c <= '\x10FFFF')
        ).ToArray());
    }
}

第二步:编写服务行为类注册检查器

public class XmlCleanupBehavior : IServiceBehavior
{
    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers.OfType<ChannelDispatcher>())
        {
            foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
            {
                endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new XmlCleanupMessageInspector());
            }
        }
    }

    // 接口其他方法空实现
    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { }
    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { }
}

第三步:在服务宿主中启用行为

ServiceHost host = new ServiceHost(typeof(YourWcfServiceImplementation));
host.Description.Behaviors.Add(new XmlCleanupBehavior());
host.Open();

如果使用配置文件管理服务,也可以注册自定义行为扩展(需额外编写BehaviorExtensionElement子类,代码略),通过配置启用该行为。

2. 自定义序列化行为过滤字符

如果你的WCF服务使用DataContractSerializer或XmlSerializer,可以通过自定义序列化行为,在序列化过程中直接处理非法字符,更贴近数据处理流程。

示例(针对DataContractSerializer):

public class CustomDataContractSerializerBehavior : DataContractSerializerOperationBehavior
{
    public CustomDataContractSerializerBehavior(OperationDescription operation) : base(operation) { }

    public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList<Type> knownTypes)
    {
        XmlWriterSettings settings = new XmlWriterSettings();
        // 使用自定义XmlWriter处理字符
        return new DataContractSerializer(type, name, ns, knownTypes, int.MaxValue, false, true, null, new CustomXmlWriter(settings));
    }
}

// 自定义XmlWriter重写字符写入逻辑
public class CustomXmlWriter : XmlWriter
{
    private readonly XmlWriter _innerWriter;

    public CustomXmlWriter(XmlWriterSettings settings)
    {
        _innerWriter = XmlWriter.Create(new MemoryStream(), settings);
    }

    public override void WriteString(string text)
    {
        string cleanedText = RemoveInvalidXmlCharacters(text);
        _innerWriter.WriteString(cleanedText);
    }

    // 重写CData写入逻辑(如果业务中用到CData)
    public override void WriteCData(string text)
    {
        string cleanedText = RemoveInvalidXmlCharacters(text);
        _innerWriter.WriteCData(cleanedText);
    }

    // 其他XmlWriter方法直接委托给内部Writer
    public override void Close() => _innerWriter.Close();
    public override void Flush() => _innerWriter.Flush();
    public override string LookupPrefix(string ns) => _innerWriter.LookupPrefix(ns);
    public override void WriteBase64(byte[] buffer, int index, int count) => _innerWriter.WriteBase64(buffer, index, count);
    public override void WriteCharEntity(char ch) => _innerWriter.WriteCharEntity(ch);
    public override void WriteChars(char[] buffer, int index, int count) => _innerWriter.WriteChars(buffer, index, count);
    // 按需实现其他XmlWriter抽象方法

    // 复用非法字符过滤逻辑
    private string RemoveInvalidXmlCharacters(string input)
    {
        if (string.IsNullOrEmpty(input)) return input;

        return new string(input.Where(c =>
            c == '\t' || c == '\n' || c == '\r' ||
            (c >= '\x20' && c <= '\xD7FF') ||
            (c >= '\xE000' && c <= '\xFFFD') ||
            (c >= '\x10000' && c <= '\x10FFFF')
        ).ToArray());
    }
}

之后通过IServiceBehavior将这个自定义序列化行为注册到服务的操作中即可(注册逻辑类似消息检查器的行为注册)。

方案对比

  • 消息检查器:侵入性最低,无需修改数据契约或序列化配置,全局覆盖所有响应,适合绝大多数场景。
  • 自定义序列化行为:更贴近数据序列化流程,适合需要精细控制序列化逻辑的场景,但需针对不同序列化器做适配。
  • 自定义文本编码器(进阶):在传输层处理,适合所有文本绑定,但实现复杂度高,一般前两种方案足够解决问题。

注意事项

XML规范允许的字符范围是:U+0009(制表符)、U+000A(换行)、U+000D(回车),以及U+0020到U+D7FF、U+E000到U+FFFD、U+10000到U+10FFFF的字符,过滤逻辑严格遵循此范围即可避免合规问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 00:15:30