/// <summary> /// Gets the view for an input document (which is different than the view for a layout, partial, or /// other indirect view because it's not necessarily on disk or in the file system). /// </summary> private IView GetViewFromStream(IServiceProvider serviceProvider, RenderRequest request, IRazorPage page) { StatiqRazorProjectFileSystem projectFileSystem = serviceProvider.GetRequiredService <StatiqRazorProjectFileSystem>(); IEnumerable <string> viewStartLocations = request.ViewStartLocation != null ? new[] { request.ViewStartLocation } : projectFileSystem.FindHierarchicalItems(request.RelativePath, ViewStartFileName).Select(x => x.FilePath); List <IRazorPage> viewStartPages = viewStartLocations .Select(serviceProvider.GetRequiredService <IRazorPageFactoryProvider>().CreateFactory) .Where(x => x.Success) .Select(x => x.RazorPageFactory()) .Reverse() .ToList(); if (request.LayoutLocation != null) { page.Layout = request.LayoutLocation; } IRazorViewEngine viewEngine = serviceProvider.GetRequiredService <IRazorViewEngine>(); IRazorPageActivator pageActivator = serviceProvider.GetRequiredService <IRazorPageActivator>(); HtmlEncoder htmlEncoder = serviceProvider.GetRequiredService <HtmlEncoder>(); DiagnosticSource diagnosticSource = serviceProvider.GetRequiredService <DiagnosticSource>(); return(new RazorView(viewEngine, pageActivator, viewStartPages, page, htmlEncoder, diagnosticSource)); }
private ViewContext GetViewContext(IServiceProvider serviceProvider, RenderRequest request, IView view, TextWriter output) { HttpContext httpContext = new DefaultHttpContext { RequestServices = serviceProvider }; ActionContext actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor()); ViewDataDictionary viewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), actionContext.ModelState) { Model = request.Model }; ITempDataDictionary tempData = new TempDataDictionary(actionContext.HttpContext, serviceProvider.GetRequiredService <ITempDataProvider>()); return(new ViewContext( actionContext, view, viewData, tempData, output, new HtmlHelperOptions(), request.Document, request.Context)); }
/// <inheritdoc /> protected override async Task <IEnumerable <IDocument> > ExecuteContextAsync(IExecutionContext context) { // Expire the internal Razor cache if this is a new execution // This needs to be done so that layouts/partials can be re-rendered if they've changed, // otherwise Razor will just use the previously cached version of them if (_executionId != Guid.Empty && _executionId != context.ExecutionId) { RazorService.ExpireChangeTokens(); } _executionId = context.ExecutionId; // Eliminate input documents that we shouldn't process ImmutableArray <IDocument> validInputs = context.Inputs .Where(x => _ignorePrefix == null || x.Source.IsNull || !x.Source.FileName.FullPath.StartsWith(_ignorePrefix)) .ToImmutableArray(); if (validInputs.Length < context.Inputs.Length) { context.LogInformation($"Ignoring {context.Inputs.Length - validInputs.Length} inputs due to source file name prefix"); } // Compile and evaluate the pages in parallel return(await validInputs.ParallelSelectAsync(RenderDocumentAsync)); async Task <IDocument> RenderDocumentAsync(IDocument input) { context.LogDebug("Processing Razor for {0}", input.ToSafeDisplayString()); using (Stream contentStream = await context.GetContentStreamAsync()) { using (Stream inputStream = input.GetContentStream()) { NormalizedPath viewStartLocationPath = _viewStartPath == null ? null : await _viewStartPath.GetValueAsync(input, context); NormalizedPath layoutPath = _layoutPath == null ? NormalizedPath.Null : (await _layoutPath.GetValueAsync(input, context)); string layoutLocation = layoutPath.IsNull ? null : layoutPath.FullPath; RenderRequest request = new RenderRequest { Input = inputStream, Output = contentStream, BaseType = _basePageType, Context = context, Document = input, LayoutLocation = layoutLocation, ViewStartLocation = viewStartLocationPath.IsNull ? null : GetRelativePath(viewStartLocationPath, context), RelativePath = GetRelativePath(input, context), Model = _model == null ? input : await _model.GetValueAsync(input, context) }; await RazorService.RenderAsync(request); } return(input.Clone(context.GetContentProvider(contentStream, MediaTypes.Html))); } } }
public async Task RenderAsync(RenderRequest request) { CompilationParameters parameters = new CompilationParameters { BasePageType = request.BaseType, Namespaces = new NamespaceCollection(request.Context.Namespaces) }; RazorCompiler compiler = _compilers.GetOrAdd(parameters, _ => new RazorCompiler(parameters, request.Context)); await compiler.RenderPageAsync(request); }
public CompilerCacheKey(RenderRequest request, byte[] fileHash) { _request = request; _fileHash = fileHash; // Precalculate hash code since we know we'll need it _hashCode = 17; _hashCode = (_hashCode * 31) + (_request.LayoutLocation?.GetHashCode() ?? 0); _hashCode = (_hashCode * 31) + (_request.ViewStartLocation?.GetHashCode() ?? 0); foreach (byte b in _fileHash) { _hashCode = (_hashCode * 31) ^ b; } }
public async Task RenderPageAsync(RenderRequest request) { using (IServiceScope scope = _serviceScopeFactory.CreateScope()) { IServiceProvider serviceProvider = scope.ServiceProvider; IRazorPage page = GetPageFromStream(serviceProvider, request); IView view = GetViewFromStream(serviceProvider, request, page); using (StreamWriter writer = request.Output.GetWriter()) { Microsoft.AspNetCore.Mvc.Rendering.ViewContext viewContext = GetViewContext(serviceProvider, request, view, writer); await viewContext.View.RenderAsync(viewContext); } } }
/// <summary> /// Gets the Razor page for an input document stream. This is roughly modeled on /// DefaultRazorPageFactory and CompilerCache. Note that we don't actually bother /// with caching the page if it's from a live stream. /// </summary> private IRazorPage GetPageFromStream(IServiceProvider serviceProvider, RenderRequest request) { string relativePath = request.RelativePath; if (relativePath.StartsWith("~/", StringComparison.Ordinal)) { // For tilde slash paths, drop the leading ~ to make it work with the underlying IFileProvider. relativePath = relativePath.Substring(1); } // Get the file info by combining the stream content with info found at the document's original location (if any) StatiqRazorProjectFileSystem projectFileSystem = serviceProvider.GetRequiredService <StatiqRazorProjectFileSystem>(); RazorProjectItem projectItem = projectFileSystem.GetItem(relativePath, request.Input); // Compute a hash for the content since pipelines could have changed it from the underlying file // We have to pre-compute the hash (I.e., no CryptoStream) since we need to check for a hit before reading/compiling the view byte[] hash = SHA512.Create().ComputeHash(request.Input); request.Input.Position = 0; CompilationResult compilationResult = CompilePage(serviceProvider, request, hash, projectItem, projectFileSystem); return(compilationResult.GetPage(request.RelativePath)); }
private CompilationResult CompilePage(IServiceProvider serviceProvider, RenderRequest request, byte[] hash, RazorProjectItem projectItem, RazorProjectFileSystem projectFileSystem) { CompilerCacheKey cacheKey = new CompilerCacheKey(request, hash); return(_compilationCache.GetOrAdd(cacheKey, _ => GetCompilation(projectItem, projectFileSystem))); }