예제 #1
0
        public async Task Write_WritesCharValues_ToUnderlyingStream_WhenNotBuffering()
        {
            // Arrange
            var inner = new Mock <TextWriter> {
                CallBase = true
            };
            var buffer  = new ViewBuffer(new TestViewBufferScope(), "some-name", pageSize: 4);
            var writer  = new ViewBufferTextWriter(buffer, Encoding.UTF8, new HtmlTestEncoder(), inner.Object);
            var buffer1 = new[] { 'a', 'b', 'c', 'd' };
            var buffer2 = new[] { 'd', 'e', 'f' };

            // Act
            writer.Flush();
            writer.Write('x');
            writer.Write(buffer1, 1, 2);
            writer.Write(buffer2);
            await writer.WriteAsync(buffer2, 1, 1);

            await writer.WriteLineAsync(buffer1);

            // Assert
            inner.Verify(v => v.Write('x'), Times.Once());
            inner.Verify(v => v.Write(buffer1, 1, 2), Times.Once());
            inner.Verify(v => v.Write(buffer1, 0, 4), Times.Once());
            inner.Verify(v => v.Write(buffer2, 0, 3), Times.Once());
            inner.Verify(v => v.WriteAsync(buffer2, 1, 1), Times.Once());
            inner.Verify(v => v.WriteLine(), Times.Once());
        }
예제 #2
0
        public void Write_WritesDataTypes_ToUnderlyingStream_WhenNotBuffering()
        {
            // Arrange
            var expected  = new[] { "True", "3", "18446744073709551615", "Hello world", "3.14", "2.718" };
            var inner     = new Mock <TextWriter>();
            var buffer    = new ViewBuffer(new TestViewBufferScope(), "some-name", pageSize: 4);
            var writer    = new ViewBufferTextWriter(buffer, Encoding.UTF8, new HtmlTestEncoder(), inner.Object);
            var testClass = new TestClass();

            // Act
            writer.Flush();
            writer.Write(true);
            writer.Write(3);
            writer.Write(ulong.MaxValue);
            writer.Write(testClass);
            writer.Write(3.14);
            writer.Write(2.718m);

            // Assert
            Assert.Equal(0, buffer.Count);
            foreach (var item in expected)
            {
                inner.Verify(v => v.Write(item), Times.Once());
            }
        }
예제 #3
0
        public async Task WriteLines_WritesCharBuffer()
        {
            // Arrange
            var newLine = Environment.NewLine;
            var buffer  = new ViewBuffer(new TestViewBufferScope(), "some-name", pageSize: 4);
            var writer  = new ViewBufferTextWriter(buffer, Encoding.UTF8);

            // Act
            writer.WriteLine();
            await writer.WriteLineAsync();

            // Assert
            var actual = GetValues(buffer);

            Assert.Equal <object>(new[] { newLine, newLine }, actual);
        }
예제 #4
0
        public void Write_HtmlContent_AfterFlush_GoesToStream()
        {
            // Arrange
            var inner  = new StringWriter();
            var buffer = new ViewBuffer(new TestViewBufferScope(), "some-name", pageSize: 4);
            var writer = new ViewBufferTextWriter(buffer, Encoding.UTF8, new HtmlTestEncoder(), inner);

            writer.Flush();

            var content = new HtmlString("Hello, world!");

            // Act
            writer.Write(content);

            // Assert
            Assert.Equal("Hello, world!", inner.ToString());
        }
예제 #5
0
        public void WriteLine_WritesDataTypes_ToBuffer()
        {
            // Arrange
            var newLine  = Environment.NewLine;
            var expected = new List <object> {
                "False", newLine, "1.1", newLine, "3", newLine
            };
            var buffer = new ViewBuffer(new TestViewBufferScope(), "some-name", pageSize: 4);
            var writer = new ViewBufferTextWriter(buffer, Encoding.UTF8);

            // Act
            writer.WriteLine(false);
            writer.WriteLine(1.1f);
            writer.WriteLine(3L);

            // Assert
            Assert.Equal(expected, GetValues(buffer));
        }
예제 #6
0
        public void Write_WritesDataTypes_ToBuffer()
        {
            // Arrange
            var expected = new object[] { "True", "3", "18446744073709551615", "Hello world", "3.14", "2.718", "m" };
            var buffer   = new ViewBuffer(new TestViewBufferScope(), "some-name", pageSize: 4);
            var writer   = new ViewBufferTextWriter(buffer, Encoding.UTF8);

            // Act
            writer.Write(true);
            writer.Write(3);
            writer.Write(ulong.MaxValue);
            writer.Write(new TestClass());
            writer.Write(3.14);
            writer.Write(2.718m);
            writer.Write('m');

            // Assert
            Assert.Equal(expected, GetValues(buffer));
        }
