Exemplo n.º 1
0
        public async Task <IActionResult> Index()
        {
            var nodeServices = Request.HttpContext.RequestServices.GetRequiredService <INodeServices>();
            var hostEnv      = Request.HttpContext.RequestServices.GetRequiredService <IHostingEnvironment>();

            var applicationBasePath   = hostEnv.ContentRootPath;
            var requestFeature        = Request.HttpContext.Features.Get <IHttpRequestFeature>();
            var unencodedPathAndQuery = requestFeature.RawTarget;
            var unencodedAbsoluteUrl  = $"{Request.Scheme}://{Request.Host}{unencodedPathAndQuery}";

            // Prerender / Serialize application (with Universal)
            var prerenderResult = await Prerenderer.RenderToString(
                "/",
                nodeServices,
                new JavaScriptModuleExport(applicationBasePath + "/Client/dist/main-server"),
                unencodedAbsoluteUrl,
                unencodedPathAndQuery,
                null,
                30000,
                Request.PathBase.ToString()
                );

            ViewData["SpaHtml"] = prerenderResult.Html;
            ViewData["Title"]   = prerenderResult.Globals["title"];
            ViewData["Styles"]  = prerenderResult.Globals["styles"];
            ViewData["Meta"]    = prerenderResult.Globals["meta"];
            ViewData["Links"]   = prerenderResult.Globals["links"];

            return(View());
        }
        public static async Task <RenderToStringResult> BuildPrerender(this HttpRequest Request)
        {
            var nodeServices = Request.HttpContext.RequestServices.GetRequiredService <INodeServices>();
            var hostEnv      = Request.HttpContext.RequestServices.GetRequiredService <IHostingEnvironment>();

            var applicationBasePath   = hostEnv.ContentRootPath;
            var requestFeature        = Request.HttpContext.Features.Get <IHttpRequestFeature>();
            var unencodedPathAndQuery = requestFeature.RawTarget;
            var unencodedAbsoluteUrl  = $"{Request.Scheme}://{Request.Host}{unencodedPathAndQuery}";

            // Custom data

            //Prerender now needs CancellationToken
            System.Threading.CancellationTokenSource cancelSource = new System.Threading.CancellationTokenSource();
            System.Threading.CancellationToken       cancelToken  = cancelSource.Token;

            // Prerender / Serialize application (with Universal)
            return(await Prerenderer.RenderToString(
                       "/",
                       nodeServices,
                       cancelToken,
                       new JavaScriptModuleExport(applicationBasePath + "/ClientApp/dist-server/main.bundle"),
                       unencodedAbsoluteUrl,
                       unencodedPathAndQuery,
                       new
            {
                AppData = $"This is server side data {DateTime.Now.ToString()}"
            },           // Our simplified Request object & any other CustommData you want to send!
                       30000,
                       Request.PathBase.ToString()
                       ));
        }
Exemplo n.º 3
0
        public static async Task <RenderToStringResult> Prerender(this HttpRequest trans)
        {
            // Grab key website data that will be serialized for use in universal rendering
            INodeServices       node = trans.HttpContext.RequestServices.GetRequiredService <INodeServices>( );
            IHostingEnvironment zone = trans.HttpContext.RequestServices.GetRequiredService <IHostingEnvironment>( );
            IHttpRequestFeature item = trans.HttpContext.Features.Get <IHttpRequestFeature>( );
            // Identify web app's host and subroute to target for prerending server-side
            string root = zone.ContentRootPath;
            string path = item.RawTarget;
            // Build out the url from the previous parameters for prerendering it's content
            string url = $"{ trans.Scheme }://{ trans.Host }{ path }";
            // Allow for the passing of custom data as a request for the frontend app
            TransferData data = new TransferData( );

            // Customized data sent to the frontend is sent through here as a parameter
            // Feel free to add more custom data here through TransferData class fields
            data.elements           = trans.Decode( );
            data.thisCameFromDotNET = "The server beckons thee!!!";
            // Requires a cancellation token for performing universal app prerendering
            CancellationTokenSource origin = new CancellationTokenSource( );
            CancellationToken       exe    = origin.Token;
            // Locate the generated server-side bundle used for the initial prerendering
            JavaScriptModuleExport js = new JavaScriptModuleExport(root + "/Node/server.bundle");

            // Serialize and prerender the frontend app as a universal/isomorphic one
            return(await Prerenderer.RenderToString("/", node, exe, js, url, path, data, 30000, trans.PathBase.ToString( )));
        }
        public async Task <IActionResult> Index()
        {
            var nodeServices = Request.HttpContext.RequestServices.GetRequiredService <INodeServices>();
            var hostEnv      = Request.HttpContext.RequestServices.GetRequiredService <IHostingEnvironment>();

            var applicationBasePath   = hostEnv.ContentRootPath;
            var requestFeature        = Request.HttpContext.Features.Get <IHttpRequestFeature>();
            var unencodedPathAndQuery = requestFeature.RawTarget;
            var unencodedAbsoluteUrl  = $"{Request.Scheme}://{Request.Host}{unencodedPathAndQuery}";

            TransferData transferData = new TransferData();

            transferData.request            = AbstractHttpContextRequestInfo(Request);
            transferData.thisCameFromDotNET = "Hi Angular it's asp.net :)";

            var prerenderResult = await Prerenderer.RenderToString(
                "/", // baseURL
                nodeServices,
                new JavaScriptModuleExport(applicationBasePath + "/ClientApp/dist/server.js"),
                unencodedAbsoluteUrl,
                unencodedPathAndQuery,
                transferData, // data from angular
                30000,        // timeout duration
                Request.PathBase.ToString()
                );

            ViewData["SkiShop"]      = prerenderResult.Html;
            ViewData["Title"]        = prerenderResult.Globals["title"];
            ViewData["Styles"]       = prerenderResult.Globals["styles"];
            ViewData["Meta"]         = prerenderResult.Globals["meta"];
            ViewData["Links"]        = prerenderResult.Globals["links"];
            ViewData["TransferData"] = prerenderResult.Globals["transferData"]; // our transfer data set to window.TRANSFER_CACHE = {};

            return(View());
        }
        public static async Task <RenderToStringResult> BuildPrerender(this HttpRequest Request)
        {
            var nodeServices = Request.HttpContext.RequestServices.GetRequiredService <INodeServices>();
            var hostEnv      = Request.HttpContext.RequestServices.GetRequiredService <IHostingEnvironment>();

            var applicationBasePath   = hostEnv.ContentRootPath;
            var requestFeature        = Request.HttpContext.Features.Get <IHttpRequestFeature>();
            var unencodedPathAndQuery = requestFeature.RawTarget;
            var unencodedAbsoluteUrl  = $"{Request.Scheme}://{Request.Host}{unencodedPathAndQuery}";

            var transferData = new TransferData
            {
                request            = Request.AbstractRequestInfo(),
                thisCameFromDotNET = "Some data sent from the server"
            };

            var cancelSource = new System.Threading.CancellationTokenSource();
            var cancelToken  = cancelSource.Token;

            return(await Prerenderer.RenderToString(
                       "/",
                       nodeServices,
                       cancelToken,
                       new JavaScriptModuleExport(applicationBasePath + "/ClientApp/dist/main-server"),
                       unencodedAbsoluteUrl,
                       unencodedPathAndQuery,
                       transferData,
                       30000,
                       Request.PathBase.ToString()
                       ));
        }
