Файл index.html NodeJS Express Host затем направляется к определенному маршруту Spa с параметрами

У меня есть приложение, которое использует Angular 13 в качестве внешнего интерфейса и nodeJS с Express для внутреннего интерфейса. Бэкэнд-серверы файлов внешнего интерфейса. Я работаю над добавлением поддоменов в свое приложение, и, хотя у меня это в некоторой степени получилось, есть пара проблем, с которыми я столкнулся, и я тоже не могу найти решение.

Я сделал большую часть этого, но проблема, похоже, в том, что когда я отправляю index.html с помощью функции sendFile(), мне удается изменить URL-адрес на URL-адрес Auth/Auth с параметрами, но веб-сайт этого не делает. работаю и получаю следующие ошибки:

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the clientThis page isn’t working "domain" redirected you too many times.

Недавно я представил субдомены, и мой server.js:

const swagger = express();
var app = express();
//apiRoutes.use(enforce.HTTPS());
app.use(express.static(path.join(__dirname, '/dist/App'), { dotfiles: 'allow' }));

allRoutes.use(bodyParser.json({limit: '50mb'}));
allRoutes.use(bodyParser.urlencoded({limit: '50mb', extended: true}));
allRoutes.use(cors());

// use JWT auth to secure the api
if (process.env.NODE_ENV !== "production" || process.env.ENV === "dev") {
    swagger.use('/', swaggerUi.serve, swaggerUi.setup(swaggerFile, { swaggerOptions: { persistAuthorization: true }}));
    app.use('/swagger', swaggerUi.serve, swaggerUi.setup(swaggerFile, { swaggerOptions: { persistAuthorization: true }}));
}
allRoutes.use('/webhooks', require('./backend/webhooks/webhooks.controller'));
allRoutes.use(subdomain('webhooks', require('./backend/webhooks/webhooks.controller')));

allRoutes.use('*', jwt());
// allRoutes.use(subdomain('api',  jwt()));
// api routes
allRoutes.use('/api/users', require('./backend/users/users.controller'));

// global error handler
// start server
app.use(subdomain('swagger', swagger));
// app.use(subdomain('webhooks', require('./backend/webhooks/webhooks.controller')));
app.use('/swagger', swagger);
app.get(/^/(?!api).*/,async function(req,res) {
    let isNonExcludedUrl = !req.url.includes('swagger') && !req.subdomains.includes('swagger') &&!req.url.includes('webhooks') && !req.subdomains.includes('webhooks');
    console.info('subdomain ', req.subdomains);
    if (req.subdomains.includes('webhooks')) {
        console.info('in webhooks')
    } else if (req.subdomains.length > 0 && isNonExcludedUrl) {
        console.info('processing website subdomain ');
        await website.checkSubdomainForWebsite(req, res)
    } else if (isNonExcludedUrl) {  //&& !req.url.includes('webhooks')
        res.sendFile(path.join(__dirname + '/dist/App/index.html'));
    }
});
app.use('/', allRoutes);
allRoutes.use(errorHandler);
app.use(errorHandler);

const port = process.env.PORT ? (process.env.PORT) : 4000;
var server = http.createServer(options, app);

server.listen(port, () => {
    console.info("server starting on port : " + port)
});

Функция checkSubdomainForWebsite:


async function checkSubdomainForWebsite(req,res) {
    let orgIdent = req.subdomains[0];
   
    if (orgIdent) {
       let qrDevice = await getOrCreateWebsiteDevice(orgIdent);

        let websiteUrl =  `/Auth/Auth`;

        res.sendFile(path.join(__dirname+'/dist/DineNGo/index.html'));
        res.redirect(url.format({
            pathname: websiteUrl, // assuming that index.html lies on root route
            query: {
                "qr": encodeURIComponent(encrypt(orgIdent)),
                "website": true
            }
        }));
    }
}

Ожидаемое поведение: когда вызывается функция checkSubdomainForWebsite(), сразу после вызова res.sendFile() и загрузки index.html я хочу перенаправиться на определенную страницу Angular SPA с параметрами запроса. URL-адрес конкретного компонента Angular, по которому я хочу перейти, — это Auth/Auth.

Изучив это, я обнаружил, что res.sendFile() и res.redirect не следует использовать вместе, и я почти уверен, что именно это вызывает проблему, но я не могу найти альтернативный подход, который бы работал. Как лучше всего разместить файл index.html и направить его к определенному компоненту в интерфейсе Angular через маршруты. Ниже я предоставил свой service.js и другие функции. Пожалуйста, дайте мне знать, если потребуется дополнительная информация, и спасибо за помощь.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
72
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Nginx — правильный выбор для обслуживания веб-интерфейса, тогда как node.js обычно используется только для обслуживания серверных API. Nginx превосходно справляется с эффективным обслуживанием статического контента, такого как HTML, CSS, JavaScript и файлов изображений. Nginx может действовать как обратный прокси-сервер, перенаправляя клиентские запросы на внутренние серверы, в вашем случае API-интерфейсы node.js.

