/// <inheritdoc/>
        public override void WriteObject(object graph, Type type, ODataMessageWriter messageWriter, ODataSerializerContext writeContext)
        {
            if (graph == null)
            {
                throw Error.ArgumentNull("graph");
            }
            if (messageWriter == null)
            {
                throw Error.ArgumentNull("messageWriter");
            }

            ODataError oDataError = graph as ODataError;
            if (oDataError == null)
            {
                HttpError httpError = graph as HttpError;
                if (httpError == null)
                {
                    string message = Error.Format(SRResources.ErrorTypeMustBeODataErrorOrHttpError, graph.GetType().FullName);
                    throw new SerializationException(message);
                }
                else
                {
                    oDataError = httpError.CreateODataError();
                }
            }

            bool includeDebugInformation = oDataError.InnerError != null;
            messageWriter.WriteError(oDataError, includeDebugInformation);
        }
Example #2
0
        /// <summary>
        /// Writes an ODataError with the given custom instance annotation to the test stream.
        /// </summary>
        private void WriteError(params KeyValuePair <string, ODataValue>[] annotations)
        {
            var writerSettings = new ODataMessageWriterSettings {
                DisableMessageStreamDisposal = true
            };

            writerSettings.SetContentType(ODataFormat.Json);
            writerSettings.SetServiceDocumentUri(new Uri("http://example.com/"));

            IODataResponseMessage messageToWrite = new InMemoryMessage {
                StatusCode = 400, Stream = this.stream
            };

            var error = new ODataError();
            var instanceAnnotations = new Collection <ODataInstanceAnnotation>();

            error.SetInstanceAnnotations(instanceAnnotations);

            foreach (var pair in annotations)
            {
                ODataInstanceAnnotation annotation = new ODataInstanceAnnotation(pair.Key, pair.Value);
                instanceAnnotations.Add(annotation);
            }

            using (var writer = new ODataMessageWriter(messageToWrite, writerSettings, this.model))
            {
                writer.WriteError(error, false);
            }
        }
Example #3
0
        /// <inheritdoc/>
        public override void WriteObject(object graph, Type type, ODataMessageWriter messageWriter, ODataSerializerContext writeContext)
        {
            if (graph == null)
            {
                throw Error.ArgumentNull("graph");
            }
            if (messageWriter == null)
            {
                throw Error.ArgumentNull("messageWriter");
            }

            ODataError oDataError = graph as ODataError;

            if (oDataError == null)
            {
                HttpError httpError = graph as HttpError;
                if (httpError == null)
                {
                    string message = Error.Format(SRResources.ErrorTypeMustBeODataErrorOrHttpError, graph.GetType().FullName);
                    throw new SerializationException(message);
                }
                else
                {
                    oDataError = httpError.CreateODataError();
                }
            }

            bool includeDebugInformation = oDataError.InnerError != null;

            messageWriter.WriteError(oDataError, includeDebugInformation);
        }