Exemplo n.º 6
0
        public static async Task <RenderToStringResult> BuildPrerender(this HttpRequest request)
        {
            var nodeServices          = request.HttpContext.RequestServices.GetRequiredService <INodeServices>();
            var hostEnv               = request.HttpContext.RequestServices.GetRequiredService <IHostingEnvironment>();
            var applicationBasePath   = hostEnv.ContentRootPath;
            var requestFeature        = request.HttpContext.Features.Get <IHttpRequestFeature>();
            var unencodedPathAndQuery = requestFeature.RawTarget;
            var unencodedAbsoluteUrl  = $"{request.Scheme}://{request.Host}{unencodedPathAndQuery}";

            var transferData = new TransferData();

            transferData.request            = request.AbstractRequestInfo();
            transferData.thisCameFromDotNET = "Hi Angular it's asp.net :)";

            CancellationTokenSource cancelSource = new CancellationTokenSource();
            CancellationToken       cancelToken  = cancelSource.Token;

            return(await Prerenderer.RenderToString(
                       "/",
                       nodeServices,
                       cancelToken,
                       new JavaScriptModuleExport(applicationBasePath + "/App/dist/client-app-server"),
                       unencodedAbsoluteUrl,
                       unencodedPathAndQuery,
                       transferData, // the simplified Request object & any other CustomData
                       30000,
                       request.PathBase.ToString()
                       ));
        }
Exemplo n.º 7
0
        public async Task <PrerenderStatus> Prerender(HttpContext httpContext)
        {
            var expired = this.IsFileCacheExpired(httpContext);

            if (!expired)
            {
                return(PrerenderStatus.CacheNotExpired);
            }

            var    basePath            = env.ContentRootPath;
            var    moduleName          = "ClientApp/dist/main-server";
            string exportName          = null;
            var    customDataParameter = new { apiBaseUrl = this.appSettings.ApiBaseUrl };
            var    timeoutMilliseconds = default(int);

            var requestFeature        = httpContext.Features.Get <IHttpRequestFeature>();
            var unencodedPathAndQuery = requestFeature.RawTarget.Replace(UrlPrefix, "");

            var request = httpContext.Request;
            var unencodedAbsoluteUrl = $"{request.Scheme}://{request.Host}{unencodedPathAndQuery}".Replace(UrlPrefix, "");

            var result = await Prerenderer.RenderToString(
                basePath,
                nodeServices,
                CancellationToken.None,
                new JavaScriptModuleExport(moduleName) { ExportName = exportName },
                unencodedAbsoluteUrl,
                unencodedPathAndQuery,
                customDataParameter,
                timeoutMilliseconds,
                httpContext.Request.PathBase.ToString());

            async void WriteFile(string content, string extension)
            {
                var file = new FileInfo(this.GetCacheBaseFilePath(httpContext) + "." + extension);

                if (!file.Directory.Exists)
                {
                    file.Directory.Create();
                }
                if (string.IsNullOrEmpty(content) && file.Exists)
                {
                    file.Delete();
                }
                if (string.IsNullOrEmpty(content))
                {
                    return;
                }
                await File.WriteAllTextAsync(file.FullName, content);
            }

            var html = result.Html;

            WriteFile(html, "html");
            var script = result.CreateGlobalsAssignmentScript();

            WriteFile(script, "js");

            return(PrerenderStatus.Prerendered);
        }
Exemplo n.º 8
0
        public async Task <ActionResult> Index()
        {
            string unencodedPathAndQuery = Request.RawUrl;
            string unencodedAbsoluteUrl  = "http://localhost" + unencodedPathAndQuery;

            TransferData transferData = new TransferData();

            transferData.request            = AbstractHttpContextRequestInfo();
            transferData.thisCameFromDotNET = "Hi Angular it's asp.net :)";

            var prerenderResult = await Prerenderer.RenderToString(
                path,
                nodeServices,
                module,
                unencodedPathAndQuery,
                unencodedAbsoluteUrl,
                transferData,
                30000,
                "/songs"
                );

            ViewBag.Body         = prerenderResult.Html;
            ViewBag.TransferData = prerenderResult.Globals["transferData"];
            return(View());
        }
