替换WCF消息的内容

时间:2011-09-02 16:12:50

标签: xml wcf

我正在尝试设置一个路由服务,它将位于我的网络的DMZ中,并允许外部人员访问一些内部托管的WCF服务。我已经设置好所有工作,但是当我转发MEX服务时,它会将我们的外部客户端指向我们的内部地址,这显然是他们无法访问的。

Microsoft seems to recommend making a copy of the wsdl,它可能会起作用,但每次服务定义发生变化时都需要我制作wsdl的新副本,而这些副本经常发生,而且看起来有点过分。唯一需要改变的是mex消息中的地址。

       <endpoint address="http://appsrv1:8781/ProcessManagementService/"
            binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IProcessManagementService"
            contract="IProcessManagementService" name="WSHttpBinding_IProcessManagementService" />

似乎使用IDispatchMessageInspector,我应该能够拦截mex消息并用外部服务器名称替换内部服务器名称,然后我只需要在需要时触摸路由服务添加或删除服务,而不是每次我做任何更改。

我对XML阅读器,编写器等没有太多经验,所以我正在寻找有关如何继续的一些指导。如果我可以将xml内容读入字符串,我可以执行替换操作以替换内部地址的外部地址,然后用我的修改版本替换回复的消息内容。我该如何去做,或者有更好的方法来修改WCF消息的内容?

编辑:所以这就是我到目前为止拼凑的东西。

public class EndpointReplacementInspector : IDispatchMessageInspector
{
    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        return null;
    }

    public void BeforeSendReply(ref Message reply, object correlationState)
    {           
        var ms = new MemoryStream();
        var w = XmlWriter.Create(ms, new XmlWriterSettings { Indent = true, IndentChars = "  ", OmitXmlDeclaration = true });
        var bodyReader = reply.GetReaderAtBodyContents();
        w.WriteStartElement("s", "Body", "http://schemas.xmlsoap.org/soap/envelope/");
        while (bodyReader.NodeType != XmlNodeType.EndElement && bodyReader.LocalName != "Body" && bodyReader.NamespaceURI != "http://schemas.xmlsoap.org/soap/envelope/")
        {
            if (bodyReader.NodeType != XmlNodeType.Whitespace)
            {
                w.WriteNode(bodyReader, true);
            }
            else
            {
                bodyReader.Read(); // ignore whitespace; maintain if you want
            }
        }
        w.WriteEndElement();
        w.Flush();
        var body = Encoding.UTF8.GetString(ms.ToArray());
        body = body.Replace("internalserver", "externalserver");            
        var replacedMessage = Message.CreateMessage(XmlReader.Create(new StringReader(body)), int.MaxValue, reply.Version);
        replacedMessage.Headers.CopyHeadersFrom(reply.Headers);
        replacedMessage.Properties.CopyProperties(reply.Properties);
        reply = replacedMessage;    
    }
}

似乎主要是工作。但是,XMLReader抛出异常“根级别的数据无效。第1行,位置1”。当我尝试创建消息时。我不知道从那里开始。

编辑2:

好的,现在我有一个方法将消息提取到xdocument中,然后将其发送到字符串,然后对其进行编辑,然后将其拉回到xdocument中,当我获得“连接强制关闭”时发回包含该已编辑消息的消息。可怕。

编辑3:

经过测试,只需从回复xdoc中提取消息然后将其加载到新消息中就足以导致“连接强制关闭”问题。这绝不是编辑邮件的正确方法。我正在寻找有关如何最好地解决这个问题的示例或经验。

3 个答案:

答案 0 :(得分:10)

我从MSDN Forums得到了答案。我的问题是我正在改变字符串的长度,但没有重置MemoryStream的长度,这意味着最后的字节没有被报告。

这是一个有效的替换功能。

 public Message ChangeString(Message oldMessage, string from, string to)
        {
            MemoryStream ms = new MemoryStream();
            XmlWriter xw = XmlWriter.Create(ms);
            oldMessage.WriteMessage(xw);
            xw.Flush();
            string body = Encoding.UTF8.GetString(ms.ToArray());
            xw.Close();

            body = body.Replace(from, to);

            ms = new MemoryStream(Encoding.UTF8.GetBytes(body));
            XmlDictionaryReader xdr = XmlDictionaryReader.CreateTextReader(ms, new XmlDictionaryReaderQuotas());
            Message newMessage = Message.CreateMessage(xdr, int.MaxValue, oldMessage.Version);
            newMessage.Properties.CopyProperties(oldMessage.Properties);
            return newMessage; 
       }

答案 1 :(得分:1)

如果您使用的是.NET 4.0,则只需要为您的服务配置代码中的<useRequestHeadersForMetadataAddress>UseRequestHeadersForMetadataAddressElement

答案 2 :(得分:1)

对于那些喜欢使用Linq to XML的人。

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Bootstrap Example</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
  <script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
  <!-- Include the plugin's CSS and JS: -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-multiselect/0.9.13/js/bootstrap-multiselect.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-multiselect/0.9.13/css/bootstrap-multiselect.css" type="text/css"/>
<script type="text/javascript">
    $(document).ready(function() {
        $('#example-optionClass').multiselect({
            
            optionClass: function(element) {
                var value = $(element).val();
 
                if (value%2 == 0) {
                    return 'even';
                }
                else {
                    return 'odd';
                }
            }
        });
    });
</script>
<style type="text/css">
    #example-optionClass-container .multiselect-container li.odd {
        background: #eeeeee;
    }
</style>
</head>
<body>

<div class="container">
  <h2>Bootstrap Multiselect Test</h2>
  <p>I want to change the Multiselect <b>background color</b>  If 'None Selected'</p>            
  <div id="example-optionClass-container">
    <select id="example-optionClass" multiple="multiple">
        <option value="1">Option 1</option>
        <option value="2">Option 2</option>
        <option value="3">Option 3</option>
        <option value="4">Option 4</option>
        <option value="5">Option 5</option>
        <option value="6">Option 6</option>
    </select>
</div>
</div>

</body>
</html>