예제 #7
0
        public void WriteLine_WritesDataTypes_ToUnbufferedStream_WhenNotBuffering()
        {
            // Arrange
            var inner  = new Mock <TextWriter>();
            var buffer = new ViewBuffer(new TestViewBufferScope(), "some-name", pageSize: 4);
            var writer = new ViewBufferTextWriter(buffer, Encoding.UTF8, new HtmlTestEncoder(), inner.Object);

            // Act
            writer.Flush();
            writer.WriteLine(false);
            writer.WriteLine(1.1f);
            writer.WriteLine(3L);

            // Assert
            inner.Verify(v => v.Write("False"), Times.Once());
            inner.Verify(v => v.Write("1.1"), Times.Once());
            inner.Verify(v => v.Write("3"), Times.Once());
            inner.Verify(v => v.WriteLine(), Times.Exactly(3));
        }
예제 #8
0
        public IHtmlContent Render()
        {
            var defaultActions = GetDefaultActions();
            var modeViewPath   = _readOnly ? DisplayTemplateViewPath : EditorTemplateViewPath;

            foreach (string viewName in GetViewNames())
            {
                var viewEngineResult = _viewEngine.GetView(_viewContext.ExecutingFilePath, viewName, isMainPage: false);
                if (!viewEngineResult.Success)
                {
                    var fullViewName = modeViewPath + "/" + viewName;
                    viewEngineResult = _viewEngine.FindView(_viewContext, fullViewName, isMainPage: false);
                }

                if (viewEngineResult.Success)
                {
                    var viewBuffer = new ViewBuffer(_bufferScope, viewName, ViewBuffer.PartialViewPageSize);
                    using (var writer = new ViewBufferTextWriter(viewBuffer, _viewContext.Writer.Encoding))
                    {
                        // Forcing synchronous behavior so users don't have to await templates.
                        var view = viewEngineResult.View;
                        using (view as IDisposable)
                        {
                            var viewContext = new ViewContext(_viewContext, viewEngineResult.View, _viewData, writer);
                            var renderTask  = viewEngineResult.View.RenderAsync(viewContext);
                            renderTask.GetAwaiter().GetResult();
                            return(viewBuffer);
                        }
                    }
                }

                Func <IHtmlHelper, IHtmlContent> defaultAction;
                if (defaultActions.TryGetValue(viewName, out defaultAction))
                {
                    return(defaultAction(MakeHtmlHelper(_viewContext, _viewData)));
                }
            }

            throw new InvalidOperationException(
                      Resources.FormatTemplateHelpers_NoTemplate(_viewData.ModelExplorer.ModelType.FullName));
        }
예제 #9
0
        public async Task Write_WritesStringValues_ToUnbufferedStream_WhenNotBuffering()
        {
            // Arrange
            var inner  = new Mock <TextWriter>();
            var buffer = new ViewBuffer(new TestViewBufferScope(), "some-name", pageSize: 4);
            var writer = new ViewBufferTextWriter(buffer, Encoding.UTF8, new HtmlTestEncoder(), inner.Object);

            // Act
            await writer.FlushAsync();

            writer.Write("a");
            writer.WriteLine("ab");
            await writer.WriteAsync("ef");

            await writer.WriteLineAsync("gh");

            // Assert
            inner.Verify(v => v.Write("a"), Times.Once());
            inner.Verify(v => v.WriteLine("ab"), Times.Once());
            inner.Verify(v => v.WriteAsync("ef"), Times.Once());
            inner.Verify(v => v.WriteLineAsync("gh"), Times.Once());
        }
예제 #10
0
        public async Task Write_WritesStringBuffer()
        {
            // Arrange
            var newLine = Environment.NewLine;
            var input1  = "Hello";
            var input2  = "from";
            var input3  = "ASP";
            var input4  = ".Net";
            var buffer  = new ViewBuffer(new TestViewBufferScope(), "some-name", pageSize: 4);
            var writer  = new ViewBufferTextWriter(buffer, Encoding.UTF8);

            // Act
            writer.Write(input1);
            writer.WriteLine(input2);
            await writer.WriteAsync(input3);

            await writer.WriteLineAsync(input4);

            // Assert
            var actual = GetValues(buffer);

            Assert.Equal <object>(new[] { input1, input2, newLine, input3, input4, newLine }, actual);
        }
예제 #11
0
        private async Task RenderLayoutAsync(
            ViewContext context,
            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 previousPage = RazorPage;
            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(Razor.RazorPage.FlushAsync));
                    throw new InvalidOperationException(message);
                }

                var layoutPage = GetLayoutPage(context, previousPage.Path, previousPage.Layout);

                if (renderedLayouts.Count > 0 &&
                    renderedLayouts.Any(l => string.Equals(l.Path, layoutPage.Path, StringComparison.Ordinal)))
                {
                    // If the layout has been previously rendered as part of this view, we're potentially in a layout
                    // rendering cycle.
                    throw new InvalidOperationException(
                        Resources.FormatLayoutHasCircularReference(previousPage.Path, layoutPage.Path));
                }

                // 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 RenderPageAsync(layoutPage, context, invokeViewStarts: false);

                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 = context.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(context.Writer))
                    {
                        await bodyWriter.Buffer.WriteToAsync(writer, _htmlEncoder);
                    }
                }
                else
                {
                    // This means we're writing to another buffer. Use MoveTo to combine them.
                    bodyWriter.Buffer.MoveTo(viewBufferTextWriter.Buffer);
                    return;
                }
            }
        }