Exemplo n.º 9
0
        public async Task <IActionResult> Index()
        {
            var nodeServices = Request.HttpContext.RequestServices.GetRequiredService <INodeServices>();
            var hostEnv      = Request.HttpContext.RequestServices.GetRequiredService <IHostingEnvironment>();

            var applicationBasePath   = hostEnv.ContentRootPath;
            var requestFeature        = Request.HttpContext.Features.Get <IHttpRequestFeature>();
            var unencodedPathAndQuery = requestFeature.RawTarget;
            var unencodedAbsoluteUrl  = $"{Request.Scheme}://{Request.Host}{unencodedPathAndQuery}";

            // *********************************
            // This parameter is where you'd pass in an Object of data you want passed down to Angular
            // to be used in the Server-rendering

            // ** TransferData concept **
            // Here we can pass any Custom Data we want !

            // By default we're passing down the REQUEST Object (Cookies, Headers, Host) from the Request object here
            TransferData transferData = new TransferData();

            transferData.request            = AbstractHttpContextRequestInfo(Request); // You can automatically grab things from the REQUEST object in Angular because of this
            transferData.thisCameFromDotNET = "Hi Angular it's asp.net :)";
            // Add more customData here, add it to the TransferData class

            var applicationLifetime = (IApplicationLifetime)Request.HttpContext.RequestServices.GetRequiredService <IApplicationLifetime>();

            // Prerender / Serialize application (with Universal)
            var prerenderResult = await Prerenderer.RenderToString(
                "/", // baseURL
                nodeServices,
                applicationLifetime.ApplicationStopping,
                new JavaScriptModuleExport(applicationBasePath + "/ClientApp/dist/main"),
                unencodedAbsoluteUrl,
                unencodedPathAndQuery,
                // Our Transfer data here will be passed down to Angular (within the main.server file)
                // Available there via `params.data.yourData`
                transferData,
                30000, // timeout duration
                Request.PathBase.ToString()
                );

            // This is where everything is now spliced out, and given to .NET in pieces
            ViewData["SpaHtml"]      = prerenderResult.Html;
            ViewData["Title"]        = prerenderResult.Globals["title"];
            ViewData["Styles"]       = prerenderResult.Globals["styles"];
            ViewData["Meta"]         = prerenderResult.Globals["meta"];
            ViewData["Links"]        = prerenderResult.Globals["links"];
            ViewData["TransferData"] = prerenderResult.Globals["transferData"]; // our transfer data set to window.TRANSFER_CACHE = {};

            // Let's render that Home/Index view
            return(View());
        }
Exemplo n.º 10
0
        public async Task <IActionResult> Index()
        {
            // for production, enable server side renderring
            var applicationBasePath   = _hostingEnvironment.ContentRootPath;
            var requestFeature        = Request.HttpContext.Features.Get <IHttpRequestFeature>();
            var unencodedPathAndQuery = requestFeature.RawTarget;
            var unencodedAbsoluteUrl  = $"{Request.Scheme}://{Request.Host}{unencodedPathAndQuery}";

            // ** TransferData concept **
            // Here we can pass any Custom Data we want !

            TransferData transferData = new TransferData
            {
                request            = AbstractHttpContextRequestInfo(Request),
                thisCameFromDotNET = new
                {
                    heroCompanies = _heroAppService.GetHeroCompanies(),
                    heroes        = _heroAppService.GetHeroes(null)
                }
            };

            var nodeService = Request.HttpContext.RequestServices.GetRequiredService <INodeServices>(); // nodeServices

            //Prerender now needs CancellationToken
            System.Threading.CancellationTokenSource cancelSource = new System.Threading.CancellationTokenSource();
            System.Threading.CancellationToken       cancelToken  = cancelSource.Token;

            // Prerender / Serialize application (with Universal)
            var prerenderResult = await Prerenderer.RenderToString(
                "/",
                nodeService,
                //Request.HttpContext.RequestServices.GetRequiredService<INodeServices>(), // nodeServices
                cancelToken,
                new JavaScriptModuleExport(applicationBasePath + "/HeroApp/dist-server/main.bundle"),
                unencodedAbsoluteUrl,
                unencodedPathAndQuery,
                transferData, // Our simplified Request object & any other CustommData you want to send!
                30000,
                Request.PathBase.ToString()
                );

            ViewData["SpaHtml"]      = prerenderResult.Html;                    // our <app> from Angular
            ViewData["Title"]        = prerenderResult.Globals["title"];        // set our <title> from Angular
            ViewData["Styles"]       = prerenderResult.Globals["styles"];       // put styles in the correct place
            ViewData["Meta"]         = prerenderResult.Globals["meta"];         // set our <meta> SEO tags
            ViewData["Links"]        = prerenderResult.Globals["links"];        // set our <link rel="canonical"> etc SEO tags
            ViewData["TransferData"] = prerenderResult.Globals["transferData"]; // our transfer data set to window.TRANSFER_CACHE = {};

            return(View());
        }
        public async Task <IActionResult> Index()
        {
            var nodeServices = Request.HttpContext.RequestServices.GetRequiredService <INodeServices>();
            var hostEnv      = Request.HttpContext.RequestServices.GetRequiredService <IHostingEnvironment>();

            var applicationBasePath   = hostEnv.ContentRootPath;
            var requestFeature        = Request.HttpContext.Features.Get <IHttpRequestFeature>();
            var unencodedPathAndQuery = requestFeature.RawTarget;
            var unencodedAbsoluteUrl  = $"{Request.Scheme}://{Request.Host}{unencodedPathAndQuery}";

            // ** TransferData concept **
            // Here we can pass any Custom Data we want !

            // By default we're passing down Cookies, Headers, Host from the Request object here
            TransferData transferData = new TransferData();

            transferData.request            = AbstractHttpContextRequestInfo(Request);
            transferData.thisCameFromDotNET = "Hi Angular it's asp.net :)";
            // Add more customData here, add it to the TransferData class

            //Prerender now needs CancellationToken
            System.Threading.CancellationTokenSource cancelSource = new System.Threading.CancellationTokenSource();
            System.Threading.CancellationToken       cancelToken  = cancelSource.Token;

            // Prerender / Serialize application (with Universal)
            var prerenderResult = await Prerenderer.RenderToString(
                "/",
                nodeServices,
                cancelToken,
                new JavaScriptModuleExport(applicationBasePath + "/ClientApp/dist/main-server"),
                unencodedAbsoluteUrl,
                unencodedPathAndQuery,
                transferData, // Our simplified Request object & any other CustommData you want to send!
                30000,
                Request.PathBase.ToString()
                );

            ViewData["SpaHtml"]      = prerenderResult.Html;                    // our <app> from Angular
            ViewData["Title"]        = prerenderResult.Globals["title"];        // set our <title> from Angular
            ViewData["Styles"]       = prerenderResult.Globals["styles"];       // put styles in the correct place
            ViewData["Scripts"]      = prerenderResult.Globals["scripts"];      // scripts (that were in our header)
            ViewData["Meta"]         = prerenderResult.Globals["meta"];         // set our <meta> SEO tags
            ViewData["Links"]        = prerenderResult.Globals["links"];        // set our <link rel="canonical"> etc SEO tags
            ViewData["TransferData"] = prerenderResult.Globals["transferData"]; // our transfer data set to window.TRANSFER_CACHE = {};

            return(View());
        }
