Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to throw WebFaultException after StreamReader.ReadToEnd() in C# WCF REST POST?

I have a WCF REST project that returns http status codes as WebFaultExceptions. This works quite well for GET calls but I'm having problems getting the WebFaultException returned for POST calls. The data in the request body is using content type as "application/x-www-form-urlencoded;charset=utf-8". I think the issue is that the underlying request stream is closed when context switches out of the using clause to throw the WebFaultException.

If I throw a WebFaultException before the "using" clause the exception is returned as expected. If I throw the WebFaultException from within the "using" clause the exception is not returned to the client.

Does anyone have any suggestions for how to be able to successfully throw a WebFaultException when using a streamreader to read the request body?

Here's an abbreviated version of my server-side code. Please note that the httpstatuscodes for this example aren't realistic for my actual implementation.

[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; }
}

I've seen this question - Wrong WebFaultException when using a Stream and closing the stream - and although it addresses the issue somewhat, left unanswered is whether it is reasonable to not dispose or close the stream.

Many thanks,

Terry

like image 920
tblank Avatar asked Oct 24 '25 06:10

tblank


1 Answers

The question you link to has now received an accepted answer which addresses your question.

You state:

and although it addresses the issue somewhat, left unanswered is whether it is reasonable to not dispose or close the stream.

To that, the answer states:

But it would be better to not leave the StreamReader without disposing it because it can't clean any unmanaged resources.

To back that up we have the following answer on another StackOverflow thread which argues why calling Dispose() is important: https://stackoverflow.com/a/2548694/700926

To solve the initial problem you ask in your question (WebFaultExceptions are not sent back to the client) I ended up doing as suggested in this answer - subclass the System.IO.StreamReader and override Close so it tells the StreamReader to only relase the unmanaged resources and not close the stream.

My WcfFriendlyStreamReader looks like this:

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

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

As seen from the MSDN documentation calling Dispose(false) only releases the unmanaged resources. And as the decompiler reveals, this also causes the Stream to stay open, which seems to do the trick:

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);
    }
  }
}
like image 137
Lasse Christiansen Avatar answered Oct 26 '25 18:10

Lasse Christiansen