使用 react-router 表达状态 404

Posted

技术标签:

【中文标题】使用 react-router 表达状态 404【英文标题】:Express status 404 with react-router 【发布时间】:2016-06-02 03:16:45 【问题描述】:

我有一个快速服务器来处理:1 个 API 路由并呈现我的初始 index.html 以包含 bundle.js 持有我的 React/React-Router/Redux 应用程序。

就目前而言,我无法在我的网页上设置 404,因为我已经掌握了所有信息:

app.use(function (req, res) 
  return res.render('index')
)

为了让react-routerNoMatch 工作,我需要发送一个 404 代码。

我的路线如下:

快递——/api/test/:x/:y

React 路由器 — :x/, :x/:y

我实际上想要实现的是,如果用户访问的 URL 为::x/:y/z/and/further,则返回 404,除非他们访问的是 /api/test/:x/:y p>

问题:

    如何匹配路由(不包括我的 API 路由),最好以可扩展的方式返回适当的状态代码? 对于这么简单的事情,在子域上设置它是否有很大的开销?这甚至会缓解这个问题吗?当应用增长时,我会遇到问题吗?

【问题讨论】:

你可能还想看看 react-project 是如何处理这个问题的:github.com/ryanflorence/react-project/blob/v0.0.30/modules/… 【参考方案1】:

对此我有一个更愚蠢但更简单的解决方案。我让我的应用程序在服务器端呈现任何 URL,如果不匹配 react-router 将呈现我的 404 页面。我只是检查我的响应文本是否有任何 404 相关,比如选择器或其他东西。如果是这样,我将发送带有 404 标头的内容。

请注意,这远不是一个好的解决方案,但它很便宜。

【讨论】:

【参考方案2】:

查看 react-router 服务器端渲染文档: reacttraining.com/react-router/web/guides/server-rendering

解决方案:

    将路由提取到单独的文件并在 express 应用中需要它 在 express 应用程序中添加一个中间件,使用来自 react-routermatch 函数检查 express 中的 url。它应该写在负责 API 路由的中间件之后。 如果请求 url 没有合适的路由,则使用 404 响应。

所以,中间件应该是这样的:

// origin code from https://github.com/reactjs/react-router/blob/master/docs/guides/ServerRendering.md
// this is ES6, but easily can be written on a ES5.

import  match, RouterContext  from 'react-router'
import routes from './routes' 

var app = express();

// ...

app.use((req, res, next) => 
  match( routes, location: req.url , (error, redirectLocation, renderProps) => 
    if (error) 
      res.status(500).send(error.message)
     else if (redirectLocation) 
      res.redirect(302, redirectLocation.pathname + redirectLocation.search)
     else if (renderProps) 
         // You can also check renderProps.components or renderProps.routes for
        // your "not found" component or route respectively, and send a 404 as
        // below, if you're using a catch-all route.

        // Here you can prerender component or just send index.html 
        // For prependering see "renderToString(<RouterContext ...renderProps />)"
        res.status(200).send(...)
     else 
      res.status(404).send('Not found')
    
  )
);

如果任何路线发生变化,您无需在 express 应用上执行任何操作,因为您在前端和后端使用相同的代码。

【讨论】:

reacttraining.com/react-router/web/guides/server-rendering -- 为未来的读者更新链接 您的链接是 404。 reacttraining.com/react-router/web/guides/server-rendering 是当前链接。【参考方案3】:

我设法让我的 React 应用程序使用 React Router v4 与运行liquid.js 的快速服务器一起工作,类似于车把,该解决方案对于任何其他模板引擎的工作方式都相同。

在你的 React App.js 中确保你已经安装和设置了 React Router v4:

import React,  Component  from 'react';
import  TransitionGroup, CSSTransition  from "react-transition-group";
import 'foundation-sites';
import 
  BrowserRouter as Router,
  Route,
  Switch
 from "react-router-dom";
import './assets/css/foundation.min.css';
import './assets/css/settings.scss';
import './assets/css/structure.css';
import './assets/css/owl-slider.css';
import './assets/fonts/fontawesome/all.css'; 


// ## COMPONENTS
import Header from './components/Header/header';
import Footer from './components/Footer/footer';

// ## PAGES 
import HomePage from './components/Pages/Home/homePage';
import AboutPage from './components/Pages/About/aboutPage';
import ServicesPage from './components/Pages/Services/services';
import ContactPage from './components/Pages/Contact/contact';