Exemplo n.º 12
0
        public static async Task <SsrResult> RenderAsync(HttpRequest request, string jsBundlePath, string appBaseUrl, SsrData data)
        {
            // Based on https://github.com/MarkPieszak/aspnetcore-angular2-universal/blob/04273f64e122fa5648f4d21fab76da30163eedfd/Server/Controllers/HomeController.cs

            var nodeServices = request.HttpContext.RequestServices.GetRequiredService <INodeServices>();
            var hostEnv      = request.HttpContext.RequestServices.GetRequiredService <IHostingEnvironment>();
            var logger       = request.HttpContext.RequestServices.GetRequiredService <ILogger <SsrRenderer> >();

            var applicationBasePath   = hostEnv.WebRootPath;
            var requestFeature        = request.HttpContext.Features.Get <IHttpRequestFeature>();
            var unencodedPathAndQuery = requestFeature.RawTarget;
            var unencodedAbsoluteUrl  = $"{request.Scheme}://{request.Host}{unencodedPathAndQuery}";

            var appPath        = $"{request.PathBase}{appBaseUrl}";
            var appRelativeUrl = unencodedPathAndQuery.StartsWith(appPath) ? unencodedPathAndQuery.Substring(appPath.Length) : unencodedPathAndQuery;

            logger.LogDebug($"[SSR] request.PathBase: {request.PathBase}; requestFeature.RawTarget: {requestFeature.RawTarget}, unencodedAbsoluteUrl: {unencodedAbsoluteUrl}, appBaseUrl: {appBaseUrl}, appPath: {appPath}, appRelativeUrl: {appRelativeUrl}");

            try {
                var prerenderResult = await Prerenderer.RenderToString(
                    "/",
                    nodeServices,
                    new JavaScriptModuleExport(hostEnv.WebRootPath + "/" + jsBundlePath),
                    unencodedAbsoluteUrl,
                    appRelativeUrl,
                    data,
                    PrerenderTimeoutMilliseconds,
                    appBaseUrl
                    );

                var result = new SsrResult(
                    prerenderResult.Html, // our <app> from Angular
                    prerenderResult.Globals["title"].ToString(),
                    prerenderResult.Globals["styles"].ToString(),
                    prerenderResult.Globals["meta"].ToString(),
                    prerenderResult.Globals["links"].ToString()
                    );

                return(result);
            }
            catch (Exception ex) {
                logger.LogError(new EventId(0, name: "ssr"), ex, "Server-side rendering failed.");

                return(null);
            }
        }
    public async Task <string> Get()
    {
        var requestFeature        = Request.HttpContext.Features.Get <IHttpRequestFeature>();
        var unencodedPathAndQuery = requestFeature.RawTarget;
        var unencodedAbsoluteUrl  = $"{Request.Scheme}://{Request.Host}{unencodedPathAndQuery}";
        var prerenderResult       = await Prerenderer.RenderToString(
            hostEnv.ContentRootPath,
            nodeServices,
            new JavaScriptModuleExport("ClientApp/dist/main-server"),
            unencodedAbsoluteUrl,
            unencodedPathAndQuery,
            /* custom data parameter */ null,
            /* timeout milliseconds */ 15 * 1000,
            Request.PathBase.ToString()
            );

        return(prerenderResult.Html);
    }
 public static async Task <RenderToStringResult> BuildPrerender(this HttpRequest request) =>
 // Prerender / Serialize application (with Universal)
 await Prerenderer.RenderToString(
     "/",
     request.HttpContext.RequestServices.GetRequiredService <INodeServices>(),
     new System.Threading.CancellationTokenSource().Token,
     new JavaScriptModuleExport(request.HttpContext.RequestServices.GetRequiredService <IHostingEnvironment>().ContentRootPath + "/wwwroot/dist/main"),
     $"{request.Scheme}://{request.Host}{request.HttpContext.Features.Get<IHttpRequestFeature>().RawTarget}",
     request.HttpContext.Features.Get <IHttpRequestFeature>().RawTarget,
     // ** TransferData concept **
     // Here we can pass any Custom Data we want !
     // By default we're passing down Cookies, Headers, Host from the Request object here
     new TransferData
 {
     request            = request.AbstractRequestInfo(),
     thisCameFromDotNET = "Hi Angular it's asp.net :)"
 },         // Our simplified Request object & any other CustommData you want to send!
     30000,
     request.PathBase.ToString()
     );
Exemplo n.º 15
0
        public static async Task <SsrResult> RenderAsync(HttpRequest request, SsrOptions options)
        {
            var nodeServices = request.HttpContext.RequestServices.GetRequiredService <INodeServices>();
            var hostEnv      = request.HttpContext.RequestServices.GetRequiredService <IHostingEnvironment>();
            var logger       = request.HttpContext.RequestServices.GetRequiredService <ILogger <SsrHelper> >();


            var applicationBasePath   = hostEnv.WebRootPath;
            var requestFeature        = request.HttpContext.Features.Get <IHttpRequestFeature>();
            var unencodedPathAndQuery = requestFeature.RawTarget;
            var unencodedAbsoluteUrl  = $"{request.Scheme}://{request.Host}{unencodedPathAndQuery}";

            try {
                var prerenderResult = await Prerenderer.RenderToString(
                    "/",
                    nodeServices,
                    new JavaScriptModuleExport(hostEnv.WebRootPath + "/" + options.JsBundlePath),
                    unencodedAbsoluteUrl,
                    unencodedPathAndQuery,
                    null,
                    30000,
                    request.PathBase.ToString()
                    );

                var result = new SsrResult(
                    prerenderResult.Html, // our <app> from Angular
                    prerenderResult.Globals["title"].ToString(),
                    prerenderResult.Globals["styles"].ToString(),
                    prerenderResult.Globals["meta"].ToString(),
                    prerenderResult.Globals["links"].ToString()
                    );

                return(result);
            }
            catch (Exception ex) {
                logger.LogError(new EventId(0, name: "ssr"), ex, "Server-side rendering failed.");

                return(null);
            }
        }
