/// <summary> /// Writes to response. /// Response headers are customizable by implementing IHasOptions an returning Dictionary of Http headers. /// </summary> /// <param name="response">The response.</param> /// <param name="result">Whether or not it was implicity handled by ServiceStack's built-in handlers.</param> /// <param name="defaultAction">The default action.</param> /// <param name="request">The serialization context.</param> /// <param name="bodyPrefix">Add prefix to response body if any</param> /// <param name="bodySuffix">Add suffix to response body if any</param> /// <returns></returns> public static Task <bool> WriteToResponse(this IResponse response, object result, ResponseSerializerDelegate defaultAction, IRequest request, byte[] bodyPrefix, byte[] bodySuffix) { using (Profiler.Current.Step("Writing to Response")) { var defaultContentType = request.ResponseContentType; try { if (result == null) { response.EndRequestWithNoContent(); return(TrueTask); } ApplyGlobalResponseHeaders(response); var httpResult = result as IHttpResult; if (httpResult != null) { if (httpResult.RequestContext == null) { httpResult.RequestContext = request; } var paddingLength = bodyPrefix != null ? bodyPrefix.Length : 0; if (bodySuffix != null) { paddingLength += bodySuffix.Length; } httpResult.PaddingLength = paddingLength; var httpError = httpResult as IHttpError; if (httpError != null) { response.Dto = httpError.CreateErrorResponse(); if (response.HandleCustomErrorHandler(request, defaultContentType, httpError.Status, response.Dto)) { return(TrueTask); } } response.Dto = response.Dto ?? httpResult.GetDto(); response.StatusCode = httpResult.Status; response.StatusDescription = httpResult.StatusDescription ?? httpResult.StatusCode.ToString(); if (string.IsNullOrEmpty(httpResult.ContentType)) { httpResult.ContentType = defaultContentType; } response.ContentType = httpResult.ContentType; } else { response.Dto = result; } /* Mono Error: Exception: Method not found: 'System.Web.HttpResponse.get_Headers' */ var responseOptions = result as IHasOptions; if (responseOptions != null) { //Reserving options with keys in the format 'xx.xxx' (No Http headers contain a '.' so its a safe restriction) const string reservedOptions = "."; foreach (var responseHeaders in responseOptions.Options) { if (responseHeaders.Key.Contains(reservedOptions)) { continue; } if (responseHeaders.Key == HttpHeaders.ContentLength) { response.SetContentLength(long.Parse(responseHeaders.Value)); continue; } Log.DebugFormat("Setting Custom HTTP Header: {0}: {1}", responseHeaders.Key, responseHeaders.Value); response.AddHeader(responseHeaders.Key, responseHeaders.Value); } } var disposableResult = result as IDisposable; if (WriteToOutputStream(response, result, bodyPrefix, bodySuffix)) { response.Flush(); //required for Compression if (disposableResult != null) { disposableResult.Dispose(); } return(TrueTask); } if (httpResult != null) { result = httpResult.Response; } //ContentType='text/html' is the default for a HttpResponse //Do not override if another has been set if (response.ContentType == null || response.ContentType == MimeTypes.Html) { response.ContentType = defaultContentType; } if (bodyPrefix != null && response.ContentType.IndexOf(MimeTypes.Json, StringComparison.InvariantCultureIgnoreCase) >= 0) { response.ContentType = MimeTypes.JavaScript; } if (HostContext.Config.AppendUtf8CharsetOnContentTypes.Contains(response.ContentType)) { response.ContentType += ContentFormat.Utf8Suffix; } var responseText = result as string; if (responseText != null) { if (bodyPrefix != null) { response.OutputStream.Write(bodyPrefix, 0, bodyPrefix.Length); } WriteTextToResponse(response, responseText, defaultContentType); if (bodySuffix != null) { response.OutputStream.Write(bodySuffix, 0, bodySuffix.Length); } return(TrueTask); } if (defaultAction == null) { throw new ArgumentNullException("defaultAction", String.Format( "As result '{0}' is not a supported responseType, a defaultAction must be supplied", (result != null ? result.GetType().GetOperationName() : ""))); } if (bodyPrefix != null) { response.OutputStream.Write(bodyPrefix, 0, bodyPrefix.Length); } if (result != null) { defaultAction(request, result, response); } if (bodySuffix != null) { response.OutputStream.Write(bodySuffix, 0, bodySuffix.Length); } if (disposableResult != null) { disposableResult.Dispose(); } return(FalseTask); } catch (Exception originalEx) { HostContext.RaiseUncaughtException(request, response, request.OperationName, originalEx); if (!HostContext.Config.WriteErrorsToResponse) { return(originalEx.AsTaskException <bool>()); } var errorMessage = String.Format( "Error occured while Processing Request: [{0}] {1}", originalEx.GetType().GetOperationName(), originalEx.Message); try { if (!response.IsClosed) { response.WriteErrorToResponse( request, defaultContentType, request.OperationName, errorMessage, originalEx, (int)HttpStatusCode.InternalServerError); } } catch (Exception writeErrorEx) { //Exception in writing to response should not hide the original exception Log.Info("Failed to write error to response: {0}", writeErrorEx); return(originalEx.AsTaskException <bool>()); } return(TrueTask); } finally { response.EndRequest(skipHeaders: true); } } }