Да, я знаю, что это старый вопрос, но я надеюсь, что это поможет кому-то еще.Ответ о Angular 6 и .NET Core 2.1.Совет: объяснение немного большое (извините)
В Core 2.1 у нас есть типичный Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
spa.Options.DefaultPage = $"/index.html";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
Важной частью является
spa.UseAngularCliServer(npmScript: "start");
это говорит .NET, что используется скрипт, определенный в package.json
{
"name": "client-app",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve --base-href=/ --serve-path=/", //<--this line
"build": "ng build",
...
},
....
}
Убедитесь, что у нас есть два варианта.--base-href и --serve-path
Когда мы выполняем приложение, мы получаем index.html наподобие
<!DOCTYPE html>
<html>
<head>
<title>Angular i18n example</title>
<base href="/"
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<app-root>Loading...</app-root>
<script type="text/javascript" src="runtime.js"></script>
<script type="text/javascript" src="polyfills.js"></script>
<script type="text/javascript" src="styles.js"></script>
<script type="text/javascript" src="vendor.js"></script>
<script type="text/javascript" src="main.js"></script>
</body>
</html>
Мы можем изменить базовый href, измените наш package.json,Но если не изменить --serve-path в соответствии, наше приложение завершится сбоем, потому что мы ищем сценарий в соответствии с базовым href.
Идея состоит в том, что сценарии не зависят от базового href.Я хочу, чтобы сценарий скриптов был в абсолютном пути.То есть сценарии становятся такими, как
<script type="text/javascript" src="/<serve-path>/runtime.js"></script>
<script type="text/javascript" src="/<serve-path>/polyfills.js"></script>
<script type="text/javascript" src="/<serve-path>/styles.js"></script>
<script type="text/javascript" src="/<serve-path>/vendor.js"></script>
<script type="text/javascript" src="/<serve-path>/main.js"></script>
Итак, если мы напишем в нашем .html что-то вроде
<head>
<title>Angular i18n example</title>
<script>
var str = document.location.href;
...
document.write('<base href="' + str+ '" />');
</script>
<!--we remove base href="/"
<base href="/"
-->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
Наши сценарии ищут правильный путь, и приложение работает.Change Как изменить способ добавления сценариев?Что ж, нам нужно внести несколько изменений в два файла:
index-html-webpack-plugin.js из node_modules \ @ angular-devkit \ build-angular \ src \ angular-cli-файлы \ плагины
browser.js из node_modules \ @ angular-devkit \ build-angular \ src \ angular-cli-files \ models \ webpack-configs
Сначала посмотрите класс IndexHtmlWebpackPlugin в index-html-webpack-plugin.js
class IndexHtmlWebpackPlugin {
constructor(options) {
this._options = Object.assign({ input: 'index.html', output: 'index.html', entrypoints: ['polyfills', 'main'], sri: false }, options);
}
apply(compiler) {
....
const scriptElements = treeAdapter.createDocumentFragment();
for (const script of scripts) {
const attrs = [
{ name: 'type', value: 'text/javascript' },
//change the line
//{ name: 'src', value: (this._options.deployUrl || '') + script },
//by
{ name: 'src', value: (this._options.baseHref)+(this._options.deployUrl || '') + script },
];
if (this._options.sri) {
const content = compilation.assets[script].source();
attrs.push(...this._generateSriAttributes(content));
}
const element = treeAdapter.createElement('script', undefined, attrs);
treeAdapter.appendChild(scriptElements, element);
}
Посмотрите, как мы добавляем this._options.baseHref в сценарии. Ну, действительно, в конструкторе мыне имеет baseHref, это потому что мы должны изменить файл browser.js в
plugins: extraPlugins.concat([
new index_html_webpack_plugin_1.IndexHtmlWebpackPlugin({
input: path.resolve(root, buildOptions.index),
output: path.basename(buildOptions.index),
baseHref: buildOptions.baseHref,
entrypoints: package_chunk_sort_1.generateEntryPoints(buildOptions),
deployUrl: buildOptions.deployUrl,
//we add this line
servePath: buildOptions.servePath,
sri: buildOptions.subresourceIntegrity,
}),
]),
Еще один шаг.Поскольку мы пишем базовый тег с использованием javascript в нашем index.html, мы не хотим, чтобы angular-cli добавил этот тег.Для этого единственное, что мы должны изменить, это закомментировать строки в index-html-webpack-plugin.js
// Adjust base href if specified
if (typeof this._options.baseHref == 'string') {
let baseElement;
for (const headChild of headElement.childNodes) {
if (headChild.tagName === 'base') {
baseElement = headChild;
}
}
const baseFragment = treeAdapter.createDocumentFragment();
if (!baseElement) {
baseElement = treeAdapter.createElement('base', undefined, [
{ name: 'href', value: this._options.baseHref },
]);
//coment the two lines below
// treeAdapter.appendChild(baseFragment, baseElement);
// indexSource.insert(headElement.__location.startTag.endOffset + 1, parse5.serialize(baseFragment, { treeAdapter }));
}
И это все !!
Еще две интересные вещи
Когда мы создаем базовый тег с использованием javascript, нам нужно настроить базовый тег.Иначе, если мы обновим страницу, наше базовое href изменится, и, если мы, например, на домашней странице, наш URL будет выглядеть как http: // www.dominio.com/ <что-то> / <что-то> / home.Мы хотим, чтобы база продолжала <что-то> / <что-то>.взгляните на сценарий и точно так же, как вы хотите, например:
<script>
var items = document.location.href.split('/');
while (items.length>4)
items.pop();
document.write('<base href="' + items.join('/') + '" />');
</script>
Последнее замечание, если сделать этот единственный действительный URL-адрес, это сервер как SPA.Для этого мы изменили наш startup.cs, чтобы единственным действительным URL был сервер.(иначе, некоторые URL, такие как http://www.dominio.com/patata/patata обслуживают наш SPA) Добавить между app.UseMvc и app.UseSpa a Middleware
app.UseMvc(routes =>
{
...
});
//Add this Middleware
app.Use(async (context, next) =>
{
string path = context.Request.Path.Value.Substring(1);
bool encontrado = path.EndsWith(".js") || path.EndsWith(".css") || path.EndsWith(".map") || path.StartsWith("sockjs");
if (!encontrado)
{
//make here the check for the pages
//"silly" e.g.
encontrado==(path=="es/adminitrator" || path=="it/administrator")
}
if (encontrado)
await next.Invoke();
else
{
context.Response.ContentType = "text/html";
await context.Response.SendFileAsync(Path.Combine(env.WebRootPath, "error404.html"));
}
app.UseSpa(spa=>
{
....
}
});
Обновление
ИспользованиеJava-скрипт для добавления базы вкладок - это действительно уродливый обходной путь.Лучше использовать BASE_REF Provider
import {Component, NgModule} from '@angular/core';
import {APP_BASE_HREF} from '@angular/common';
@NgModule({
providers: [{provide: APP_BASE_HREF, useValue: '/my/app'}]
})
class AppModule {}