Exemplo n.º 16
0
        public static async Task <RenderToStringResult> BuildPrerender(this HttpRequest Request)
        {
            var nodeServices = Request.HttpContext.RequestServices.GetRequiredService <INodeServices>();
            var hostEnv      = Request.HttpContext.RequestServices.GetRequiredService <IHostingEnvironment>();

            var applicationBasePath   = hostEnv.ContentRootPath;
            var requestFeature        = Request.HttpContext.Features.Get <IHttpRequestFeature>();
            var unencodedPathAndQuery = requestFeature.RawTarget;
            var unencodedAbsoluteUrl  = $"{Request.Scheme}://{Request.Host}{unencodedPathAndQuery}";

            // ** TransferData concept **
            // Here we can pass any Custom Data we want !

            // By default we're passing down Cookies, Headers, Host from the Request object here
            TransferData transferData = new TransferData();

            transferData.request = Request.AbstractRequestInfo();

            var cookie = Request.Cookies["SpiskerOAuth2Token"];

            transferData.SpiskerOAuth2Token = cookie;
            // Add more customData here, add it to the TransferData class

            //Prerender now needs CancellationToken
            System.Threading.CancellationTokenSource cancelSource = new System.Threading.CancellationTokenSource();
            System.Threading.CancellationToken       cancelToken  = cancelSource.Token;

            // Prerender / Serialize application (with Universal)
            return(await _policy.ExecuteAsync(() => Prerenderer.RenderToString(
                                                  "/",
                                                  nodeServices,
                                                  cancelToken,
                                                  new JavaScriptModuleExport(applicationBasePath + "/ClientApp/dist/main-server"),
                                                  unencodedAbsoluteUrl,
                                                  unencodedPathAndQuery,
                                                  transferData, // Our simplified Request object & any other CustommData you want to send!
                                                  30000,
                                                  Request.PathBase.ToString()
                                                  )));
        }
        public static async Task <RenderToStringResult> BuildPrerenderAsync(this HttpRequest target, String entryPoint)
        {
            var __NodeServices       = target.HttpContext.RequestServices.GetRequiredService <INodeServices>();
            var __HostingEnvironment = target.HttpContext.RequestServices.GetRequiredService <IHostingEnvironment>();

            var __ApplicationBasePath   = __HostingEnvironment.ContentRootPath;
            var __RequestFeature        = target.HttpContext.Features.Get <IHttpRequestFeature>();
            var __UnencodedPathAndQuery = __RequestFeature.RawTarget;
            var __UnencodedAbsoluteUrl  = $"{target.Scheme}://{target.Host}{__UnencodedPathAndQuery}";

            // ** TransferData concept **
            // Here we can pass any Custom Data we want !

            // By default we're passing down Cookies, Headers, Host from the Request object here
            // Add more customData here, add it to the TransferData class
            var __TransferData = new TransferData
            {
                request            = target.AbstractRequestInfo(),
                thisCameFromDotNET = "Hi Angular it's asp.net :)"
            };

            //Prerender now needs CancellationToken
            var __CancelSource      = new System.Threading.CancellationTokenSource();
            var __CancellationToken = __CancelSource.Token;

            // Prerender / Serialize application (with Universal)
            return(await Prerenderer.RenderToString
                   (
                       "/",
                       __NodeServices,
                       __CancellationToken,
                       new JavaScriptModuleExport(__ApplicationBasePath + entryPoint),
                       __UnencodedAbsoluteUrl,
                       __UnencodedPathAndQuery,
                       __TransferData,          // Our simplified Request object & any other CustommData you want to send!
                       30000,
                       target.PathBase.ToString()
                   ).ConfigureAwait(false));
        }
        public static async Task <RenderToStringResult> BuildPrerender(this HttpRequest request)
        {
            var nodeServices = request.HttpContext.RequestServices.GetRequiredService <INodeServices>();
            var hostEnv      = request.HttpContext.RequestServices.GetRequiredService <IHostingEnvironment>();

            var applicationBasePath   = hostEnv.ContentRootPath;
            var requestFeature        = request.HttpContext.Features.Get <IHttpRequestFeature>();
            var unencodedPathAndQuery = requestFeature.RawTarget;
            var unencodedAbsoluteUrl  = $"{request.Scheme}://{request.Host}{unencodedPathAndQuery}";

            // ** TransferData concept **
            // Here we can pass any Custom Data we want !

            // By default we're passing down Cookies, Headers, Host from the Request object here
            TransferData transferData = new TransferData
            {
                request            = request.AbstractRequestInfo(),
                thisCameFromDotNET = "Hi Angular it's asp.net :)"
            };
            // Add more customData here, add it to the TransferData class

            //Prerender now needs CancellationToken
            CancellationTokenSource cancelSource = new CancellationTokenSource();
            CancellationToken       cancelToken  = cancelSource.Token;

            // Prerender / Serialize application (with Universal)
            return(await Prerenderer.RenderToString(
                       "/",
                       nodeServices,
                       cancelToken,
                       new JavaScriptModuleExport(applicationBasePath + "/CodefictionApp/dist-server/main.bundle"),
                       unencodedAbsoluteUrl,
                       unencodedPathAndQuery,
                       transferData, // Our simplified Request object & any other CustommData you want to send!
                       30000,
                       request.PathBase.ToString()
                       ));
        }
