ASP.NET Core 6 Web app with index.html using <base href> would not load script files - html

I have an ASP.NET Core 6 app and I host my Angular app with it, among other things (from Program.cs):
...
app.Use(async (context, next) =>
{
RewriteXFrameOptionsHeader(context);
await next();
if (context.Response.StatusCode == 404 && !Path.HasExtension(context.Request.Path.Value))
{
context.Request.Path = "/";
await next();
}
});
app.UseDefaultFiles(new DefaultFilesOptions {DefaultFileNames = new List<string> {"index.html"}});
app.UseStaticFiles();
...
It it doesn't work, because scripts are not loaded. The Angular index.html uses base href and relative paths to js sources, like so:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<base href="/ngxapp">
...
<script src="runtime.7cef1b4acdcbe752.js" type="module"></script><script src="polyfills.e4b5afbd657fbe4a.js" type="module"></script><script src="main.c8a9bcf210ef6760.js" type="module"></script>
</body>
</html>
Even though script src's contain relative paths, the browser tries to load script from the root. Here's what dev tools Network tab shows:
Request URL: https://localhost:7101/runtime.7cef1b4acdcbe752.js
MDN docs for base href state:
The HTML element specifies the base URL to use for all relative URLs in a document.
Why then scripts are being loaded from root?

Related

How to link css file with ejs?

In my directory I have created a public folder in which I have put another folder named css in which is located styles.css. In another folder in my directory named views I have put my ejs file, with which I want to link styles.css like this:
<link rel="stylesheet" type="css/text" href="css/styles.css">
However this does not work.
I dont even get an error in my browsers console.
If you are using npm and Express, we need to set up a public folder for the static content (like your css file):
1) In your root folder, create a folder called 'public', then another one inside called 'css' and place your styles.ccs file in there.
2) In your JS application file (e.g. app.js), we need to have something like this:
//jshint esversion:6
const express = require('express');
const ejs = require("ejs");
const app = express();
app.set('view engine', 'ejs');
app.use(express.static("public"));
app.get('/', (req, res) => {
res.render('index', { foo: 'FOO' });
});
3) In your root folder, create a folder called 'views' and inside place the EJS file you want to render (e.g. index.ejs), stablishing the link like this: <link rel="stylesheet" href="/css/styles.css">:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="/css/styles.css">
<title>Document</title>
</head>
<body>
<h1>Hello World!</h1>
</body>
</html>
In this case, you should be able to see the css code applied in the home route "/", rendering the index.ejs file. I hope it help you!

ASP.NET Core - serve different HTML file for SPA?

Question
How can I serve different HTML (entry) files for an SPA application (Vue) in ASP.NET Core?
Explanation
Depending on a condition, I would like to serve a different HTML page (much like a controller would do for a non-SPA). The page would still include the entry point for Vue apps <div id="app">, but some other changes should be done before serving the HTML.
I know I somehow have to change the startup.cs file because that renders the HTML with app.UseStaticFiles() and app.UseSPAStaticFiles()
Example
Condition 1 is fulfilled, base.html is served from client -> public -> base.html
Condition 2 is fulfilled instead, special.html is served from client -> public -> special.html
Code
The basic HTML looks something like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>Title</title>
</head>
<body>
<noscript>
<strong>We're sorry but this webpage doesn't work properly without JavaScript enabled. Please enable it to
continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
The important parts of startup.cs looks like this:
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
// ....
app.UseStaticFiles();
app.UseSpaStaticFiles();
// ....
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
if (env.IsDevelopment())
{
endpoints.MapToVueCliProxy(
"{*path}",
new SpaOptions { SourcePath = "ClientApp" },
npmScript: "serve",
regex: "Compiled successfully");
}
// Add MapRazorPages if the app uses Razor Pages. Since Endpoint Routing includes support for many frameworks, adding Razor Pages is now opt -in.
endpoints.MapRazorPages();
});
// ....
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
});

Automatically filling certain parameters in an html file on hosted server with node js

