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()); }
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()); } }
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); }
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()); }
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)); }
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)); }
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)); }
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)); }
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()); }
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); }
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; } } }
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; }
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)); }
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)); }