【问题标题】:WCF returned System.IO.Stream garbledWCF 返回 System.IO.Stream 乱码
【发布时间】:2012-05-04 10:09:07
【问题描述】:

我在我的软件项目中使用 WCF 接口包装现有的 ASMX Web 服务作为过渡阶段,以节省时间。除了一个返回 System.String 的函数外,这很好用。

原始 ASMX 服务根据给定的参数返回文本或 XML。这在 ASMX 中不是问题。在 WCF 中,返回的值(如果是 XML)被转义为:<gml>,它应该是 <gml>。请参阅下面的 SOAP。

请求

POST http://someuri.org/WebServices/Utils.svc HTTP/1.1
Content-Type: text/xml; charset=utf-8
SOAPAction: http://www.someuri.org/IUtils/Function
Content-Length: 283
Accept: */*
User-Agent: Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)
Host: foo.bar.org
Connection: Keep-Alive

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <s:Body>
        <Function xmlns="http://www.someri.org/">
            <type>...</type>
            <input1>...</input1>
            <input2>...</input2>
            <input3>true</input3>
        </Function >
    </s:Body>
</s:Envelope>

回应

HTTP/1.1 200 OK
Date: Fri, 04 May 2012 11:40:01 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
X-AspNet-Version: 2.0.50727
Cache-Control: private
Content-Type: text/xml; charset=utf-8
Content-Length: 2070

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <s:Body>
        <FunctionResponse xmlns="http://www.crotec.nl/">   
            <FunctionResult>&lt;gml&gt;data&lt;/gml&gt;</FunctionResult>
        </FunctionResponse>
    </s:Body>
</s:Envelope>

一些谷歌搜索让我返回一个 System.IO.Stream 对象。

string result = DoStuff(arg1, arg2, arg3);            
byte[] bin = Encoding.UTF8.GetBytes(result);
WebOperationContext.Current.OutgoingResponse.ContentType = "text/plain";
return new System.IO.MemoryStream(bin);

这在一定程度上有效。

string result =  "02010415DBD800D7E17577787A626978";
byte[] bin = {48,50,48,49,48,52,49,53,68,66,68,56,48,48,68,55,69,49,55,53,55,55,55,56,55,65,54,50,54,57,55,56};

但是 SOAP 消息中的返回结果是:

MDIwMTA0MTVEQkQ4MDBEN0UxNzU3Nzc4N0E2MjY5Nzg=

所以生成的输出是乱码(我认为还是由消息编码引起的(?))

该方法是带有 OperationContract 的属性,并且该服务托管在 IIS6 中,具有以下 ABC:

<service name="WebServices.BeheerUtils" behaviorConfiguration="Services.ServiceBehavior">
    <!-- Service Endpoints -->
    <endpoint address="" binding="basicHttpBinding" contract="WebServices.IUtils"/>
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>

任何想法为什么输出是乱码或如何防止 HTML 编码?

界面

[OperationContract]
System.IO.Stream Function(string type, string input1, string input2, string input3);

实施

public new System.IO.Stream Function(string type, string input1, string input2, string input3)
{
    // Call the old ASMX method
    string result = DoStuff(type, input1, input2, input3, true);

    byte[] bin = Encoding.UTF8.GetBytes(result);
    WebOperationContext.Current.OutgoingResponse.ContentType = "text/plain";
    return new System.IO.MemoryStream(bin);
}

【问题讨论】:

  • 文本MDIwMTA0MTVEQkQ4MDBEN0UxNzU3Nzc4N0E2MjY5Nzg=02010415DBD800D7E17577787A626978的base64编码值。
  • 您能否在您的 WCF 服务接口上发布详细信息 - 至少是方法定义。我真的不明白为什么使用 asmx 可以返回字符串或 xml,而使用 WCF 则无法做到(xml 不只是一个字符串吗?)。
  • 感谢您的更新,但您能回答我的第二个问题吗?为什么不能直接返回字符串?
  • 因为 DoStuff() 方法也可以返回 XML 数据。变量中的这个 XML 字符串可能看起来像“data”,但在返回的 SOAP 信封中,它看起来像“<gml>data</gml>”。这是我希望解决的实际问题。您知道如何更改 Stream 的编码吗?从 64 位到 8 位?
  • 为什么要检查 SOAP 消息?有必要在这么低的水平上处理吗? WCF 必须对特殊的 XML 字符进行编码才能在 XML 消息中发送它 - 这是一种标准和预期的行为。

标签: c# wcf stream iis-6


【解决方案1】:

如果你的函数返回一个字符串,那么它应该被编码是正常的。

您可以尝试将您的函数声明为返回 XmlNode 而不是字符串。

【讨论】:

  • XmlNode 是否能够包含纯文本?所以没有父元素?
  • 添加到我的第一个回复中;我不一定认为它被逃脱是正常的。至少不是没有给我一个禁用此转义的选项。也许有这样的选择?在那种情况下,我想学习如何做到这一点,因为我还无法获得它。
  • @Nebula,我不知道禁用转义的选项。 SOAP 消息中的字符串需要转义,但这对调用者应该是透明的 - 任何提取节点内容的 XML API 都会自动对字符串进行转义。
  • 是的,我也料到了。然而,ASP Classic 的 XML API 似乎并没有为我们做到这一点(我的同事写了这个,我在 VBS 方面没有受过特别好的培训)
【解决方案2】:

我不能同意这种说法:

这在 ASMX 中不是问题。在 WCF 中,返回的值(如果是 XML)是 转义为:&amp;lt;gml&amp;gt; 应该是&lt;gml&gt;

我用方法创建了一个服务:

[OperationContract]
string GetXml(string str);

public string GetXml(string str)
{
    return "<gml>" + str + "</gml>";
}

然后我用生成的 WCF 客户端调用服务:

var client = new MyWcfServiceClient();
var result = client.GetXml("test");

结果是:

<gml>test</gml>

更新

为了确保您是 xml 的标准行为方式,请执行以下测试并检查 respone.FunctionResult 的值:

public class FunctionResponse
{
    public string FunctionResult { get; set; }
}

public void Test()
{
    var serialized = @"<?xml version=""1.0"" encoding=""UTF-8""?>
<FunctionResponse>   
    <FunctionResult>&lt;gml&gt;data&lt;/gml&gt;</FunctionResult>
</FunctionResponse>";
    var ser = new XmlSerializer(typeof(FunctionResponse));
    using (var stringReader = new StringReader(serialized))
    {
        using (var xmlReader = new XmlTextReader(stringReader))
        {
            var response = ser.Deserialize(xmlReader);                    
        }
    }            
}

【讨论】:

  • 我向你保证,我没有说谎。请查看更新后的问题。
  • @Nebula:我不是说你在撒谎 :) 我是说如果你生成/创建 wcf 客户端,你应该得到正确的结果。
  • 啊,现在我看到了问题所在。实际上,该服务是由一个经典的 ASP 网站使用的。我想这就是存在差距的地方。我们必须手动编写所有消息,所以没有我可以使用的框架。
  • @Nebula:经典的 ASP 是什么意思?添加 Web 引用(不是服务引用)也可以正常工作。
  • 希望我能正确理解您的问题。经典 ASP 是 ASP.Net 之前的技术。在那里,您不能简单地添加对 Web 服务的引用,因为与 ASP.Net 相比,它本身不受支持。我更新后的问题是否会给您启发来解决这个问题而不添加额外的 XML 元素?
【解决方案3】:

使用 basicHttpBinding 而不是 wsHttpBinding。

【讨论】:

    【解决方案4】:

    所以我摆弄了一些返回类型,这就是我能做到的:

    string result = DoStuff(type, input1, input2, input3, true);
    
    XDocument d = new XDocument();
    XDocument basis = new XDocument(new XElement("result"));
    
    // Load the result into a XDocument, add the result as a value of the wrapper element if the format is invalid    
    try
    {
        d = XDocument.Parse(result);
        basis.Root.Add(d.Root);
    }
    catch (Exception)
    {
        basis.Root.Value = result;
    }    
    
    // Return the XElement
    return basis.Root;
    

    我基本上将所有响应包装在一个新的 Root 元素中。这给了我封装的 SOAP 中的文字 XML,而不是“html 编码”。尽管它可以满足我的需求,但我觉得很尴尬。但是我发现没有其他解决方案适合我的需要,因此我将其作为最终解决方案提出。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-02
      • 1970-01-01
      • 2012-04-13
      • 1970-01-01
      相关资源
      最近更新 更多