I'm working on a project in which I have a simple web server hosted with node.js (see code below) and I want to be able to dynamically load the code form html files and modify them each time someone makes a request. I've already putted some marker in my code ${likeThis} and I just need the code to put a string in the right place.
Here is my server code:
const express = require('express');
const app = express();
app.get('/', function (req, res) {
res.send('Hello World');
})
app.listen(8080);
});
And here is an example page in which I want to change the value ${sampleText} with the plain text "hello world!!!":
<!DOCTYPE html>
<html>
<meta charset="UTF-8">
<head>
<title>Titler</title>
</head>
<body>
${sampleText}
</body>
Mind that there might be more of the same or different kind of value all over the html page.
On the user side I'd expect this:
<!DOCTYPE html>
<html>
<meta charset="UTF-8">
<head>
<title>Titler</title>
</head>
<body>
Hello world!!!
</body>
There are several ways to use live data in pages returned from express. All of them utilize a "template" into which you inject "data". These include:
pug
mustache
handlebars
Another option would be to use NodeJS/ES6 template strings, such as:
const express = require('express')
const app = express()
// Text to insert into template
const sampleText = 'hello world!!!'
// Template to accept sampleText
const result = `<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Titler</title>
</head>
<body>
${sampleText}
</body>
</html>
`
app.get('/', function (req, res) {
res.send(result);
})
app.listen(8080);
Backticks ("`") are used to define template strings in Node where "${expression}" is used to insert any evaluable JavaScript expression into a template, like:
const result = `The contents of file ${filepath} are: ${fs.readFileSync(filepath).toString()}`
For more information, see Using Template Engines with Express
and for an exhaustive list of template engines that work "out of the box" with Express see Template Engines
i ll illustrate with Mustache, you need webpack for communication between front-side and web-server, but since webpack is headache i ll use mustache CDN.
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
</head>
<body>
<div id="message"></div>
//we are gonna render the message into this div tag.
//this is a javascript code
//make sure script tags are at the bottom
<script id="message-template" type="text/html">
<div class="message">
{{message}} . //yes double curly brackets
</div>
</script>
//CDN link, Mustache pack files are stored here and we import from there
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/3.0.1/mustache.min.js"></script>
//for src add path of index.js in your app
<script src="./index.js"></script>
</body>
</html>
index.js
//select the div that you wanna place the message
const $message = document.querySelector("#messages");
//select the script tag in
const messageTemplate = document.querySelector("#message-template").innerHTML;
const html = Mustache.render(messageTemplate, {
//I hardcoded the message here, but as you learn, you will catch a dynamic data, put it here and display in your html page
message:"hello world",
});
$message.innerHTML(html);
After a bit of work(very flew actualy) i monaged to make this function that allows me to find the strings and replace them with the correct text, i will publish it hoping someone else in the future migth need it:
function substituteString(input, stringToChange, substitute) {
var n = 0;
while (true) {
n = input.indexOf(stringToChange, n);
if (n == -1) { break; } else {
input = input.replace(stringToChange ,substitute);
}
}
return input;
}
Easier than i was thinking

AngularJS Node app downloads instead of loading in Internet Explorer

