I’ve developed a web application built using ASP.NET Core Web API
and Angular 4
. My module bundler is Web Pack 2
.
I would like to make my application crawlable or link sharable by Facebook, Twitter, Google. The url
must be the same when some user tries to post my news at Facebook. For example, Jon wants to share a page with url – http://myappl.com/#/hellopage
at Facebook, then Jon inserts this link into Facebook: http://myappl.com/#/hellopage
.
I’ve seen this tutorial of Angular Universal server side rendering without tag helper and would like to make server side rendering. As I use ASP.NET Core Web API
and my Angular 4
application does not have any .cshtml
views, so I cannot send data from controller to view through ViewData["SpaHtml"]
from my controller:
ViewData["SpaHtml"] = prerenderResult.Html;
In addition, I see this google tutorial of Angular Universal, but they use NodeJS
server, not ASP.NET Core
.
I would like to use server side prerendering. I am adding metatags through this way:
import { Meta } from '@angular/platform-browser';
constructor(
private metaService: Meta) {
}
let newText = "Foo data. This is test data!:)";
//metatags to publish this page at social nets
this.metaService.addTags([
// Open Graph data
{ property: 'og:title', content: newText },
{ property: 'og:description', content: newText }, {
{ property: "og:url", content: window.location.href },
{ property: 'og:image', content: "http://www.freeimageslive.co.uk/files
/images004/Italy_Venice_Canal_Grande.jpg" }]);
and when I inspect this element in a browser it looks like this:
<head>
<meta property="og:title" content="Foo data. This is test data!:)">
<meta property="og:description" content="Foo data. This is test data!:)">
<meta name="og:url" content="http://foourl.com">
<meta property="og:image" content="http://www.freeimageslive.co.uk/files
/images004/Italy_Venice_Canal_Grande.jpg"">
</head>
I am bootstrapping the application usual way:
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
and my webpack.config.js
config looks like this:
var path = require('path');
var webpack = require('webpack');
var ProvidePlugin = require('webpack/lib/ProvidePlugin');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var CopyWebpackPlugin = require('copy-webpack-plugin');
var CleanWebpackPlugin = require('clean-webpack-plugin');
var WebpackNotifierPlugin = require('webpack-notifier');
var isProd = (process.env.NODE_ENV === 'production');
function getPlugins() {
var plugins = [];
// Always expose NODE_ENV to webpack, you can now use `process.env.NODE_ENV`
// inside your code for any environment checks; UglifyJS will automatically
// drop any unreachable code.
plugins.push(new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify(process.env.NODE_ENV)
}
}));
plugins.push(new webpack.ProvidePlugin({
jQuery: 'jquery',
$: 'jquery',
jquery: 'jquery'
}));
plugins.push(new CleanWebpackPlugin(
[
'./wwwroot/js',
'./wwwroot/fonts',
'./wwwroot/assets'
]
));
return plugins;
}
module.exports = {
devtool: 'source-map',
entry: {
app: './persons-app/main.ts' //
},
output: {
path: "./wwwroot/",
filename: 'js/[name]-[hash:8].bundle.js',
publicPath: "/"
},
resolve: {
extensions: ['.ts', '.js', '.json', '.css', '.scss', '.html']
},
devServer: {
historyApiFallback: true,
stats: 'minimal',
outputPath: path.join(__dirname, 'wwwroot/')
},
module: {
rules: [{
test: /.ts$/,
exclude: /node_modules/,
loader: 'tslint-loader',
enforce: 'pre'
},
{
test: /.ts$/,
loaders: [
'awesome-typescript-loader',
'angular2-template-loader',
'angular-router-loader',
'source-map-loader'
]
},
{
test: /.js/,
loader: 'babel',
exclude: /(node_modules|bower_components)/
},
{
test: /.(png|jpg|gif|ico)$/,
exclude: /node_modules/,
loader: "file?name=img/[name].[ext]"
},
{
test: /.css$/,
exclude: /node_modules/,
use: ['to-string-loader', 'style-loader', 'css-loader'],
},
{
test: /.scss$/,
exclude: /node_modules/,
loaders: ["style", "css", "sass"]
},
{
test: /.html$/,
loader: 'raw'
},
{
test: /.(eot|svg|ttf|woff|woff2|otf)$/,
loader: 'file?name=fonts/[name].[ext]'
}
],
exprContextCritical: false
},
plugins: getPlugins()
};
Is it possible to do server side rendering without ViewData
? Is there an alternative way to make server side rendering in ASP.NET Core Web API and Angular 2?
I have uploaded an example to a github repository.
2
Answers
Based on your linked tutorials you could return the HTML directly from the controller.
The prerendered page will be available at
http://<host>
:Note the
Produces
attribute, which allows to return HTML content. See this question.There is an option in Angular to use HTML5 style urls (without hashes): LocationStrategy and browser URL styles. You should opt this URL style. And for each URL that you want to be shared o Facebook you need to render the entire page as shown in the tutorial you referenced. Having full URL on server you are able to render corresponding view and return HTML.
Code provided by @DávidMolnár might work very well for the purpose, but I haven’t tried yet.
UPDATE:
First of all, to make server prerendering work you should not use
useHash: true
which prevents sending route information to the server.In the demo ASP.NET Core + Angular 2 universal app that was mentioned in GitHub issue you referenced, ASP.NET Core MVC Controller and View are used only to server prerendered HTML from Angular in a more convenient way. For the remaining part of application only WebAPI is used from .NET Core world everything else is Angular and related web technologies.
It is convenient to use Razor view, but if you are strictly against it you can hardcode HTML into controller action directly:
Please note that the fallback URL is used to process all routes in
HomeController
and render corresponding angular route:To make it easier to start consider to take that demo project and modify it to fit with your application.
UPDATE 2:
If you don’t need to use anything from ASP.NET MVC like Razor with NodeServices it feels more natural to me to host Universal Angular app with server prerendering on Node.js server. And host ASP.NET Web Api independently so that Angular UI can access API on different server. I think it is quite common approach to host static files (and utilize server prerendering in case) independently fro API.
Here is a starter repo of Universal Angular hosted on Node.js: https://github.com/angular/universal-starter.
And here is an example of how UI and web API can be hosted on different servers: https://github.com/thinktecture/nodejs-aspnetcore-webapi. Notice how API URL is configured in
urlService.ts
.Also you could consider to hide both UI and API server behind reverse proxy so that both can be accessed through same public domain and host and you don’t have to deal with CORS to make it work in a browser.