3

我有一个 WCF REST 项目,它以 WebFaultExceptions 的形式返回 http 状态代码。这对于 GET 调用非常有效,但我在为 POST 调用返回 WebFaultException 时遇到问题。请求正文中的数据使用内容类型为“application/x-www-form-urlencoded;charset=utf-8”。我认为问题在于,当上下文切换出 using 子句以引发 WebFaultException 时,底层请求流已关闭。

如果我在“使用”子句之前抛出 WebFaultException,则会按预期返回异常。如果我从“使用”子句中抛出 WebFaultException,则异常不会返回给客户端。

有没有人对使用流读取器读取请求正文时如何能够成功抛出 WebFaultException 有任何建议?

这是我的服务器端代码的缩写版本。请注意,此示例的 httpstatuscodes 对于我的实际实现是不现实的。

[WebInvoke(Method = "POST"
, UriTemplate = "urls/{id}"
, BodyStyle = WebMessageBodyStyle.WrappedRequest
)]
public string PostItem(string id, object streamdata)
{

    int _id = 0;
    if (int.TryParse(companyIdSr2, out _id))
    {
        using (System.IO.StreamReader reader = new System.IO.StreamReader(streamdata))
        {
            string body = reader.ReadToEnd();

            if(string.IsNullOrEmpty(body))
            {
                // this exception doesn't make it back to the client's request object
                ThrowError(HttpStatusCode.BadRequest, "empty body");
            }   
        }
    }
    else
    {
        // this exception is successfully returned to the client's request object
        ThrowError(HttpStatusCode.BadRequest, "invalid id");        
    }    
}

private static void ThrowError(HttpStatusCode status, string message)
{
    request_error error = new request_error
    {
        request_url = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.RequestUri.OriginalString,
        error_status_code = status.ToString(),
        error_message = message,
    };

    throw new WebFaultException<request_error>(error, status);
}

public class request_error
{
    [XmlElement("request_url")]
    public string request_url { get; set; }
    [XmlElement("error_status_code")]
    public string error_status_code { get; set; }
    [XmlElement("error_message")]
    public string error_message { get; set; }
}

我已经看到了这个问题 - Wrong WebFaultException when using a Stream and close the stream - 虽然它在一定程度上解决了这个问题,但没有回答的是不处置或关闭流是否合理。

非常感谢,

特里

4

1 回答 1

3

您链接到的问题现在已收到一个已接受的答案,可以解决您的问题。

你说:

尽管它在一定程度上解决了这个问题,但没有回答的是不处置或关闭流是否合理。

对此,答案是:

但是最好不要在没有处理它的情况下离开 StreamReader,因为它无法清理任何非托管资源。

为了支持这一点,我们在另一个 StackOverflow 线程上有以下答案,该线程争论了为什么调用Dispose()很重要:https ://stackoverflow.com/a/2548694/700926

为了解决您在问题中提出的初始问题(WebFaultExceptions 不会发送回客户端),我最终按照此答案中的建议进行操作-子类化System.IO.StreamReader并覆盖Close,因此它告诉 StreamReader 仅释放非托管资源而不关闭流.

我的 WcfFriendlyStreamReader 看起来像这样:

public class WcfFriendlyStreamReader : StreamReader
{
    public WcfFriendlyStreamReader(Stream s) : base(s) { }

    public override void Close()
    {
        base.Dispose(false);
    }
}

MSDN 文档中可以看出,调用Dispose(false)仅释放非托管资源。正如反编译器所揭示的那样,这也会导致Stream保持打开状态,这似乎起到了作用:

protected override void Dispose(bool disposing)
{
  try
  {
    if (this.LeaveOpen || !disposing || this.stream == null)
      return;
    this.stream.Close();
  }
  finally
  {
    if (!this.LeaveOpen && this.stream != null)
    {
      this.stream = (Stream) null;
      this.encoding = (Encoding) null;
      this.decoder = (Decoder) null;
      this.byteBuffer = (byte[]) null;
      this.charBuffer = (char[]) null;
      this.charPos = 0;
      this.charLen = 0;
      base.Dispose(disposing);
    }
  }
}
于 2013-12-15T18:35:56.350 回答