Example #4
0
        /// <summary>
        /// Writes the error with fallback logic for XML cases where the writer is in an error state and a new writer must be created.
        /// </summary>
        /// <param name="messageWriter">The message writer.</param>
        /// <param name="encoding">The encoding to use for the error if we have to fallback.</param>
        /// <param name="responseStream">The response stream to write to in the fallback case.</param>
        /// <param name="args">The args for the error.</param>
        /// <param name="error">The error to write.</param>
        /// <param name="messageWriterBuilder">MessageWriterBuilder to use if a new ODataMessageWriter needs to be constructed.</param>
        private static void WriteErrorWithFallbackForXml(ODataMessageWriter messageWriter, Encoding encoding, Stream responseStream, HandleExceptionArgs args, ODataError error, MessageWriterBuilder messageWriterBuilder)
        {
            Debug.Assert(args != null, "args != null");
#if DEBUG
            Debug.Assert(args.ProcessExceptionWasCalled, "ProcessException was not called by the time we tried to serialze this error message with ODataLib.");
#endif

            if (messageWriter != null)
            {
                try
                {
                    // If the XmlWriter inside the ODataMessageWriter had entered Error state, ODataMessageWriter.WriteError would throw an InvalidOperationException
                    // when we try to write to it. Note that XmlWriter doesn't always throw an XmlException when it enters Error state.
                    // The right thing to do is we don't write any more because at this point we don't know what's been written to the underlying
                    // stream. However we still should flush the writer to make sure that all the content that was written but is sitting in the buffers actually appears
                    // in the stream before writing the instream error. Otherwise the buffer will be flushed when disposing the writer later and we would end up with
                    // either content written after the instream error (this would also result in having the Xml declaration in the middle of the payload -
                    // [Astoria-ODataLib-Integration] In-stream errors due to XmlExceptions are written out backwards (error before partial valid payload)) or,
                    // hypothetically, the instream error in the middle of the other content that was already partially written. For example we can end up with a payload that
                    // looks like <element attr="val<m:error... The XmlReader would not be able to parse the error payload in this case. Disposing the writer will flush the buffer.
                    // It is fine to do it since the writer is not usable at this point anyways. Also note that the writer will be disposed more than once (e.g. in finally block
                    // in ResponseBodySerializer) but only the first call will have any effect.
                    // However since in the versions we shipped we always create a new XmlWriter to serialize the error payload when the existing
                    // one is in error state, we will continue to do the same to avoid introducing any breaking change here.
                    messageWriter.WriteError(error, args.UseVerboseErrors);
                }
                catch (ODataException e)
                {
                    // Yikes, ODataLib threw while writing the error. This tends to happen if the service author did something invalid during custom
                    // error handling, such as add an custom instance annotation to the error payload. In this dire case, we treat it almost like
                    // an in-stream error, and abort the previous writing. We write out the new error. Note that this will produce an invalid payload like
                    // the situation noted above with XmlWriter errors.
                    WebUtil.Dispose(messageWriter);
                    messageWriterBuilder.SetMessageForErrorInError();
                    var        newErrorWriter = messageWriterBuilder.CreateWriter();
                    ODataError errorWhileWritingOtherError = new ODataError()
                    {
                        ErrorCode  = "500",
                        InnerError = new ODataInnerError(e),
                        Message    = Strings.ErrorHandler_ErrorWhileWritingError
                    };

                    newErrorWriter.WriteError(errorWhileWritingOtherError, args.UseVerboseErrors);
                }
                catch (InvalidOperationException)
                {
                    Debug.Assert(ContentTypeUtil.IsNotJson(args.ResponseContentType), "Should never get here for JSON responses");
                    WebUtil.Dispose(messageWriter);

                    // if either an InvalidOperationException was encountered (see comment above) or the message writer was null, write the error out manually.
                    Debug.Assert(responseStream != null, "responseStream != null");
                    using (XmlWriter xmlWriter = XmlWriter.Create(responseStream, XmlUtil.CreateXmlWriterSettings(encoding)))
                    {
                        ErrorUtils.WriteXmlError(xmlWriter, error, args.UseVerboseErrors, MaxInnerErrorDepth);
                    }
                }
            }
        }
Example #5
0
        /// <inheritdoc/>
        public override void WriteObject(object graph, Type type, ODataMessageWriter messageWriter, ODataSerializerContext writeContext)
        {
            if (graph == null)
            {
                throw Error.ArgumentNull("graph");
            }
            if (messageWriter == null)
            {
                throw Error.ArgumentNull("messageWriter");
            }

            ODataError oDataError = GetError(graph);
            bool       includeDebugInformation = oDataError.InnerError != null;

            messageWriter.WriteError(oDataError, includeDebugInformation);
        }
