public void CreateODataErrorFromExceptionArgsShouldCreateODataErrorWithCustomAnnotationsAndInstanceAnnotations() { DataServiceException dse = new DataServiceException(500, "500", "Test message", "en-US", null); HandleExceptionArgs args = new HandleExceptionArgs(dse, responseWritten:false, contentType:"application/json",verboseResponse:true); ODataError error = args.CreateODataError(); error.InstanceAnnotations.As<object>().Should().BeSameAs(args.InstanceAnnotations); }
/// <summary>Method to handle a data service exception during processing.</summary> /// <param name="args">Exception handling description.</param> void IDataServiceHost.ProcessException(HandleExceptionArgs args) { // This would typically set headers on the host. WebUtil.CheckArgumentNull(args, "args"); Debug.Assert(CommonUtil.IsCatchableExceptionType(args.Exception), "CommonUtil.IsCatchableExceptionType(args.Exception)"); this.responseStatusCode = args.ResponseStatusCode; this.responseHeaders[HttpResponseHeader.ContentType] = args.ResponseContentType; this.responseHeaders[HttpResponseHeader.Allow] = args.ResponseAllowHeader; }
/// <summary> /// Method to handle a data service exception during processing. /// </summary> /// <param name="args">Exception handling description.</param> void IDataServiceHost.ProcessException(HandleExceptionArgs args) { WebUtil.CheckArgumentNull(args, "args"); Debug.Assert(this.operationContext != null, "this.operationContext != null"); this.errorFound = true; if (!args.ResponseWritten) { ((IDataServiceHost)this).ResponseStatusCode = args.ResponseStatusCode; ((IDataServiceHost)this).ResponseContentType = args.ResponseContentType; if (args.ResponseAllowHeader != null) { this.operationContext.OutgoingResponse.Headers[HttpResponseHeader.Allow] = args.ResponseAllowHeader; } } }
/// <summary> /// Prevents a default instance of the <see cref="ErrorHandler"/> class from being created. /// </summary> /// <param name="exception">The exception to be written.</param> /// <param name="verbose">if set to <c>true</c> indicates verbose errors should be written.</param> /// <param name="responseVersion">The response version.</param> /// <param name="acceptableContentTypes">The acceptable content types.</param> /// <param name="requestAcceptCharsetHeader">The request accept charset header.</param> private ErrorHandler(Exception exception, bool verbose, Version responseVersion, string acceptableContentTypes, string requestAcceptCharsetHeader) { this.contentType = GetErrorResponseContentType(acceptableContentTypes, responseVersion); this.encoding = GetEncodingForError(requestAcceptCharsetHeader); // [GQL Failure, ODataLib integration] Server does not write charset in content-type header on error responses // [GQL Failure, Astoria-ODataLib Integration] ContentType provided to DataService.HandleException does not match final header value in $batch // With the integration of ODataLib into the server-side , batch-parts which have top level error messages in them // have the charset header value appended to the content-type of the response. // To retain a consistent behavior between top level error messages in the batch and non-batch cases, we will append // the charset header to the response content type before passing it to the HandleException method on the IDataServiceHost. this.contentTypeWithCharset = string.Concat(this.contentType, ";", XmlConstants.HttpCharsetParameter, "=", this.encoding.WebName); this.exceptionArgs = new HandleExceptionArgs(exception, false, this.contentTypeWithCharset, verbose); Debug.Assert(responseVersion != null, "responseVersion != null"); this.responseVersion = responseVersion; }
/// <summary> /// Handles an exception that occurred while writing a response. /// </summary> /// <param name="service">Data service doing the processing.</param> /// <param name="exception">The exception that was thrown.</param> /// <param name="responseMessage">The response message.</param> /// <param name="messageWriter">The message writer, if null this will fall back to writing a raw XML error to the stream.</param> /// <param name="encoding">The encoding to while writing the error.</param> /// <param name="responseStream">The response stream to write the error to.</param> /// <param name="messageWriterBuilder">MessageWriterBuilder to use in case a new ODataMessageWriter needs to be constructed.</param> internal static void HandleExceptionWhileWriting(IDataService service, Exception exception, IODataResponseMessage responseMessage, ODataMessageWriter messageWriter, Encoding encoding, Stream responseStream, MessageWriterBuilder messageWriterBuilder) { Debug.Assert(service != null, "service != null"); Debug.Assert(service.Configuration != null, "service.Configuration != null"); Debug.Assert(exception != null, "exception != null"); Debug.Assert(CommonUtil.IsCatchableExceptionType(exception), "CommonUtil.IsCatchableExceptionType(exception)"); Debug.Assert(responseMessage != null, "responseMessage != null"); string contentType = responseMessage.GetHeader(XmlConstants.HttpContentType); HandleExceptionArgs args = new HandleExceptionArgs(exception, true, contentType, service.Configuration.UseVerboseErrors); service.InternalHandleException(args); service.OperationContext.RequestMessage.ProcessException(args); ODataError error = args.CreateODataError(); WriteErrorWithFallbackForXml(messageWriter, encoding, responseStream, args, error, messageWriterBuilder); }
/// <summary>Method to handle a data service exception during processing.</summary> /// <param name="args">Exception handling description.</param> public void ProcessException(HandleExceptionArgs args) { processExceptionCalled = true; if (!args.ResponseWritten) { this.ResponseStatusCode = args.ResponseStatusCode; this.ResponseContentType = args.ResponseContentType; throw new Exception(args.Exception.Message); } }
/// <summary>Method to handle a data service exception during processing.</summary> /// <param name="args">Exception handling description.</param> internal void ProcessException(HandleExceptionArgs args) { this.host.ProcessException(args); }
public void ProcessException(HandleExceptionArgs args) { if (this.ProcessExceptionCallBack != null) { this.ProcessExceptionCallBack(args); } else { throw new NotImplementedException(); } }
public void ProcessException(HandleExceptionArgs args) { throw new NotImplementedException(); }
public void InternalHandleException(HandleExceptionArgs args) { }
/// <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 ProcessException(HandleExceptionArgs args) { this.plainException = args.Exception; DataServiceException dse = args.Exception as DataServiceException; if (dse != null) { if (dse.StatusCode == (int)HttpStatusCode.NotModified) { // 304 is not a failure, we let the server handle it. return; } } if (AllowServerToSerializeException.Value) { this.ResponseStatusCode = args.ResponseStatusCode; this.ResponseContentType = args.ResponseContentType; } else { // set the right status code and content type this.ResponseStatusCode = args.ResponseStatusCode; this.ResponseContentType = "text/plain"; // write the error message in the payload StreamWriter writer = new StreamWriter(this.responseStream); writer.WriteLine("TestServiceHost.ProcessException special pre-writing handling:"); writer.Write(args.Exception.Message); writer.Flush(); // Re-throw the exception. This makes things consistent for tests, // which except an exception from HttpWebRequest.StatusCode <> 200 as well. throw new WebException("WebException from TestServiceHost.ProcessException", args.Exception); } }