问题:localhost:3000/cart,出现错误为 localhost:3000/api/products/cart 404(未找到)

我正在开发一个电子商务网站并在其上添加“购物车”路线,但出现以下错误:错误图像本地主机地址为:3000/cart,但错误地址来自:3000/api /products/cart.(我正在使用 express 从:http://localhost:5000/api/products 获取数据)

另外,如果我将地址输入为:“http://localhost:3000/cart/223”,则错误为:

GET http://localhost:3000/cart/api/products/cart 404   (Not Found)

应用程序.js:

function App() {
  return (
    <Switch>
      <Route exact path="/" component={HomePage} />
      <Route exact path="/products" component={ProductPage}/ >
      <Route path="/:id" component={ProductDetail} />
      <Route path="/cart" component={CartPage} />
      <Route path="/cart/:id" component={CartPage} />
    </Switch>
  )
}

export default App;

购物车页面.jsx:

import React, { useEffect } from "react";
import { addToCart } from "../actions/cartActions";
import { useDispatch } from "react-redux";

function CartPage(props) {
  const productId = props.match.params.id;
  const qty = props.location.search? Number(props.location.search.split("=")[1]): 1;
  const dispatch = useDispatch();

  useEffect(() => {
    if (productId) {
      dispatch(addToCart(productId, qty));
    }
  }, []);

  return <div>Hell world!</div>;
}

export default CartPage;

productDetail.jsx:

import React, { useState, useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { detailsProduct } from "../actions/productActions";

function ProductDetail(props) {
  const [qty, setQty] = useState(1);
  const productDetails = useSelector((state) => state.productDetails);
  const { product, loading, error } = productDetails;
  const dispatch = useDispatch();

  useEffect(() => {
    dispatch(detailsProduct(props.match.params.id));
    return () => {//};
  }, []);

  const handleAddToCart = () => {
    props.history.push("/cart/" + props.match.params.id + "?qty=" + qty);
  };

  return ({product.countInStock > 0 && (
    <button onClick={handleAddToCart}>
      Add to Cart
    </button>
  )})

http://localhost:5000/api/products:

[
  {
     name: "Slim Shirt",
     category: "Shirts",
     image: "/img/d2.png",
     price: 60,
     brand: "Nike",
     rating: 4.5,
     numReviews: 10,
     _id: "123",
     countInStock: 0,
   },
   {
     name: "Best Shirt",
     category: "Shirts",
     image: "/img/d3.png",
     price: 50,
     brand: "ads",
     rating: 4.6,
     numReviews: 12,
     _id: "223",
     countInStock: 6,
   },
   ....}

server.js:

import express from "express";
import { data } from "./data";

const app = express();

app.get("/api/products/:id", (req, res) => {
  const product = data.find((x) => x._id === req.params.id);

  if (product) {
    res.send(product);
  } else {
    res.status(404).send({ msg: "Product Not Found!!"     });
  }
});

app.get("/api/products", (req, res) => {
  res.send(data);
});

app.listen(5000, () => {
  console.log("server started at http://localhost:5000");
});

包.json:

{
 "name": "frontend",
 "proxy": "http://127.0.0.1:5000",
 "version": "0.1.0",
 "private": true,
 "dependencies": {
    .....}

//store.js:

import { createStore, combineReducers, compose, 
applyMiddleware } from "redux";
import {
productDetailsReducer,
productListReducer,
} from "./reducers/productReducers";
import thunk from "redux-thunk";
import { cartReducer } from "./reducers/cartReducers";

const initialState = {};
const reduer = combineReducers({
productList: productListReducer,
productDetails: productDetailsReducer,
cart: cartReducer,
});

const composeEnhancer = 
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

const store = createStore(
reduer,
initialState,
composeEnhancer(applyMiddleware(thunk))
);
export default store;

//productActions.js:

const listProducts = () => async (dispatch) => {
  try {
    dispatch({ type: PRODUCT_LIST_REQUEST });
    const { data } = await axios.get("api/products");
    dispatch({ type: PRODUCT_LIST_SUCCESS, payload: data 
    });
 } catch (error) {
   dispatch({ type: PRODUCT_LIST_FAIL, payload: 
   error.message });
  }
 };

const detailsProduct = (productId) => async (dispatch) => {
  try {
   dispatch({ type: PRODUCT_DETAILS_REQUEST, payload: 
   productId });
   const { data } = await axios.get("api/products/" + 
     productId);
   dispatch({ type: PRODUCT_DETAILS_SUCCESS, payload: data 
   });
   } catch (error) {
   dispatch({ type: PRODUCT_DETAILS_FAIL, payload: 
  error.message });
    }
  };

  export { listProducts, detailsProduct };

解答

问题

问题似乎从Switch组件呈现的路由顺序开始。Switch中的路由路径顺序和特殊性很重要。"/:id"路由路径比"/cart"less 特定,"/cart""/cart/:id"less 特定,因此它将由Switch匹配和渲染。

<Switch>
  <Route exact path="/" component={HomePage} />
  <Route exact path="/products" component={ProductPage} />
  <Route path="/:id" component={ProductDetail} /> // matches "/cart"
  <Route path="/cart" component={CartPage} />     // unreachable route!!
  <Route path="/cart/:id" component={CartPage} /> // unreachable route!!
</Switch>

最后两条路线无法到达。当路径为"/cart"并发出无效请求时,会呈现错误的组件。

ProductDetail 调度一个可能意味着使用正确 id 发出获取请求的操作,但"cart"id值,路径"/cart""/:id"路由匹配。

useEffect(() => {
  dispatch(detailsProduct(props.match.params.id));
  return () => {};
}, []);

解决方案

以路径特异性的相反顺序重新排序路由。在大多数情况下,这样做也完全消除了使用exact属性填充所有路由的需要。

例子:

<Switch>
  <Route path="/cart/:id" component={CartPage} />
  <Route path="/cart" component={CartPage} />
  <Route path="/products" component={ProductPage} />
  <Route path="/:id" component={ProductDetail} />
  <Route path="/" component={HomePage} />
</Switch>

请注意,"/cart/:id"more"/cart""/products""/:id"更具体,并且"/cart""/products"都_more_ 比"/:id"更具体,并且它们_all_ more 具体比上一条路径路径"/"

另请注意,react-router-dom@5允许Route组件使用path属性的路径数组。请记住,数组中的顺序和特异性仍然很重要。还要记住,数组中的 all 路由应该比下面/之后的路由路径更具体。

例子:

<Switch>
  <Route path={["/cart/:id", "/cart"]} component={CartPage} />
  <Route path="/products" component={ProductPage} />
  <Route path="/:id" component={ProductDetail} />
  <Route path="/" component={HomePage} />
</Switch>

API请求

对于请求,请尝试使用绝对路径,即axios.get("/api/products/" + productId),因此不会从"/products"子路由发出请求。您希望 API 请求路径类似于"/api/products/123"而不是"/products/api/products/123"

例子:

const listProducts = () => async (dispatch) => {
  try {
    dispatch({ type: PRODUCT_LIST_REQUEST });
    const { data } = await axios.get("/api/products");
    dispatch({ type: PRODUCT_LIST_SUCCESS, payload: data });
  } catch (error) {
    dispatch({ type: PRODUCT_LIST_FAIL, payload: error.message });
  }
};

const detailsProduct = (productId) => async (dispatch) => {
  try {
    dispatch({ type: PRODUCT_DETAILS_REQUEST, payload: productId });
    const { data } = await axios.get("/api/products/" + productId);
    dispatch({ type: PRODUCT_DETAILS_SUCCESS, payload: data });
  } catch (error) {
    dispatch({ type: PRODUCT_DETAILS_FAIL, payload: error.message });
  }
};

export { listProducts, detailsProduct };
Logo

React社区为您提供最前沿的新闻资讯和知识内容

更多推荐