Example #6
0
        public void Write(MemoryStream stream, NephosErrorDetails errorDetails, Exception errorException, bool useVerboseErrors)
        {
            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }
            if (errorDetails == null)
            {
                throw new ArgumentNullException("errorDetails");
            }
            string item = null;
            string str  = null;

            if (errorDetails.ResponseHeaders != null)
            {
                item = errorDetails.ResponseHeaders["Content-Type"];
                str  = errorDetails.ResponseHeaders["DataServiceVersion"];
            }
            if (string.IsNullOrWhiteSpace(item))
            {
                throw new ArgumentException(string.Format("errorDetails should contain a valid value for the key [{0}] in ResponseHeader.", "Content-Type"));
            }
            if (string.IsNullOrWhiteSpace(str))
            {
                throw new ArgumentException(string.Format("errorDetails should contain a valid value for the key [{0}] in ResponseHeader.", "MaxDataServiceVersion"));
            }
            ODataError oDataError = new ODataError()
            {
                ErrorCode       = errorDetails.StatusEntry.StatusId,
                Message         = errorDetails.UserSafeErrorMessage,
                MessageLanguage = "en-US",
                InnerError      = new ODataInnerError(errorException)
            };
            ODataMessageWriterSettings oDataMessageWriterSetting = new ODataMessageWriterSettings()
            {
                DisableMessageStreamDisposal = true
            };
            ResponseMessage responseMessage = new ResponseMessage(stream);

            responseMessage.SetHeader("MaxDataServiceVersion", str);
            responseMessage.SetHeader("Content-Type", item);
            using (ODataMessageWriter oDataMessageWriter = new ODataMessageWriter(responseMessage, oDataMessageWriterSetting))
            {
                oDataMessageWriter.WriteError(oDataError, useVerboseErrors);
            }
        }
Example #7
0
        internal static void SerializeODataError(HandleExceptionArgs args, ODataMessageWriter writer, Stream outputStream, Encoding encoding)
        {
            ODataError error = CreateODataErrorFromExceptionArgs(args);

            try
            {
                writer.WriteError(error, args.UseVerboseErrors);
            }
            catch (InvalidOperationException)
            {
                if (!WebUtil.CompareMimeType(args.ResponseContentType, "application/json;odata=verbose"))
                {
                    WebUtil.Dispose(writer);
                    using (XmlWriter writer2 = XmlWriter.Create(outputStream, XmlUtil.CreateXmlWriterSettings(encoding)))
                    {
                        ErrorUtils.WriteXmlError(writer2, error, args.UseVerboseErrors, 100);
                    }
                }
            }
        }
Example #8
0
        public void ShouldBeAbleToWriteCustomInstanceAnnotationToErrorInJsonLight()
        {
            const string expectedPayload =
                "{" +
                "\"error\":{" +
                "\"code\":\"400\"," +
                "\"message\":\"Resource not found for the segment 'Address'.\"," +
                "\"@instance.annotation\":\"stringValue\"" +
                "}" +
                "}";

            var writerSettings = new ODataMessageWriterSettings {
                DisableMessageStreamDisposal = true
            };

            writerSettings.SetContentType(ODataFormat.Json);
            writerSettings.ODataUri = new ODataUri()
            {
                ServiceRoot = new Uri("http://www.example.com")
            };

            MemoryStream          stream         = new MemoryStream();
            IODataResponseMessage messageToWrite = new InMemoryMessage {
                StatusCode = 400, Stream = stream
            };

            // Write payload
            using (var messageWriter = new ODataMessageWriter(messageToWrite, writerSettings, Model))
            {
                ODataError error = new ODataError {
                    ErrorCode = "400", Message = "Resource not found for the segment 'Address'."
                };
                error.InstanceAnnotations.Add(new ODataInstanceAnnotation("instance.annotation", new ODataPrimitiveValue("stringValue")));
                messageWriter.WriteError(error, includeDebugInformation: true);
            }

            stream.Position = 0;
            string payload = (new StreamReader(stream)).ReadToEnd();

            payload.Should().Be(expectedPayload);
        }
        public override void WriteObject(object graph, ODataMessageWriter messageWriter, ODataSerializerContext writeContext)
        {
            if (graph == null)
            {
                throw Error.ArgumentNull("graph");
            }

            if (messageWriter == null)
            {
                throw Error.ArgumentNull("messageWriter");
            }

            ODataError odataError = graph as ODataError;          
            if (odataError == null)
            {
                throw Error.InvalidOperation(SRResources.ErrorTypeMustBeODataError, graph.GetType().FullName);
            }

            bool includeDebugInformation = odataError.InnerError != null;
            messageWriter.WriteError(odataError, includeDebugInformation);
        }