Whenever I try loading my AngularJS webpage (on a node js + express server) through http://localhost:3000, Internet Explorer just tries to download it with a random name and no file extension, instead of showing the actual page.
I can't even check the console for errors, because the download attempt seems to start before even trying to load it as a webpage.
The website works perfectly on both Firefox and Chrome. I am using Internet Explorer 11, so it's not even an old version.
I tried running my index through the W3 Validator, and the only errors it points out in the DOM are the Angular directives and tags (which are not really errors).
I tried changing the DOCTYPE from just a simple <!DOCTYPE html> to this:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-loose.dtd">
And this:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
... and this:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
To no effect.
As suggested in other questions, I also tried serving my page as "text/plain" rather than "text/html" in my node server, but that didn't help either.
Here's the html for my index:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="it">
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<!-- Angular Material CSS now available via Google CDN; version 1.0.7 used here -->
<link rel="stylesheet" href="./modules/angular-material/angular-material.min.css"/>
<link rel="stylesheet" href="./css/style.css"/>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons"/>
<link rel="icon" href="icons/favicon.ico" type="image/x-icon"/>
<base href="/"/>
<title>Area Clienti</title>
<script src="./modules/angular/angular.js" type="text/javascript"></script>
<script src="./modules/ngstorage/ngStorage.js" type="text/javascript"></script>
<script src="./modules/angular-aria/angular-aria.min.js" type="text/javascript"></script>
<script src="./modules/angular-animate/angular-animate.min.js" type="text/javascript"></script>
<script src="./modules/angular-material/angular-material.min.js" type="text/javascript"></script>
<script src="./modules/angular-ui-router/release/angular-ui-router.min.js" type="text/javascript"></script>
<script src="./modules/ui-router-extras/release/modular/ct-ui-router-extras.core.min.js" type="text/javascript"></script>
<script src="./modules/ui-router-extras/release/modular/ct-ui-router-extras.dsr.min.js" type="text/javascript"></script>
<script src="./modules/ui-router-extras/release/modular/ct-ui-router-extras.sticky.min.js" type="text/javascript"></script>
<script src="./modules/pdfmake/build/pdfmake.min.js" type="text/javascript"></script>
<script src="./modules/pdfmake/build/vfs_fonts.js" type="text/javascript"></script>
<script src="./modules/angular-messages/angular-messages.min.js" type="text/javascript"></script>
<script src="http://ngmaterial.assets.s3.amazonaws.com/svg-assets-cache.js" type="text/javascript"></script>
<script src="./js/client.js" type="text/javascript"></script>
</head>
<body ng-app="App" ng-controller="AppCtrl" ng-view ui-view ng-cloak layout="row">
</body>
</html>
The script tags were in the body before, I tried putting them in the head to see if that would have helped, but it didn't.
What might be the issue here? And why is it so painful to make stuff work on IE?
EDIT: here's the server code.
var express = require("express");
var fs = require('fs');
var xml2js = require("./node_modules/xml2js");
var auth = require("./authenticator.js");
var bodyParser = require('body-parser');
var app=express();
//app.use(express.static('pages'));
var server = app.listen(3000, function(){
var host = server.address().address;
var port = server.address().port;
});
var jsonParser = bodyParser.json(); // to support JSON-encoded bodies
var urlencodedParser = bodyParser.urlencoded({ // to support URL-encoded bodies
extended: true
});
app.get('/', function(req, res){
sendFile(res, './index.html', getHeader('text/plain'));
});
app.use('/font/', express.static('./font/'));
app.use('/modules/', express.static('./node_modules/'));
app.use('/css/', express.static('./css/'));
app.use('/views/', express.static('./views/'));
app.use('/img/', express.static('./img/'));
app.use('/js/', express.static('./js/'));
function sendFile(res, path, header){
fs.readFile(path, function(err, content){
if(err) {
console.log("\nErrore caricando " + path + " - err: " + err);
//pageNotFound(res, "error");
}
else sendToClient(content, res, 200, header);
});
}
function getHeader(type){
return {"Content-Type": type};
}
function sendToClient(data, res, code, type){
res.writeHead(code, getHeader(type));
(type === "text/html" || type === "text") ? res.end(data, "utf-8") : res.end(data);
}
Plus a bunch of other POST and GET services which I think are not relevant
UPDATE: I managed to get it to say something in the developer console. This is the error:
(translation: impossible to open http://localhost:3000/)
It still tries to download the page.
UPDATE 2:
I managed to find out what the issue was: for some reason, the Content-Type header content was being interpreted as an object rather than the string it actually was, so IE couldn't render the page.
It now renders the index, but Angular-UI-Router doesn't seem to work.
It appears as though ui-router can't load sub states (it can switch between normal states though, since I was able to log in and be transferred to my dashboard page, which is a different state)
(Everything is still working perfectly fine on Chrome and Firefox)
Could it be sending you a file because you're using .sendFile():
app.get('/', function(req, res){
sendFile(res, './index.html', getHeader('text/plain'));
});
instead of using .render() to render the page?
app.get('/', function (req, res) {
res.render('index.html');
})
Try using Express's use() to always send via sendFile() the index.html after any HTTP action using the * wildcard. Make sure this goes after any other API REST routes or similar. This helps ensure that the Angular app within index.html is not constantly reloaded by other pages being rendered. This way any other API routes send/receive the data you need and only if nothing else is matched, will the index.html be loaded. Below is a structure that worked for me for an Express + Angular 2 application.
File Structure:
api
foo
foo.js
bar
bar.js
public
fonts
css
img
index.html
server.js
Server JS:
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
// fonts, css, images are stored in respective folders within folder "public"
app.use(express.static(path.join(__dirname, 'public')));
// some RESTful endpoints
app.use('/api/foo', fooRoutes);
app.use('/api/bar', barRoutes);
app.use('*', (req, res) => res.sendFile(path.join(__dirname, 'public', 'index.html')));
// error handling middleware
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});

Chrome web store newtab override with http request

To override the chrome web store new tab page I use the following code:
"chrome_url_overrides": {
"newtab": "index.html"
}
I have a backend which serves the html files so instead of using the index.html file I would like to get a html file via a http request.
Is this possible? Or is there a workaround Thanks.
You could make an ajax call from your index page to remote server, and replace the entire html with external html. Sample code looks like the following
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script src="index.js"></script>
</body>
</html>
index.js
var SERVER_URL = "";
var xhr = new XMLHttpRequest();
xhr.onload = function() {
replaceHtml(xhr.responseText);
};
xhr.open("GET", SERVER_URL);
xhr.send();
function replaceHtml(data) {
document.open("text/html");
document.write(data);
document.close();
}
You could simply have some javascript inside a <script></script> tag in your index.html file that grabs your generated html content from a custom domain.