class App extends Component 


  render() 
    return (
      <Router>
        <div className="App page-a blur" id="page" data-toggler=".blur" >
          <Header/>
          <div className="pageWrapper">
          <Route render=( location ) => (
              <TransitionGroup>
                <CSSTransition key=location.key classNames="pageTransition" timeout=500>
                  <Switch location=location>
                    <Route exact path="/" exact component=HomePage />
                    <Route path="/services" render=props => <ServicesPage ...props/> />
                    <Route path="/about" component=AboutPage />
                    <Route path="/contact" component=ContactPage />
                    <Route render=() => <div>Not Found</div> />
                  </Switch>
                </CSSTransition>
            </TransitionGroup>
          )/>
          </div>
          <Footer footerMessage="Liliana Alves // Sport &amp; Soft Tissue Therapist"/>
        </div>
      </Router>
    );
  


export default App;

上面的代码将确保当用户浏览你的 React 应用程序时,路由正在执行他们的工作,将用户发送到他们导航、刷新或手动输入 URL 的页面。

在您的快速服务器 app.js 中,您想为您的反应应用程序定义主访问根“/”“不要使用通配符 *(星号),这将不起作用!” :

app.get('/', (req, res) => 

        res.status(200).sendFile(path.join(__dirname+'/react-site/build/index.html'));

); //THE REQUEST CONTENT

然后,如果您的 express 中有 404,它会将您的用户重定向回 React 以使用 React Router 处理 404,此方法是使用 express 错误处理程序完成的:

app.use((req, res, next) => 
    const error = new Error('Not Found'); //Error object
    error.status = 404;

    //res.render('./404'); by default in express applications you would render a 404 page

    res.status(200).sendFile(path.join(__dirname+'/react-site/build/index.html'));

    next(error);

);

如果有人认为这应该改进或者它可能需要对错误功能的更多支持,请告诉我。

【讨论】:

【参考方案4】:

听起来你可以利用溃败的顺序来发挥自己的优势。

app.get('/api/test/:x/:y', function())

app.get('/:x/:y', function())

app.get('/:x/:y/:err', function(res, req) res.status(404).send('oops') )

这样请求将尝试/api/test 然后/something/something 然后如果它的/something/something/something-else 它将失败。

【讨论】:

【参考方案5】:

express 中的routermiddleware,因此,在定义路线时,顺序非常重要。为了分离 API 路由,您可以创建一个模块来处理这些路由,然后创建 middlewares 以捕获其他路由,包括用于 404 响应的路由。不要忘记将 api 路由放在首位。这是一个例子:

var express = require('express');
...

var routes = require('./routes/api');
var app = express();
...

// handle the request for the API
app.use('/api', routes);
...

// catch 404 and forward to error handler
app.use(function(req, res, next) 
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
);

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') 
  app.use(function(err, req, res, next) 
    res.status(err.status || 500);
    res.render('error', 
      message: err.message,
      error: err
    );
  );


// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) 
  res.status(err.status || 500);
  res.render('error', 
    message: err.message,
    error: 
  );
);


module.exports = app;

还有api路由模块:

var express = require('express');
var router = express.Router();

/* GET test page. */
router.get('/test/:x/:y', function(req, res, next) 
  res.render('test',  x: req.params.x, y: req.params.y );
);

module.exports = router;

【讨论】:

【参考方案6】:

你需要将你的实际 API 路由放在你的包罗万象之上,以便它们在它之前被拾取。

基本上,首先定义的中间件优先于路由复杂性。

一旦向客户端发送响应,Express 将停止处理中间件,除非抛出错误或出于某种原因手动调用 next

您可以像这样定义一个简单的错误处理程序:

app.use(function catchError(req, res, next, err) 
    console.error('Caught error', err);
    res.status(500).json(
        error: err
    );
);

【讨论】:

以上是关于使用 react-router 表达状态 404的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 react-router 维护状态

在将 react-router 5 与 redux 7 一起使用时,react-router <Link> 在转到新路由后不会重置状态

检查 react-router 路径是不是处于活动状态

react-router 如何使用 redirectLocation 或 301 或 302 状态

如何使用 react-router 在组件中保存表单的状态

webpack-dev-server react-router 推送状态