Example #10
0
        public override void WriteObject(object graph, ODataMessageWriter messageWriter, ODataSerializerWriteContext writeContext)
        {
            if (graph == null)
            {
                throw Error.ArgumentNull("graph");
            }

            if (messageWriter == null)
            {
                throw Error.ArgumentNull("messageWriter");
            }

            ODataError odataError = graph as ODataError;

            if (odataError == null)
            {
                throw Error.InvalidOperation(SRResources.ErrorTypeMustBeODataError, graph.GetType().FullName);
            }

            bool includeDebugInformation = odataError.InnerError != null;

            messageWriter.WriteError(odataError, includeDebugInformation);
        }
        /// <summary>
        /// Writes an ODataError with the given custom instance annotation to the test stream.
        /// </summary>
        private void WriteError(params KeyValuePair<string, ODataValue>[] annotations)
        {
            var writerSettings = new ODataMessageWriterSettings { DisableMessageStreamDisposal = true };
            writerSettings.SetContentType(ODataFormat.Json);
            writerSettings.SetServiceDocumentUri(new Uri("http://example.com/"));

            IODataResponseMessage messageToWrite = new InMemoryMessage { StatusCode = 400, Stream = this.stream };

            var error = new ODataError();
            var instanceAnnotations = new Collection<ODataInstanceAnnotation>();
            error.SetInstanceAnnotations(instanceAnnotations);

            foreach (var pair in annotations)
            {
                ODataInstanceAnnotation annotation = new ODataInstanceAnnotation(pair.Key, pair.Value);
                instanceAnnotations.Add(annotation);
            }

            using (var writer = new ODataMessageWriter(messageToWrite, writerSettings, this.model))
            {
                writer.WriteError(error, false);
            }
        }
Example #12
0
 internal static void SerializeODataError(HandleExceptionArgs args, ODataMessageWriter writer, Stream outputStream, Encoding encoding)
 {
     ODataError error = CreateODataErrorFromExceptionArgs(args);
     try
     {
         writer.WriteError(error, args.UseVerboseErrors);
     }
     catch (InvalidOperationException)
     {
         if (!WebUtil.CompareMimeType(args.ResponseContentType, "application/json;odata=verbose"))
         {
             WebUtil.Dispose(writer);
             using (XmlWriter writer2 = XmlWriter.Create(outputStream, XmlUtil.CreateXmlWriterSettings(encoding)))
             {
                 ErrorUtils.WriteXmlError(writer2, error, args.UseVerboseErrors, 100);
             }
         }
     }
 }
