From 749c7cb3ce751725d38e23edc5be3e6cf8873c3b Mon Sep 17 00:00:00 2001 From: SteveSandersonMS Date: Tue, 19 Jul 2016 15:50:54 +0100 Subject: [PATCH] Add example of full-page prerendering via a custom action result --- .../Webpack/ActionResults/PrerenderResult.cs | 47 +++++++++++++++++++ .../PrerenderResultExtensions.cs | 13 +++++ .../Webpack/Clientside/PrerenderingSample.ts | 17 +++++++ .../FullPagePrerenderingController.cs | 25 ++++++++++ samples/misc/Webpack/Startup.cs | 2 + samples/misc/Webpack/Views/Home/Index.cshtml | 3 ++ samples/misc/Webpack/package.json | 9 ++-- samples/misc/Webpack/tsconfig.json | 3 +- 8 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 samples/misc/Webpack/ActionResults/PrerenderResult.cs create mode 100644 samples/misc/Webpack/ActionResults/PrerenderResultExtensions.cs create mode 100644 samples/misc/Webpack/Clientside/PrerenderingSample.ts create mode 100644 samples/misc/Webpack/Controllers/FullPagePrerenderingController.cs diff --git a/samples/misc/Webpack/ActionResults/PrerenderResult.cs b/samples/misc/Webpack/ActionResults/PrerenderResult.cs new file mode 100644 index 00000000..b8e7ec15 --- /dev/null +++ b/samples/misc/Webpack/ActionResults/PrerenderResult.cs @@ -0,0 +1,47 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Extensions; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.NodeServices; +using Microsoft.AspNetCore.SpaServices.Prerendering; +using Microsoft.Extensions.DependencyInjection; + +namespace Webpack.ActionResults +{ + // This is an example of how you could invoke the prerendering API from an ActionResult, so as to + // prerender a SPA component as the entire response page (instead of injecting the SPA component + // into a Razor view's output) + public class PrerenderResult : ActionResult + { + private JavaScriptModuleExport _moduleExport; + private object _dataToSupply; + + public PrerenderResult(JavaScriptModuleExport moduleExport, object dataToSupply = null) + { + _moduleExport = moduleExport; + _dataToSupply = dataToSupply; + } + + public override async Task ExecuteResultAsync(ActionContext context) + { + var nodeServices = context.HttpContext.RequestServices.GetRequiredService(); + var hostEnv = context.HttpContext.RequestServices.GetRequiredService(); + var applicationBasePath = hostEnv.ContentRootPath; + var request = context.HttpContext.Request; + var response = context.HttpContext.Response; + + var prerenderedHtml = await Prerenderer.RenderToString( + applicationBasePath, + nodeServices, + _moduleExport, + request.GetEncodedUrl(), + request.Path + request.QueryString.Value, + _dataToSupply + ); + + response.ContentType = "text/html"; + await response.WriteAsync(prerenderedHtml.Html); + } + } +} \ No newline at end of file diff --git a/samples/misc/Webpack/ActionResults/PrerenderResultExtensions.cs b/samples/misc/Webpack/ActionResults/PrerenderResultExtensions.cs new file mode 100644 index 00000000..926e1149 --- /dev/null +++ b/samples/misc/Webpack/ActionResults/PrerenderResultExtensions.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.SpaServices.Prerendering; + +namespace Webpack.ActionResults +{ + public static class PrerenderResultExtensions + { + public static PrerenderResult Prerender(this ControllerBase controller, JavaScriptModuleExport exportToPrerender, object dataToSupply = null) + { + return new PrerenderResult(exportToPrerender, dataToSupply); + } + } +} diff --git a/samples/misc/Webpack/Clientside/PrerenderingSample.ts b/samples/misc/Webpack/Clientside/PrerenderingSample.ts new file mode 100644 index 00000000..143ddd04 --- /dev/null +++ b/samples/misc/Webpack/Clientside/PrerenderingSample.ts @@ -0,0 +1,17 @@ +export default function (params: any): Promise<{ html: string, globals?: any }> { + return new Promise((resolve, reject) => { + + // Here, you could put any logic that synchronously or asynchronously prerenders + // your SPA components. For example, see the boot-server.ts files in the Angular2Spa + // and ReactReduxSpa templates for ways to prerender Angular 2 and React components. + // + // If you wanted, you could use a property on the 'params.data' object to specify + // which SPA component or template to render. + + const html = ` +

Hello

+ It works! You passed ${ JSON.stringify(params.data) } + and are currently requesting ${ params.location.path }`; + resolve({ html }); + }); +}; diff --git a/samples/misc/Webpack/Controllers/FullPagePrerenderingController.cs b/samples/misc/Webpack/Controllers/FullPagePrerenderingController.cs new file mode 100644 index 00000000..48935459 --- /dev/null +++ b/samples/misc/Webpack/Controllers/FullPagePrerenderingController.cs @@ -0,0 +1,25 @@ +using System; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.SpaServices.Prerendering; +using Webpack.ActionResults; + +namespace Webpack.Controllers +{ + // This sample shows how you could invoke the prerendering APIs directly from an MVC + // action result. + public class FullPagePrerenderingController : Controller + { + private static JavaScriptModuleExport BootModule = new JavaScriptModuleExport("Clientside/PrerenderingSample") + { + // Because the boot module is written in TypeScript, we need to specify a webpack + // config so it can be built. If it was written in JavaScript, this would not be needed. + WebpackConfig = "webpack.config.js" + }; + + public IActionResult Index() + { + var dataToSupply = new { nowTime = DateTime.Now.Ticks }; + return this.Prerender(BootModule, dataToSupply); + } + } +} diff --git a/samples/misc/Webpack/Startup.cs b/samples/misc/Webpack/Startup.cs index a19fcc1a..c81e5c0d 100755 --- a/samples/misc/Webpack/Startup.cs +++ b/samples/misc/Webpack/Startup.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using System.IO; +using Microsoft.AspNetCore.NodeServices; namespace Webpack { @@ -14,6 +15,7 @@ public class Startup public void ConfigureServices(IServiceCollection services) { services.AddMvc(); + services.AddNodeServices(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. diff --git a/samples/misc/Webpack/Views/Home/Index.cshtml b/samples/misc/Webpack/Views/Home/Index.cshtml index 7828ec19..365dfaad 100755 --- a/samples/misc/Webpack/Views/Home/Index.cshtml +++ b/samples/misc/Webpack/Views/Home/Index.cshtml @@ -5,6 +5,9 @@

Hello

Hi there. Enter some text: +
+See also: Full-page prerendering example + @section scripts { } diff --git a/samples/misc/Webpack/package.json b/samples/misc/Webpack/package.json index 39bacb81..e894a076 100644 --- a/samples/misc/Webpack/package.json +++ b/samples/misc/Webpack/package.json @@ -2,15 +2,18 @@ "name": "Webpack", "version": "0.0.0", "devDependencies": { - "aspnet-webpack": "^1.0.3", "css-loader": "^0.23.1", "extendify": "^1.0.0", "extract-text-webpack-plugin": "^1.0.1", "less": "^2.6.0", "less-loader": "^2.2.2", "style-loader": "^0.13.0", - "ts-loader": "^0.8.1", - "typescript": "^1.7.5", "webpack-hot-middleware": "^2.7.1" + }, + "dependencies": { + "aspnet-webpack": "^1.0.3", + "aspnet-prerendering": "^1.0.4", + "ts-loader": "^0.8.1", + "typescript": "^1.7.5" } } diff --git a/samples/misc/Webpack/tsconfig.json b/samples/misc/Webpack/tsconfig.json index 5cbeb866..bb577c55 100644 --- a/samples/misc/Webpack/tsconfig.json +++ b/samples/misc/Webpack/tsconfig.json @@ -1,7 +1,8 @@ { "compilerOptions": { "moduleResolution": "node", - "target": "es5", + "module": "commonjs", + "target": "es6", "jsx": "preserve", "sourceMap": true },