Wrong WebFaultException when using a Stream and cl

2019-06-18 04:21发布

We have a REST API, built with WCF.

We handle all the exceptions backend with WebFaultException like this:

throw new WebFaultException<string>(e.Message, HttpStatusCode.NotAcceptable);

This works just fine except in one scenario where we do a Post, with a stream.

An example of this:

[WebInvoke(Method = "POST", UriTemplate = "saveUser?sessionId={sessionId}&userId={userId}",
        RequestFormat = WebMessageFormat.Json,
        ResponseFormat = WebMessageFormat.Json,            
        BodyStyle = WebMessageBodyStyle.WrappedRequest)]
    [OperationContract]
    string SaveUser(string sessionId, int userId, Stream stream);

When handling this stream in a using statement, whenever we then run into an exception we keep getting:

From Fiddler:

HTTP/1.1 400 Bad Request 
  <p>The server encountered an error processing the request. The exception message is 'The message object has been disposed.'. See server logs for more details. The exception stack trace is: </p>
  <p>  at System.ServiceModel.Channels.ByteStreamMessage.InternalByteStreamMessage.get_Properties()
   at System.ServiceModel.OperationContext.get_IncomingMessageProperties()
   at System.ServiceModel.Dispatcher.WebErrorHandler.ProvideFault(Exception error, MessageVersion version, Message&amp; fault)</p>

Looks like it has something to do with the stream and StreamReader being disposed.

I have then tried to remove anything that will dispose the StreamReader, and this acctualy works. The code handling this now looks like this:

enter image description here

This solves the problem with sending correct exception messages, but how bad will this affect our application, not closing or disposing our StreamReader? Do you see any other ways of solving this ?

1条回答
我欲成王,谁敢阻挡
2楼-- · 2019-06-18 04:46

This happens because the StreamReader takes over 'ownership' of the stream. In other words, it makes itself responsible for closing the source stream. As soon as your program calls Dispose or Close (leaving the using statement scope in your case) then it will dispose the source stream as well. Calling sr.Dispose() in your case. So the file stream is dead after.

If you don't want this you could create a new class which inherits from StreamReader and override the Close method; inside your Close method, call Dispose(false) which does not close the stream.

You could also use the NonClosingStreamWrapper class from Jon Skeet's MiscUtil library, it serves exactly that purpose.

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

查看更多
登录 后发表回答