Exemplo n.º 19
0
        public override async Task ExecuteResultAsync(ActionContext context)
        {
            var nodeServices        = context.HttpContext.RequestServices.GetRequiredService <INodeServices>();
            var hostEnv             = context.HttpContext.RequestServices.GetRequiredService <IHostingEnvironment>();
            var applicationLifetime = context.HttpContext.RequestServices.GetRequiredService <IApplicationLifetime>();
            var applicationBasePath = hostEnv.ContentRootPath;
            var request             = context.HttpContext.Request;
            var response            = context.HttpContext.Response;

            var prerenderedHtml = await Prerenderer.RenderToString(
                applicationBasePath,
                nodeServices,
                applicationLifetime.ApplicationStopping,
                _moduleExport,
                request.GetEncodedUrl(),
                request.Path + request.QueryString.Value,
                _dataToSupply,
                /* timeoutMilliseconds */ 30000,
                /* requestPathBase */ "/"
                );

            response.ContentType = "text/html";
            await response.WriteAsync(prerenderedHtml.Html);
        }
        /// <summary>
        /// Enables server-side prerendering middleware for a Single Page Application.
        /// </summary>
        /// <param name="appBuilder">The <see cref="IApplicationBuilder"/>.</param>
        /// <param name="entryPoint">The path, relative to your application root, of the JavaScript file containing prerendering logic.</param>
        /// <param name="buildOnDemand">Optional. If specified, executes the supplied <see cref="ISpaPrerendererBuilder"/> before looking for the <paramref name="entryPoint"/> file. This is only intended to be used during development.</param>
        public static void UseSpaPrerendering(
            this IApplicationBuilder appBuilder,
            string entryPoint,
            ISpaPrerendererBuilder buildOnDemand = null)
        {
            if (string.IsNullOrEmpty(entryPoint))
            {
                throw new ArgumentException("Cannot be null or empty", nameof(entryPoint));
            }

            var defaultPageMiddleware = SpaDefaultPageMiddleware.FindInPipeline(appBuilder);

            if (defaultPageMiddleware == null)
            {
                throw new Exception($"{nameof(UseSpaPrerendering)} should be called inside the 'configure' callback of a call to {nameof(SpaApplicationBuilderExtensions.UseSpa)}.");
            }

            var urlPrefix = defaultPageMiddleware.UrlPrefix;

            if (urlPrefix == null || urlPrefix.Length < 2)
            {
                throw new ArgumentException(
                          "If you are using server-side prerendering, the SPA's public path must be " +
                          "set to a non-empty and non-root value. This makes it possible to identify " +
                          "requests for the SPA's internal static resources, so the prerenderer knows " +
                          "not to return prerendered HTML for those requests.",
                          nameof(urlPrefix));
            }

            // We only want to start one build-on-demand task, but it can't commence until
            // a request comes in (because we need to wait for all middleware to be configured)
            var lazyBuildOnDemandTask = new Lazy <Task>(() => buildOnDemand?.Build(appBuilder));

            // Get all the necessary context info that will be used for each prerendering call
            var serviceProvider          = appBuilder.ApplicationServices;
            var nodeServices             = GetNodeServices(serviceProvider);
            var applicationStoppingToken = serviceProvider.GetRequiredService <IApplicationLifetime>()
                                           .ApplicationStopping;
            var applicationBasePath = serviceProvider.GetRequiredService <IHostingEnvironment>()
                                      .ContentRootPath;
            var moduleExport          = new JavaScriptModuleExport(entryPoint);
            var urlPrefixAsPathString = new PathString(urlPrefix);

            // Add the actual middleware that intercepts requests for the SPA default file
            // and invokes the prerendering code
            appBuilder.Use(async(context, next) =>
            {
                // Don't interfere with requests that are within the SPA's urlPrefix, because
                // these requests are meant to serve its internal resources (.js, .css, etc.)
                if (context.Request.Path.StartsWithSegments(urlPrefixAsPathString))
                {
                    await next();
                    return;
                }

                // If we're building on demand, do that first
                var buildOnDemandTask = lazyBuildOnDemandTask.Value;
                if (buildOnDemandTask != null && !buildOnDemandTask.IsCompleted)
                {
                    await buildOnDemandTask;
                }

                // As a workaround for @angular/cli not emitting the index.html in 'server'
                // builds, pass through a URL that can be used for obtaining it. Longer term,
                // remove this.
                var customData = new
                {
                    templateUrl = GetDefaultFileAbsoluteUrl(context, defaultPageMiddleware.DefaultPageUrl)
                };

                // TODO: Add an optional "supplyCustomData" callback param so people using
                //       UsePrerendering() can, for example, pass through cookies into the .ts code

                var renderResult = await Prerenderer.RenderToString(
                    applicationBasePath,
                    nodeServices,
                    applicationStoppingToken,
                    moduleExport,
                    context,
                    customDataParameter: customData,
                    timeoutMilliseconds: 0);

                await ApplyRenderResult(context, renderResult);
            });
        }
        /// <summary>
        /// Enables server-side prerendering middleware for a Single Page Application.
        /// </summary>
        /// <param name="appBuilder">The <see cref="IApplicationBuilder"/>.</param>
        /// <param name="entryPoint">The path, relative to your application root, of the JavaScript file containing prerendering logic.</param>
        /// <param name="buildOnDemand">Optional. If specified, executes the supplied <see cref="ISpaPrerendererBuilder"/> before looking for the <paramref name="entryPoint"/> file. This is only intended to be used during development.</param>
        /// <param name="excludeUrls">Optional. If specified, requests within these URL paths will bypass the prerenderer.</param>
        /// <param name="supplyData">Optional. If specified, this callback will be invoked during prerendering, allowing you to pass additional data to the prerendering entrypoint code.</param>
        public static void UseSpaPrerendering(
            this IApplicationBuilder appBuilder,
            string entryPoint,
            ISpaPrerendererBuilder buildOnDemand = null,
            string[] excludeUrls = null,
            Action <HttpContext, IDictionary <string, object> > supplyData = null)
        {
            if (string.IsNullOrEmpty(entryPoint))
            {
                throw new ArgumentException("Cannot be null or empty", nameof(entryPoint));
            }

            // If we're building on demand, start that process now
            var buildOnDemandTask = buildOnDemand?.Build(appBuilder);

            // Get all the necessary context info that will be used for each prerendering call
            var serviceProvider          = appBuilder.ApplicationServices;
            var nodeServices             = GetNodeServices(serviceProvider);
            var applicationStoppingToken = serviceProvider.GetRequiredService <IApplicationLifetime>()
                                           .ApplicationStopping;
            var applicationBasePath = serviceProvider.GetRequiredService <IHostingEnvironment>()
                                      .ContentRootPath;
            var moduleExport       = new JavaScriptModuleExport(entryPoint);
            var excludePathStrings = (excludeUrls ?? Array.Empty <string>())
                                     .Select(url => new PathString(url))
                                     .ToArray();

            // Capture the non-prerendered responses, which in production will typically only
            // be returning the default SPA index.html page (because other resources will be
            // served statically from disk). We will use this as a template in which to inject
            // the prerendered output.
            appBuilder.Use(async(context, next) =>
            {
                // If this URL is excluded, skip prerendering
                foreach (var excludePathString in excludePathStrings)
                {
                    if (context.Request.Path.StartsWithSegments(excludePathString))
                    {
                        await next();
                        return;
                    }
                }

                // If we're building on demand, do that first
                if (buildOnDemandTask != null)
                {
                    await buildOnDemandTask;
                }

                // It's no good if we try to return a 304. We need to capture the actual
                // HTML content so it can be passed as a template to the prerenderer.
                RemoveConditionalRequestHeaders(context.Request);

                using (var outputBuffer = new MemoryStream())
                {
                    var originalResponseStream = context.Response.Body;
                    context.Response.Body      = outputBuffer;

                    try
                    {
                        await next();
                        outputBuffer.Seek(0, SeekOrigin.Begin);
                    }
                    finally
                    {
                        context.Response.Body = originalResponseStream;
                    }

                    // If it's not a success response, we're not going to have any template HTML
                    // to pass to the prerenderer.
                    if (context.Response.StatusCode < 200 || context.Response.StatusCode >= 300)
                    {
                        var message = $"Prerendering failed because no HTML template could be obtained. Check that your SPA is compiling without errors. The {nameof(SpaApplicationBuilderExtensions.UseSpa)}() middleware returned a response with status code {context.Response.StatusCode}";
                        if (outputBuffer.Length > 0)
                        {
                            message += " and the following content: " + Encoding.UTF8.GetString(outputBuffer.GetBuffer());
                        }

                        throw new InvalidOperationException(message);
                    }

                    // Most prerendering logic will want to know about the original, unprerendered
                    // HTML that the client would be getting otherwise. Typically this is used as
                    // a template from which the fully prerendered page can be generated.
                    var customData = new Dictionary <string, object>
                    {
                        { "originalHtml", Encoding.UTF8.GetString(outputBuffer.GetBuffer()) }
                    };

                    supplyData?.Invoke(context, customData);

                    var(unencodedAbsoluteUrl, unencodedPathAndQuery) = GetUnencodedUrlAndPathQuery(context);
                    var renderResult = await Prerenderer.RenderToString(
                        applicationBasePath,
                        nodeServices,
                        applicationStoppingToken,
                        moduleExport,
                        unencodedAbsoluteUrl,
                        unencodedPathAndQuery,
                        customDataParameter: customData,
                        timeoutMilliseconds: 0,
                        requestPathBase: context.Request.PathBase.ToString());

                    await ApplyRenderResult(context, renderResult);
                }
            });
        }
        /// <summary>
        /// Adds middleware for server-side prerendering of a Single Page Application.
        /// </summary>
        /// <param name="spaBuilder">The <see cref="ISpaBuilder"/>.</param>
        /// <param name="entryPoint">The path, relative to your application root, of the JavaScript file containing prerendering logic.</param>
        /// <param name="buildOnDemand">Optional. If specified, executes the supplied <see cref="ISpaPrerendererBuilder"/> before looking for the <paramref name="entryPoint"/> file. This is only intended to be used during development.</param>
        public static void UsePrerendering(
            this ISpaBuilder spaBuilder,
            string entryPoint,
            ISpaPrerendererBuilder buildOnDemand = null)
        {
            if (string.IsNullOrEmpty(entryPoint))
            {
                throw new ArgumentException("Cannot be null or empty", nameof(entryPoint));
            }

            // We only want to start one build-on-demand task, but it can't commence until
            // a request comes in (because we need to wait for all middleware to be configured)
            var lazyBuildOnDemandTask = new Lazy <Task>(() => buildOnDemand?.Build(spaBuilder));

            // Get all the necessary context info that will be used for each prerendering call
            var appBuilder               = spaBuilder.AppBuilder;
            var serviceProvider          = appBuilder.ApplicationServices;
            var nodeServices             = GetNodeServices(serviceProvider);
            var applicationStoppingToken = GetRequiredService <IApplicationLifetime>(serviceProvider)
                                           .ApplicationStopping;
            var applicationBasePath = GetRequiredService <IHostingEnvironment>(serviceProvider)
                                      .ContentRootPath;
            var moduleExport = new JavaScriptModuleExport(entryPoint);

            // Add the actual middleware that intercepts requests for the SPA default file
            // and invokes the prerendering code
            appBuilder.Use(async(context, next) =>
            {
                // Don't interfere with requests that aren't meant to render the SPA default file
                if (!context.Items.ContainsKey(SpaExtensions.IsSpaFallbackRequestTag))
                {
                    await next();
                    return;
                }

                // If we're building on demand, do that first
                var buildOnDemandTask = lazyBuildOnDemandTask.Value;
                if (buildOnDemandTask != null && !buildOnDemandTask.IsCompleted)
                {
                    await buildOnDemandTask;
                }

                // If we're waiting for other SPA initialization tasks, do that first.
                await spaBuilder.StartupTasks;

                // As a workaround for @angular/cli not emitting the index.html in 'server'
                // builds, pass through a URL that can be used for obtaining it. Longer term,
                // remove this.
                var customData = new
                {
                    templateUrl = GetDefaultFileAbsoluteUrl(spaBuilder, context)
                };

                // TODO: Add an optional "supplyCustomData" callback param so people using
                //       UsePrerendering() can, for example, pass through cookies into the .ts code

                var renderResult = await Prerenderer.RenderToString(
                    applicationBasePath,
                    nodeServices,
                    applicationStoppingToken,
                    moduleExport,
                    context,
                    customDataParameter: customData,
                    timeoutMilliseconds: 0);

                await ApplyRenderResult(context, renderResult);
            });
        }
        /// <summary>
        /// Enables server-side prerendering middleware for a Single Page Application.
        /// </summary>
        /// <param name="spaBuilder">The <see cref="ISpaBuilder"/>.</param>
        /// <param name="configuration">Supplies configuration for the prerendering middleware.</param>
        public static void UseSpaPrerendering(
            this ISpaBuilder spaBuilder,
            Action <SpaPrerenderingOptions> configuration)
        {
            if (spaBuilder == null)
            {
                throw new ArgumentNullException(nameof(spaBuilder));
            }

            if (configuration == null)
            {
                throw new ArgumentNullException(nameof(configuration));
            }

            var options = new SpaPrerenderingOptions();

            configuration.Invoke(options);

            var capturedBootModulePath = options.BootModulePath;

            if (string.IsNullOrEmpty(capturedBootModulePath))
            {
                throw new InvalidOperationException($"To use {nameof(UseSpaPrerendering)}, you " +
                                                    $"must set a nonempty value on the ${nameof(SpaPrerenderingOptions.BootModulePath)} " +
                                                    $"property on the ${nameof(SpaPrerenderingOptions)}.");
            }

            // If we're building on demand, start that process in the background now
            var buildOnDemandTask = options.BootModuleBuilder?.Build(spaBuilder);

            // Get all the necessary context info that will be used for each prerendering call
            var applicationBuilder       = spaBuilder.ApplicationBuilder;
            var serviceProvider          = applicationBuilder.ApplicationServices;
            var nodeServices             = GetNodeServices(serviceProvider);
            var applicationStoppingToken = serviceProvider.GetRequiredService <IApplicationLifetime>()
                                           .ApplicationStopping;
            var applicationBasePath = serviceProvider.GetRequiredService <IHostingEnvironment>()
                                      .ContentRootPath;
            var moduleExport       = new JavaScriptModuleExport(capturedBootModulePath);
            var excludePathStrings = (options.ExcludeUrls ?? Array.Empty <string>())
                                     .Select(url => new PathString(url))
                                     .ToArray();

            applicationBuilder.Use(async(context, next) =>
            {
                // If this URL is excluded, skip prerendering.
                // This is typically used to ensure that static client-side resources
                // (e.g., /dist/*.css) are served normally or through SPA development
                // middleware, and don't return the prerendered index.html page.
                foreach (var excludePathString in excludePathStrings)
                {
                    if (context.Request.Path.StartsWithSegments(excludePathString))
                    {
                        await next();
                        return;
                    }
                }

                // If we're building on demand, wait for that to finish, or raise any build errors
                if (buildOnDemandTask != null)
                {
                    await buildOnDemandTask;
                }

                // It's no good if we try to return a 304. We need to capture the actual
                // HTML content so it can be passed as a template to the prerenderer.
                RemoveConditionalRequestHeaders(context.Request);

                // Make sure we're not capturing compressed content, because then we'd have
                // to decompress it. Since this sub-request isn't leaving the machine, there's
                // little to no benefit in having compression on it.
                var originalAcceptEncodingValue = GetAndRemoveAcceptEncodingHeader(context.Request);

                // Capture the non-prerendered responses, which in production will typically only
                // be returning the default SPA index.html page (because other resources will be
                // served statically from disk). We will use this as a template in which to inject
                // the prerendered output.
                using (var outputBuffer = new MemoryStream())
                {
                    var originalResponseStream = context.Response.Body;
                    context.Response.Body      = outputBuffer;

                    try
                    {
                        await next();
                        outputBuffer.Seek(0, SeekOrigin.Begin);
                    }
                    finally
                    {
                        context.Response.Body = originalResponseStream;

                        if (!string.IsNullOrEmpty(originalAcceptEncodingValue))
                        {
                            context.Request.Headers[HeaderNames.AcceptEncoding] = originalAcceptEncodingValue;
                        }
                    }

                    // If it isn't an HTML page that we can use as the template for prerendering,
                    //  - ... because it's not text/html
                    //  - ... or because it's an error
                    // then prerendering doesn't apply to this request, so just pass through the
                    // response as-is. Note that the non-text/html case is not an error: this is
                    // typically how the SPA dev server responses for static content are returned
                    // in development mode.
                    var canPrerender = IsSuccessStatusCode(context.Response.StatusCode) &&
                                       IsHtmlContentType(context.Response.ContentType);
                    if (!canPrerender)
                    {
                        await outputBuffer.CopyToAsync(context.Response.Body);
                        return;
                    }

                    // Most prerendering logic will want to know about the original, unprerendered
                    // HTML that the client would be getting otherwise. Typically this is used as
                    // a template from which the fully prerendered page can be generated.
                    var customData = new Dictionary <string, object>
                    {
                        { "originalHtml", Encoding.UTF8.GetString(outputBuffer.GetBuffer()) }
                    };

                    // If the developer wants to use custom logic to pass arbitrary data to the
                    // prerendering JS code (e.g., to pass through cookie data), now's their chance
                    options.SupplyData?.Invoke(context, customData);

                    var(unencodedAbsoluteUrl, unencodedPathAndQuery)
                        = GetUnencodedUrlAndPathQuery(context);
                    var renderResult = await Prerenderer.RenderToString(
                        applicationBasePath,
                        nodeServices,
                        applicationStoppingToken,
                        moduleExport,
                        unencodedAbsoluteUrl,
                        unencodedPathAndQuery,
                        customDataParameter: customData,
                        timeoutMilliseconds: 0,
                        requestPathBase: context.Request.PathBase.ToString());

                    await ServePrerenderResult(context, renderResult);
                }
            });
        }