예제 #12
0
        private async Task<ViewBufferTextWriter> RenderPageAsync(
            IRazorPage page,
            ViewContext context,
            bool invokeViewStarts)
        {
            var writer = context.Writer as ViewBufferTextWriter;
            if (writer == null)
            {
                Debug.Assert(_bufferScope != 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.Path, ViewBuffer.ViewPageSize);
                writer = new ViewBufferTextWriter(buffer, context.Writer.Encoding, _htmlEncoder, context.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.Path, ViewBuffer.ViewPageSize);
                writer = new ViewBufferTextWriter(buffer, context.Writer.Encoding);
            }

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

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

            try
            {
                if (invokeViewStarts)
                {
                    // Execute view starts using the same context + writer as the page to render.
                    await RenderViewStartsAsync(context);
                }

                await RenderPageCoreAsync(page, context);
                return writer;
            }
            finally
            {
                context.Writer = oldWriter;
                context.ExecutingFilePath = oldFilePath;
            }
        }
        private async Task<IHtmlContent> RenderRazorViewAsync(string path, DisplayContext context)
        {
            var viewEngineResult = _viewEngine.Value.ViewEngines.First().FindView(context.ViewContext, path, isMainPage: false);
            if (viewEngineResult.Success)
            {
                var bufferScope = context.ViewContext.HttpContext.RequestServices.GetRequiredService<IViewBufferScope>();
                var viewBuffer = new ViewBuffer(bufferScope, viewEngineResult.ViewName, ViewBuffer.PartialViewPageSize);
                using (var writer = new ViewBufferTextWriter(viewBuffer, context.ViewContext.Writer.Encoding))
                {
                    // Forcing synchronous behavior so users don't have to await templates.
                    var view = viewEngineResult.View;
                    using (view as IDisposable)
                    {
                        var viewContext = new ViewContext(context.ViewContext, viewEngineResult.View, context.ViewContext.ViewData, writer);
                        await viewEngineResult.View.RenderAsync(viewContext);
                        return viewBuffer;
                    }
                }
            }

            return null;
        }
예제 #14
0
        public IHtmlContent Render()
        {
            var defaultActions = GetDefaultActions();
            var modeViewPath = _readOnly ? DisplayTemplateViewPath : EditorTemplateViewPath;

            foreach (string viewName in GetViewNames())
            {
                var viewEngineResult = _viewEngine.GetView(_viewContext.ExecutingFilePath, viewName, isMainPage: false);
                if (!viewEngineResult.Success)
                {
                    var fullViewName = modeViewPath + "/" + viewName;
                    viewEngineResult = _viewEngine.FindView(_viewContext, fullViewName, isMainPage: false);
                }

                if (viewEngineResult.Success)
                {
                    var viewBuffer = new ViewBuffer(_bufferScope, viewName, ViewBuffer.PartialViewPageSize);
                    using (var writer = new ViewBufferTextWriter(viewBuffer, _viewContext.Writer.Encoding))
                    {
                        // Forcing synchronous behavior so users don't have to await templates.
                        var view = viewEngineResult.View;
                        using (view as IDisposable)
                        {
                            var viewContext = new ViewContext(_viewContext, viewEngineResult.View, _viewData, writer);
                            var renderTask = viewEngineResult.View.RenderAsync(viewContext);
                            renderTask.GetAwaiter().GetResult();
                            return viewBuffer;
                        }
                    }
                }

                Func<IHtmlHelper, IHtmlContent> defaultAction;
                if (defaultActions.TryGetValue(viewName, out defaultAction))
                {
                    return defaultAction(MakeHtmlHelper(_viewContext, _viewData));
                }
            }

            throw new InvalidOperationException(
                Resources.FormatTemplateHelpers_NoTemplate(_viewData.ModelExplorer.ModelType.FullName));
        }
예제 #15
0
        public async Task Write_WithHtmlString_WritesValueWithoutEncoding()
        {
            // Arrange
            var buffer = new ViewBuffer(new TestViewBufferScope(), string.Empty, pageSize: 32);
            var writer = new ViewBufferTextWriter(buffer, Encoding.UTF8);

            var page = CreatePage(p =>
            {
                p.Write(new HtmlString("Hello world"));
            });
            page.ViewContext.Writer = writer;

            // Act
            await page.ExecuteAsync();

            // Assert
            Assert.Equal("Hello world", HtmlContentUtilities.HtmlContentToString(buffer));
        }