Example #13
0
        public async Task WriteInStreamError_APIsShouldYieldSameResult()
        {
            var nestedWriterSettings = new ODataMessageWriterSettings
            {
                Version = ODataVersion.V4,
                EnableMessageStreamDisposal = false
            };

            nestedWriterSettings.SetServiceDocumentUri(new Uri(ServiceUri));

            var asyncException = await Assert.ThrowsAsync <ODataException>(async() =>
            {
                IODataResponseMessage asyncResponseMessage = new InMemoryMessage {
                    StatusCode = 200, Stream = this.asyncStream
                };
                using (var messageWriter = new ODataMessageWriter(asyncResponseMessage, writerSettings))
                {
                    // Call to CreateODataAsynchronousWriterAsync triggers setting of output in-stream error listener
                    var asynchronousWriter     = await messageWriter.CreateODataAsynchronousWriterAsync();
                    var responseMessage        = await asynchronousWriter.CreateResponseMessageAsync();
                    responseMessage.StatusCode = 200;

                    // Next section added is to demonstrate that what was already written is flushed to the buffer before exception is thrown
                    using (var nestedMessageWriter = new ODataMessageWriter(responseMessage, nestedWriterSettings))
                    {
                        var writer = await nestedMessageWriter.CreateODataResourceWriterAsync();
                    }

                    await messageWriter.WriteErrorAsync(
                        new ODataError {
                        ErrorCode = "NRE", Message = "Object reference not set to an instance of an object."
                    },
                        /*includeDebugInformation*/ true);
                }
            });

            this.asyncStream.Position = 0;
            var asyncResult = await new StreamReader(this.asyncStream).ReadToEndAsync();

            var syncException = await Assert.ThrowsAsync <ODataException>(
                () => TaskUtils.GetTaskForSynchronousOperation(() =>
            {
                IODataResponseMessage syncResponseMessage = new InMemoryMessage {
                    StatusCode = 200, Stream = this.syncStream
                };
                using (var messageWriter = new ODataMessageWriter(syncResponseMessage, writerSettings))
                {
                    // Call to CreateODataAsynchronousWriterAsync triggers setting of output in-stream error listener
                    var asynchronousWriter     = messageWriter.CreateODataAsynchronousWriter();
                    var responseMessage        = asynchronousWriter.CreateResponseMessage();
                    responseMessage.StatusCode = 200;

                    // Next section is added to demonstrate that what was already written is flushed to the buffer before exception is thrown
                    using (var nestedMessageWriter = new ODataMessageWriter(responseMessage, nestedWriterSettings))
                    {
                        var writer = nestedMessageWriter.CreateODataResourceWriter();
                    }

                    messageWriter.WriteError(
                        new ODataError {
                        ErrorCode = "NRE", Message = "Object reference not set to an instance of an object."
                    },
                        /*includeDebugInformation*/ true);
                }
            }));

            this.syncStream.Position = 0;
            var syncResult = await new StreamReader(this.syncStream).ReadToEndAsync();

            var expected = @"HTTP/1.1 200 OK
OData-Version: 4.0
Content-Type: application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8

";

            Assert.Equal(Strings.ODataAsyncWriter_CannotWriteInStreamErrorForAsync, asyncException.Message);
            Assert.Equal(Strings.ODataAsyncWriter_CannotWriteInStreamErrorForAsync, syncException.Message);
            Assert.Equal(expected, asyncResult);
            Assert.Equal(expected, syncResult);
        }