#Nginx config
server {
    listen 80;
 
    server_name example.com; # your domain name or ip address
 
    location / {
        root /path/to/your/angular/dist; # your real dist path
        try_files $uri $uri/ /index.html;
    }
    location /api {
        proxy_pass http://localhost:4000;
    } 
    location /webhooks {
        proxy_pass http://localhost:4000;
    } 
    location /swagger {
        proxy_pass http://localhost:4000;
    } 
}

и ваш Node.js должен слушать 4000.

Спасибо за ответ. Можете ли вы привести пример или показать мне, как я могу изменить свой код для реализации такого поведения? На самом деле мне нужно обратное: мне нужен сервер для загрузки angular index.html, но с определенным маршрутом к компоненту. Будет ли Nginx это поддерживать?

coder123 18.07.2024 06:57

Я добавил краткую конфигурацию Nginx. Пожалуйста, обратитесь к нему.

dunhuang 20.07.2024 15:39
Ответ принят как подходящий

Да, перенаправление и отправку файла нельзя использовать вместе. Итак, вы можете отправить файл, а затем продолжить работу с AJAX/Angular.

Вы можете попробовать сначала перенаправить с помощью строки запроса, но включите в строку запроса некоторый флаг, который обработчик будет читать при перенаправлении, и по нему будет известно, что теперь ему нужно отправить файл (или, возможно, в переменную памяти в сочетании с другими проверками), (это делает два запроса и запускает другое промежуточное программное обеспечение и может вызвать слишком много перенаправлений, поэтому было бы лучше обрабатывать это во внешнем интерфейсе), например:

добавить флаг в qs:

"redirected": true

читай каждый раз:

if (req.query.redirected) return res.sendFile(path.join(__dirname+'/dist/DineNGo/index.html'));

код:

async function checkSubdomainForWebsite(req,res) {

    let orgIdent = req.subdomains[0];

    // add some flag to make sure it's redirect in combination with other checks
    if (req.query.redirected) return res.sendFile(path.join(__dirname+'/dist/DineNGo/index.html'));
   
    if (orgIdent) {
       let qrDevice = await getOrCreateWebsiteDevice(orgIdent);

        let websiteUrl =  `/Auth/Auth`;
        // redirect first
        res.redirect(url.format({
            pathname: websiteUrl, // assuming that index.html lies on root route
            query: {
                "qr": encodeURIComponent(encrypt(orgIdent)),
                "website": true,
                "redirected": true
            }
        }));
    }
}

Это сработало на удивление хорошо. Просто интересно, есть ли недостатки у этого подхода?

coder123 20.07.2024 17:27

наряду с указанным, возможно, ваш SPA зависит от сервера, поэтому я бы попробовал обработать его на внешнем интерфейсе, например, просто отправить файл по этому маршруту, а затем получить URL-адрес запроса с помощью службы angular http, перенаправить с помощью маршрутизатора, поделитесь им с компонентами и т. д.

traynor 20.07.2024 21:36

Как известно, цикл запрос-ответ может иметь только один запрос и один ответ. Поэтому res.sendFile и последующее res.redirect в одном и том же цикле являются нарушением и выдают сообщение об ошибке.

Ниже приведен обходной путь.

Минимальный код ниже адресует то же самое с помощью JavaScript в браузере. Но здесь есть и проблема. Этот тип кода отключает кнопку возврата. Мне еще предстоит найти решение этой конкретной проблемы - ломается кнопка назад. Когда пользователь нажимает кнопку «Назад», он не переходит на страницу index.html. Если кто-то найдет решение этой проблемы, это может быть одним из способов обхода этого вопроса.

сервер.js

const express = require('express');
const app = express();

app.get('/', (req, res) => {
  res
    .cookie('somecookie', 'somevalue')
    .sendFile(__dirname + '/' + './index.html');
});

app.get('/route1', (req, res) => {
  console.info(req.query);
  res.send('route1 navigation automated from the root route');
});

app.listen(3000, console.info('L@3000'));

index.html

<!DOCTYPE html>
<html>
  <head>
    Navigation automated to another route on load.
  </head>
  <body>
    <br />
    <br />
    <a href = "./route1">A test route</a>
  </body>
  <script async>
    history.pushState({}, '', window.location.href);
    //for testing, assumed there is no other cookies
    const cookie = document.cookie;
    window.location.href = './route1?' + cookie;
    // to avoid unnecessary redirection, this cookie 
    // needs to be deleted once the redirection is done
    // { code to delete the cookie }
  </script>
</html>

Результаты теста:

URL : http://localhost:3000
Redirected with the URL : http://localhost:3000/route1?somecookie=somevalue

server console : 
{ somecookie: 'somevalue' }

Другие вопросы по теме