Как добавить дополнительные экспресс-маршруты в рендеринг на стороне сервера для использования в качестве бэкэнда

Найдите внизу Edits.

Я действительно изучил множество руководств и даже документацию по рендерингу на стороне сервера для обучения реакции, расположенную здесь: https://reacttraining.com/react-router/web/guides/server-rendering

Что-то, что было проблемой для меня, это использование других маршрутов, чтобы иметь приложение с полным стеком. Так, например, мы будем использовать стек MERN (mongodb express React/Redux nodejs). Допустим, у нас есть два маршрута в

app.js вот так:

app.use('/users', users);
app.use('/posts', posts);

и наш рендеринг на стороне сервера настроен так же, как и большинство этих руководств, на самом деле я не смог найти ни одного, который бы не настраивал его таким образом, и для экономии времени я просто укажу, где должен быть код SSR. Если вы не знаете, как сделать SSR, посмотрите документы по ссылке, которую я дал выше. Я думаю, что это может быть не нужно, но если это так, дайте мне знать, и я мог бы добавить простую настройку.

Что-то типа:

app.get('*', (req, res) => { //server side rendering code here});

Таким образом, проблема здесь в том, что он затрагивает каждый маршрут, и внутри рендеринга на стороне сервера для React Router 4 большинство людей будут использовать функцию matchRoutes и передавать свои маршруты, которые они используют для стороны реагирования.

Итак, я многое объяснил, но не совсем в чем проблема:

ПРОБЛЕМА: когда на стороне реагирования, скажем, с помощью действия, и мы будем использовать аксиомы для простого примера здесь.

If I do:

axios.get('/posts')
.then(response => { 
 console.log(response.data);
 })
.catch(err => {
 console.log(err);
 });

Это приводит к проблеме. И любой из вас, кто хорошо знает экспресс и SSR, поймет, почему. app.get('*') затрагивает каждый маршрут или, другими словами, каждый запрос маршрута будет проходить здесь, проблема в том, что когда мы отправляем запрос на /posts, происходит один из двух случаев.

Случай 1: в файле маршрутов для ответной стороны есть маршрут на стороне клиента /posts, и в этом случае html на стороне сервера будет возвращен как response.data, что явно не то, что нам нужно.

Случай 2: в файле маршрутов для ответной стороны нет маршрута на стороне клиента /posts, и в этом случае будет возвращена ошибка 404 или что-то похожее на маршрут или страница не найдена, если вы правильно настроили SSR.

Мне нужен способ, чтобы иметь возможность использовать мой сервер, но также иметь возможность использовать SSR для реагирования.

Возможное решение?

  1. Используйте какой-либо тип прокси и скажите, если /api/routename, то используйте прокси, чтобы ваши звонки не проходили через app.get('*').

  2. Или каким-то образом выполнять SSR только на отдельных маршрутах.

  3. Или исключая определенные маршруты, но имея возможность использовать все маршруты на стороне клиента. В этом я имею в виду, что если у меня есть клиентская сторона /users и экспресс-сторона /users, я не хочу, чтобы клиентская сторона ломалась, если я исключаю /users, но каким-то образом все еще исключаю экспресс-сторону.

Я не совсем уверен, как реализовать эти решения, если они возможны, но мне нужно иметь возможность использовать мои экспресс-маршруты, и я не могу найти конкретного способа, как это сделать, или что лучше всего.

РЕДАКТИРОВАНИЕ:

серверная сторона app.js:

require('babel-core/register')({
  presets: ['env', 'react', 'stage-0', 'stage-1']
});

const pkg_json = require('./package.json');
const vertex = require('vertex360')({ site_id: pkg_json.app });
var renderer = require('./renderer.js');

// initialize app
const app = vertex.app();

// import routes
const index = require('./routes/index');
const api = require('./routes/api');
const users = require('./routes/users');

// set routes
app.use('/api/users', users);

// hopefully will be used on every Route, this should handle SSR RR4
app.use(renderer);

module.exports = app;

рендерер.js:

import React from 'react';
import { renderToString } from 'react-dom/server';
import { StaticRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { renderRoutes } from 'react-router-config';
import serialize from 'serialize-javascript';
import { Helmet } from 'react-helmet';
import { matchRoutes } from 'react-router-config';

import routes from './src/routes';
import createStore from './src/stores';

function handleRender(req, res) {
  const store = createStore.configure(null); // create Store in order to get data from redux

  const promises = matchRoutes(routes, req.path)
    .map(({ route }) => {
      // Matches the route and loads data if loadData function is there
      return route.loadData ? route.loadData(store) : null;
    })
    .map(promise => {
      if (promise) {
        return new Promise((resolve, reject) => {
          promise.then(resolve).catch(resolve); // lets all data load even if route fails
        });
      }
    });

  Promise.all(promises).then(() => {
    const context = {};
    if (context.url) {
      return res.redirect(301, context.url); // redirect for non auth users
    }

    if (context.notFound) {
      res.status(404); // set status to 404 for unknown route
    }

    const content = renderToString(
      <Provider store={store}>
        <StaticRouter location={req.path} context={context}>
          <div>{renderRoutes(routes)}</div>
        </StaticRouter>
      </Provider>
    );

    const initialState = serialize(store.getState());

    const helmet = Helmet.renderStatic();

    res.render('index', { content, initialState, helmet });
  });
}

module.exports = handleRender;

Точка входа в реакцию:

import React from 'react';
import { renderToString } from 'react-dom/server';
import { StaticRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { renderRoutes } from 'react-router-config';
import serialize from 'serialize-javascript';
import { Helmet } from 'react-helmet';
import { matchRoutes } from 'react-router-config';

import routes from './src/routes';
import createStore from './src/stores';

function handleRender(req, res) {
  const store = createStore.configure(null); // create Store in order to get data from redux

  const promises = matchRoutes(routes, req.path)
    .map(({ route }) => {
      // Matches the route and loads data if loadData function is there
      return route.loadData ? route.loadData(store) : null;
    })
    .map(promise => {
      if (promise) {
        return new Promise((resolve, reject) => {
          promise.then(resolve).catch(resolve); // lets all data load even if route fails
        });
      }
    });

  Promise.all(promises).then(() => {
    const context = {};
    if (context.url) {
      return res.redirect(301, context.url); // redirect for non auth users
    }

    if (context.notFound) {
      res.status(404); // set status to 404 for unknown route
    }

    const content = renderToString(
      <Provider store={store}>
        <StaticRouter location={req.path} context={context}>
          <div>{renderRoutes(routes)}</div>
        </StaticRouter>
      </Provider>
    );

    const initialState = serialize(store.getState());

    const helmet = Helmet.renderStatic();

    res.render('index', { content, initialState, helmet });
  });
}

module.exports = handleRender;

person Taylor Austin    schedule 10.11.2017    source источник
comment
Лично я добавляю ко всем своим API-маршрутам префикс /api. Кроме того, если вы поместите свой маршрут * в конце, после всех других маршрутов, то все ваши маршруты API не будут достигать маршрута *, до них будут доходить только запросы ssr.   -  person VivekN    schedule 13.11.2017
comment
Спасибо, я попробую и дам вам знать, что я найду завтра   -  person Taylor Austin    schedule 13.11.2017
comment
@VivekN Я ​​получаю сообщение об ошибке window is not defined, когда я помещаю свои маршруты перед SSR всякий раз, когда я им звоню.   -  person Taylor Austin    schedule 13.11.2017
comment
пожалуйста, опубликуйте весь свой экспресс-файл index.js, чтобы я мог проверить его один раз.   -  person VivekN    schedule 13.11.2017
comment
Я сделал отдельный пост для этого, но я не думаю, что могу связать здесь свой другой пост, я продолжу и добавлю экспресс-файл для вас, хотя я также добавил точку входа реакции, после некоторого обсуждения с товарищами по команде они поверьте, проблема именно в этом.   -  person Taylor Austin    schedule 13.11.2017


Ответы (1)


Благодаря @VivekN вы делаете это так, чтобы убедиться, что ваш app.get('*') находится ниже каждого из объявленных маршрутов. Это должно позволить сначала пройти другие маршруты.

person Taylor Austin    schedule 14.11.2017