Example #14
0
        /// <summary>
        /// Writes the error with fallback logic for XML cases where the writer is in an error state and a new writer must be created.
        /// </summary>
        /// <param name="messageWriter">The message writer.</param>
        /// <param name="encoding">The encoding to use for the error if we have to fallback.</param>
        /// <param name="responseStream">The response stream to write to in the fallback case.</param>
        /// <param name="args">The args for the error.</param>
        /// <param name="error">The error to write.</param>
        /// <param name="messageWriterBuilder">MessageWriterBuilder to use if a new ODataMessageWriter needs to be constructed.</param>
        private static void WriteErrorWithFallbackForXml(ODataMessageWriter messageWriter, Encoding encoding, Stream responseStream, HandleExceptionArgs args, ODataError error, MessageWriterBuilder messageWriterBuilder)
        {
            Debug.Assert(args != null, "args != null");
#if DEBUG
            Debug.Assert(args.ProcessExceptionWasCalled, "ProcessException was not called by the time we tried to serialze this error message with ODataLib.");
#endif

            if (messageWriter != null)
            {
                try
                {
                    // If the XmlWriter inside the ODataMessageWriter had entered Error state, ODataMessageWriter.WriteError would throw an InvalidOperationException
                    // when we try to write to it. Note that XmlWriter doesn't always throw an XmlException when it enters Error state.
                    // The right thing to do is we don't write any more because at this point we don't know what's been written to the underlying
                    // stream. However we still should flush the writer to make sure that all the content that was written but is sitting in the buffers actually appears 
                    // in the stream before writing the instream error. Otherwise the buffer will be flushed when disposing the writer later and we would end up with
                    // either content written after the instream error (this would also result in having the Xml declaration in the middle of the payload -
                    // [Astoria-ODataLib-Integration] In-stream errors due to XmlExceptions are written out backwards (error before partial valid payload)) or, 
                    // hypothetically, the instream error in the middle of the other content that was already partially written. For example we can end up with a payload that 
                    // looks like <element attr="val<m:error... The XmlReader would not be able to parse the error payload in this case. Disposing the writer will flush the buffer. 
                    // It is fine to do it since the writer is not usable at this point anyways. Also note that the writer will be disposed more than once (e.g. in finally block
                    // in ResponseBodySerializer) but only the first call will have any effect.
                    // However since in the versions we shipped we always create a new XmlWriter to serialize the error payload when the existing
                    // one is in error state, we will continue to do the same to avoid introducing any breaking change here.
                    messageWriter.WriteError(error, args.UseVerboseErrors);
                }
                catch (ODataException e)
                {
                    // Yikes, ODataLib threw while writing the error. This tends to happen if the service author did something invalid during custom
                    // error handling, such as add an custom instance annotation to the error payload. In this dire case, we treat it almost like 
                    // an in-stream error, and abort the previous writing. We write out the new error. Note that this will produce an invalid payload like
                    // the situation noted above with XmlWriter errors.
                    WebUtil.Dispose(messageWriter);
                    messageWriterBuilder.SetMessageForErrorInError();
                    var newErrorWriter = messageWriterBuilder.CreateWriter();
                    ODataError errorWhileWritingOtherError = new ODataError()
                    {
                        ErrorCode = "500",
                        InnerError = new ODataInnerError(e),
                        Message = Strings.ErrorHandler_ErrorWhileWritingError
                    };

                    newErrorWriter.WriteError(errorWhileWritingOtherError, args.UseVerboseErrors);
                }
                catch (InvalidOperationException)
                {
                    Debug.Assert(ContentTypeUtil.IsNotJson(args.ResponseContentType), "Should never get here for JSON responses");
                    WebUtil.Dispose(messageWriter);

                    // if either an InvalidOperationException was encountered (see comment above) or the message writer was null, write the error out manually.
                    Debug.Assert(responseStream != null, "responseStream != null");
                    using (XmlWriter xmlWriter = XmlWriter.Create(responseStream, XmlUtil.CreateXmlWriterSettings(encoding)))
                    {
                        ErrorUtils.WriteXmlError(xmlWriter, error, args.UseVerboseErrors, MaxInnerErrorDepth);
                    }
                }
            }
        }
        public void ShouldBeAbleToWriteCustomInstanceAnnotationToErrorInJsonLight()
        {
            const string expectedPayload =
            "{" +
                "\"error\":{" +
                    "\"code\":\"400\"," +
                    "\"message\":\"Resource not found for the segment 'Address'.\"," +
                    "\"@instance.annotation\":\"stringValue\"" +
                "}" +
            "}";

            var writerSettings = new ODataMessageWriterSettings { DisableMessageStreamDisposal = true };
            writerSettings.SetContentType(ODataFormat.Json);
            writerSettings.ODataUri = new ODataUri() { ServiceRoot = new Uri("http://www.example.com") };

            MemoryStream stream = new MemoryStream();
            IODataResponseMessage messageToWrite = new InMemoryMessage { StatusCode = 400, Stream = stream };

            // Write payload
            using (var messageWriter = new ODataMessageWriter(messageToWrite, writerSettings, Model))
            {
                ODataError error = new ODataError { ErrorCode = "400", Message = "Resource not found for the segment 'Address'." };
                error.InstanceAnnotations.Add(new ODataInstanceAnnotation("instance.annotation", new ODataPrimitiveValue("stringValue")));
                messageWriter.WriteError(error, includeDebugInformation: true);
            }

            stream.Position = 0;
            string payload = (new StreamReader(stream)).ReadToEnd();
            payload.Should().Be(expectedPayload);
        }