简体   繁体   中英

WCF IErrorHandler Get Request message

(Similar to this question, however it ended up not being needed in that case)

I'm using an IErrorHandler implementation -- creating a FaultException so that clients to the service receive 'pretty' errors in the event of unhandled exceptions.

I'd now like to use an element in the request message and bounce it back in the FaultException and I'm not winning.

I can access the OperationContext.Current.RequestContext.RequestMessage , and can see the full envelope (including the 'message ID' property I want the FaultResponse to contain) using its ToString() , however I'd like to either 'manually' deserialize the request Message, or even just read the body as XML in order to extract the request property I'm looking for.

However I'm not able to read from the message, as (I assume) the Message has already been read somewhere previous in the WCF pipeline:

'The message cannot support the operation because it has been copied'

Here's my ProvideFault() implementation (I'm aware the XElement parsing may not even work in this form -- I'm not focusing on that now):

public void ProvideFault(Exception error, MessageVersion version, ref Message fault) {
    if (error is FaultException)
    {
        // Let WCF handle            
    }
    else
    {
        // Extract the 'messageID' element from the request message
        var req = OperationContext.Current.RequestContext.RequestMessage;

        // Throws the 'has been copied' exception
        XElement body = XElement.Parse(XElement.ReadFrom(req.GetReaderAtBodyContents()).ToString()); 

        var msgId = (string)body.Descendants("messageID").FirstOrDefault();

        var err = new FaultException<Foo>(new Foo
            {
                MessageID = msgId
            }, "Server error");

        var msgFault = err.CreateMessageFault();
        fault = Message.CreateMessage(
            version,
            msgFault,
            null);
    } }

What's the best way to do this?

EDIT: It doesn't look like it's possible to read the OperationContext's Request Message during the IErrorHandler events -- presumably because it's already been read earlier in the WCF pipeline (so creating a copy of the message using the MessageBuffer will not work). A 'workaround' to the problem is described in the accepted answer.

One possible approach is to use an IDispatchMessageInspector to read the values you require out of the request message and store them in OperationContext until you need to access them.

The MSDN documentation for IDispatchMessageInspector is straight forward. Just remember to first create a buffered copy of the message before reading it

public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel, InstanceContext instanceContext)
{
    var bufferedCopy = request.CreateBufferedCopy(2000000); // choose a suitable buffer size
    request = bufferedCopy.CreateMessage();// Message for continued processing

    var messageToRead = bufferedCopy.CreateMessage(); // message to read properties from

    return null;
}

Refer to this question on how to attach your properties to OperationContext.

You should copy the buffer of original message:

// Read orignal messagem
var originalMessage = OperationContext.Current.RequestContext.RequestMessage;

// Copy message
MessageBuffer buffer = originalMessage.CreateBufferedCopy(Int32.MaxValue);
//OperationContext.Current.RequestContext.RequestMessage = buffer.CreateMessage();

var req = buffer.CreateMessage();
XElement body = XElement.Parse(XElement.ReadFrom(req.GetReaderAtBodyContents()).ToString());

Hope it helps.

You should customize your exception and use it properties to keep Id

public void ProvideFault(Exception error, MessageVersion version, ref Message fault) 
{
    var myException = error as MyException;
    if (myException == null)
    {
        return;           
    }

    var err = new FaultException<Foo>(new Foo
        {
            MessageID = myException.MsgId
        }, "Server error");

    var msgFault = err.CreateMessageFault();
    fault = Message.CreateMessage(
        version,
        msgFault,
        null);
} 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM