Example #1
0
        RazorViewPage GetViewInstance(ViewResult viewResult, ViewRenderingContext context)
        {
            var view = Activator.CreateInstance(viewResult.ViewType) as RazorViewPage;

            this.HydrateView(viewResult, view, context);

            return(view);
        }
        public async Task Render(ViewResult viewResult, PipelineContext context, ViewRenderingContext viewRenderingContext)
        {
            var razorPage = this.GetViewInstance(viewResult, context);

            var viewBufferScope = this.container.Resolve <IViewBufferScope>();

            var bodyWriter = await this.RenderPageAsync(viewBufferScope, razorPage, viewRenderingContext);

            await this.RenderLayoutAsync(viewBufferScope, razorPage, viewResult, viewRenderingContext, bodyWriter);
        }
Example #3
0
        public override StringBuilder Render(ViewResult viewResult, ViewRenderingContext context)
        {
            var view = this.GetViewInstance(viewResult, context);

            view.ExecuteView(null, null);

            var body            = view.Body;
            var sectionContents = view.SectionContents;

            // null means no override, string.Empty means ensure there is no layout
            if (viewResult.MasterPageOverride != null)
            {
                view.Layout = viewResult.MasterPageOverride;
            }

            var root = string.IsNullOrWhiteSpace(view.Layout);

            // render the initial view, and move "up" through its parent layouts
            while (!root)
            {
                Type masterType;
                try
                {
                    masterType = this.viewResolver.Resolve(view.Layout);
                }
                catch (Exception ex)
                {
                    throw new Exception($"Unable to resolve layout for view: {view}", ex);
                }

                var masterResult = new ViewResult(masterType, viewResult.AreaName, viewResult.ViewData, viewResult.StatusCode);
                view = this.GetViewInstance(masterResult, context);

                view.ExecuteView(body, sectionContents);

                body            = view.Body;
                sectionContents = view.SectionContents;

                root = !view.HasLayout;
            }

            return(new StringBuilder(body));
        }
        public override void Execute(PipelineContext context, ViewResult result)
        {
            StringBuilder rendered;

            var renderer = this.viewRendererFinder.FindViewRenderer(result.ViewType);

            var renderingContext = new ViewRenderingContext
            {
                RequestUrl   = context.Request.Url,
                RequestState = context.Request.State,
                ContextItems = context.Items
            };

            try
            {
                rendered = renderer.Render(result, renderingContext);
            }
            catch (Exception e)
            {
                log.Error(e, "Error rendering view `{ViewName}`", result.ViewType.FullName);
#if DEBUG
                if (Debugger.IsAttached)
                {
                    Debugger.Break();
                }
#endif

                // TODO: render something useful
                context.Response.ContentType = "text/plain";
                context.Response.StatusCode  = HttpStatusCode.InternalServerError;

                return;
            }

            context.Response.SetCacheHeaders(result);
            context.Response.StatusCode = result.StatusCode;
            context.Response.Write(rendered.ToString());
            context.Response.ContentType = "text/html";

            SetLinkHeaders(context, result);
            SetTempData(context, result.TempData);
        }
        public override async Task Execute(PipelineContext context, ViewResult result)
        {
            var rendered = new StringBuilder();

            var renderer = this.viewRendererFinder.FindViewRenderer(result.ViewType);

            var renderingContext = new ViewRenderingContext
            {
                Writer = new StringWriter(rendered)
            };

            try
            {
                await renderer.Render(result, context, renderingContext);
            }
            catch (Exception e)
            {
                log.Error(e, "Error rendering view `{ViewName}`", result.ViewType.FullName);
#if DEBUG
                if (Debugger.IsAttached)
                {
                    Debugger.Break();
                }
#endif

                // TODO: render something useful
                context.Response.StatusCode = HttpStatusCode.InternalServerError;
                return;
            }

            context.Response.SetCacheHeaders(result);
            context.Response.StatusCode  = result.StatusCode;
            context.Response.ContentType = "text/html";

            SetLinkHeaders(context, result);
            this.tempDataMechanism.SetTempData(context, result.TempData);

            await context.Response.Write(rendered.ToString());
        }
        async Task RenderLayoutAsync(IViewBufferScope bufferScope, IRazorPage previousPage, ViewResult viewResult, ViewRenderingContext viewRenderingContext, ViewBufferTextWriter bodyWriter)
        {
            // A layout page can specify another layout page. We'll need to continue
            // looking for layout pages until they're no longer specified.
            var renderedLayouts = new List <IRazorPage>();

            // This loop will execute Layout pages from the inside to the outside. With each
            // iteration, bodyWriter is replaced with the aggregate of all the "body" content
            // (including the layout page we just rendered).
            while (!string.IsNullOrEmpty(previousPage.Layout))
            {
                if (!bodyWriter.IsBuffering)
                {
                    // Once a call to RazorPage.FlushAsync is made, we can no longer render Layout pages - content has
                    // already been written to the client and the layout content would be appended rather than surround
                    // the body content. Throwing this exception wouldn't return a 500 (since content has already been
                    // written), but a diagnostic component should be able to capture it.

                    var message = "Resources.FormatLayoutCannotBeRendered(Path, nameof(New.RazorPage.FlushAsync))";
                    throw new InvalidOperationException(message);
                }

                var layoutPage = this.GetLayoutPage(viewResult, previousPage.PipelineContext, previousPage.Layout);

                if (renderedLayouts.Count > 0 && renderedLayouts.Any(l => l.GetType() == layoutPage.GetType()))
                {
                    // If the layout has been previously rendered as part of this view, we're potentially in a layout
                    // rendering cycle.
                    throw new InvalidOperationException("Layout has circular reference. Previous Page: `" + previousPage.GetType() + "`, Current Layout: `" + layoutPage.GetType() + "`");
                }

                // Notify the previous page that any writes that are performed on it are part of sections being written
                // in the layout.
                previousPage.IsLayoutBeingRendered = true;
                layoutPage.PreviousSectionWriters  = previousPage.SectionWriters;
                layoutPage.BodyContent             = bodyWriter.Buffer;
                bodyWriter = await this.RenderPageAsync(bufferScope, layoutPage, viewRenderingContext);

                renderedLayouts.Add(layoutPage);
                previousPage = layoutPage;
            }

            // Now we've reached and rendered the outer-most layout page. Nothing left to execute.

            // Ensure all defined sections were rendered or RenderBody was invoked for page without defined sections.
            foreach (var layoutPage in renderedLayouts)
            {
                layoutPage.EnsureRenderedBodyOrSections();
            }

            if (bodyWriter.IsBuffering)
            {
                // If IsBuffering - then we've got a bunch of content in the view buffer. How to best deal with it
                // really depends on whether or not we're writing directly to the output or if we're writing to
                // another buffer.
                var viewBufferTextWriter = viewRenderingContext.Writer as ViewBufferTextWriter;
                if (viewBufferTextWriter == null || !viewBufferTextWriter.IsBuffering)
                {
                    // This means we're writing to a 'real' writer, probably to the actual output stream.
                    // We're using PagedBufferedTextWriter here to 'smooth' synchronous writes of IHtmlContent values.
                    using (var writer = bufferScope.CreateWriter(viewRenderingContext.Writer))
                        await bodyWriter.Buffer.WriteToAsync(writer, this.htmlEncoder);
                }
                else
                {
                    // This means we're writing to another buffer. Use MoveTo to combine them.
                    bodyWriter.Buffer.MoveTo(viewBufferTextWriter.Buffer);
                }
            }
        }
        async Task <ViewBufferTextWriter> RenderPageAsync(IViewBufferScope bufferScope, IRazorPage page, ViewRenderingContext viewRenderingContext)
        {
            var writer = viewRenderingContext.Writer as ViewBufferTextWriter;

            if (writer == null)
            {
                // If we get here, this is likely the top-level page (not a partial) - this means
                // that context.Writer is wrapping the output stream. We need to buffer, so create a buffered writer.
                var buffer = new ViewBuffer(bufferScope, page.GetType().AssemblyQualifiedName, ViewBuffer.ViewPageSize);
                writer = new ViewBufferTextWriter(buffer, viewRenderingContext.Writer.Encoding, this.htmlEncoder, viewRenderingContext.Writer);
            }
            else
            {
                // This means we're writing something like a partial, where the output needs to be buffered.
                // Create a new buffer, but without the ability to flush.
                var buffer = new ViewBuffer(bufferScope, page.GetType().AssemblyQualifiedName, ViewBuffer.ViewPageSize);
                writer = new ViewBufferTextWriter(buffer, viewRenderingContext.Writer.Encoding);
            }

            // The writer for the body is passed through the ViewContext, allowing things like HtmlHelpers
            // and ViewComponents to reference it.
            var oldWriter = viewRenderingContext.Writer;

            //var oldFilePath = context.ExecutingFilePath;

            viewRenderingContext.Writer = writer;
            //context.ExecutingFilePath = page.Path;

            try
            {
                page.RenderingContext = viewRenderingContext;
                await page.ExecuteAsync();

                return(writer);
            }
            finally
            {
                viewRenderingContext.Writer = oldWriter;
                //context.ExecutingFilePath = oldFilePath;
            }
        }