using System; using System.IO; using System.Threading.Tasks; using ServiceStack.Text; using ServiceStack.Web; namespace ServiceStack.Script { public class PageFormat { public string ArgsPrefix { get; set; } = "---"; public string ArgsSuffix { get; set; } = "---"; public string Extension { get; set; } public string ContentType { get; set; } = MimeTypes.PlainText; public Func EncodeValue { get; set; } public Func ResolveLayout { get; set; } public Func OnExpressionException { get; set; } public Func OnViewException { get; set; } public PageFormat() { EncodeValue = DefaultEncodeValue; ResolveLayout = DefaultResolveLayout; OnExpressionException = DefaultExpressionException; OnViewException = DefaultViewException; } public string DefaultEncodeValue(object value) { if (value is IRawString rawString) return rawString.ToRawString(); var str = value.ToString(); if (str == string.Empty) return string.Empty; return str; } public SharpPage DefaultResolveLayout(SharpPage page) { page.Args.TryGetValue(SharpPages.Layout, out object layout); return page.Context.Pages.ResolveLayoutPage(page, layout as string); } public virtual object DefaultExpressionException(PageResult result, Exception ex) { if (result.Page.Context.RenderExpressionExceptions) return $"{ex.GetType().Name}: ${ex.Message}"; // Evaluate Null References in Binding Expressions to null if (ScriptConfig.CaptureAndEvaluateExceptionsToNull.Contains(ex.GetType())) return JsNull.Value; return null; } public virtual async Task DefaultViewException(PageResult pageResult, IRequest req, Exception ex) { var sb = StringBuilderCache.Allocate(); if (ContentType == MimeTypes.Html) sb.AppendLine("
");
            sb.AppendLine($"{ex.GetType().Name}: {ex.Message}");
            if (pageResult.Context.DebugMode) 
                sb.AppendLine(ex.StackTrace);

            if (ex.InnerException != null)
            {
                sb.AppendLine();
                sb.AppendLine("Inner Exceptions:");
                var innerEx = ex.InnerException;
                while (innerEx != null)
                {
                    sb.AppendLine($"{innerEx.GetType().Name}: {innerEx.Message}");
                    if (pageResult.Context.DebugMode) 
                        sb.AppendLine(innerEx.StackTrace);
                    innerEx = innerEx.InnerException;;
                }
            }
            if (ContentType == MimeTypes.Html)
                sb.AppendLine("
"); var html = StringBuilderCache.ReturnAndFree(sb); await req.Response.OutputStream.WriteAsync(html).ConfigAwait(); } } public class HtmlPageFormat : PageFormat { public HtmlPageFormat() { ArgsPrefix = ""; Extension = "html"; ContentType = MimeTypes.Html; EncodeValue = HtmlEncodeValue; ResolveLayout = HtmlResolveLayout; OnExpressionException = HtmlExpressionException; } public static string HtmlEncodeValue(object value) { if (value == null) return string.Empty; if (value is IHtmlString htmlString) return htmlString.ToHtmlString(); if (value is IRawString rawString) return rawString.ToRawString(); var str = value.ToString(); if (str == string.Empty) return string.Empty; return str.HtmlEncode(); } public SharpPage HtmlResolveLayout(SharpPage page) { var isCompletePage = page.BodyContents.Span.StartsWithIgnoreCase("".AsSpan()) || page.BodyContents.Span.StartsWithIgnoreCase("" + (ex.GetType().Name + ": " + ex.Message).HtmlEncode() + "").ToRawString(); // Evaluate Null References in Binding Expressions to null if (ScriptConfig.CaptureAndEvaluateExceptionsToNull.Contains(ex.GetType())) return JsNull.Value; return null; } public static async Task HtmlEncodeTransformer(Stream stream) { var contents = await stream.ReadToEndAsync().ConfigAwait(); var htmlEncoded = contents.HtmlEncode(); return MemoryStreamFactory.GetStream(htmlEncoded.ToUtf8Bytes()); } } }