Next.js - 错误:仅支持绝对URL

时间:2017-06-03 09:02:58

标签: reactjs react-redux next.js

我正在使用express作为next.js的自定义服务器。当我点击产品到产品列表时,一切都很好

第1步:点击产品链接

enter image description here

第2步:它会在数据库中显示产品。

enter image description here

但是,如果我刷新/products页面,我会收到此错误

enter image description here

服务器代码(查看/products端点)

app.prepare()
.then(() => {
  const server = express()

  // This is the endpoints for products
  server.get('/api/products', (req, res, next) => {
    // Im using Mongoose to return the data from the database
    Product.find({}, (err, products) => {
      res.send(products)
    })
  })

  server.get('*', (req, res) => {
    return handle(req, res)
  })

  server.listen(3000, (err) => {
    if (err) throw err
    console.log('> Ready on http://localhost:3000')
  })
})
.catch((ex) => {
  console.error(ex.stack)
  process.exit(1)
})

Pages - products.js(将循环产品json数据的简单布局)

import Layout from '../components/MyLayout.js'
import Link from 'next/link'
import fetch from 'isomorphic-unfetch'

const Products = (props) => (
  <Layout>
    <h1>List of Products</h1>
    <ul>
      { props.products.map((product) => (
        <li key={product._id}>{ product.title }</li>
      ))}
    </ul>
  </Layout>
)

Products.getInitialProps = async function() {

  const res = await fetch('/api/products')
  const data = await res.json()

  console.log(data)
  console.log(`Showed data fetched. Count ${data.length}`)

  return {
    products: data
  }
}

export default Products

10 个答案:

答案 0 :(得分:7)

如错误所述,您必须使用您正在制作的fetch的绝对网址。我假设它与您的代码可以在其上执行的不同环境(客户端和服务器)有关。相对网址不明确&amp;在这种情况下足够可靠。

解决此问题的一种方法是将服务器地址硬编码到fetch请求中,另一种方法是设置config模块以响应您的环境:

/config/index.js

const dev = process.env.NODE_ENV !== 'production';

export const server = dev ? 'http://localhost:3000' : 'https://your_deployment.server.com';

products.js

import { server } from '../config';

// ...

Products.getInitialProps = async function() {

  const res = await fetch(`${server}/api/products`)
  const data = await res.json()

  console.log(data)
  console.log(`Showed data fetched. Count ${data.length}`)

  return {
    products: data
  }
}

答案 1 :(得分:2)

情况1。这不是错误。 isomorphic-unfetch正在SSR模式下运行,因此Node.js需要知道要从中获取的绝对URL,因为后端不知道您的浏览器设置。

情况2。另一种情况是防止http主机中毒标头攻击。

将密钥和令牌附加到包含密钥和令牌的链接上

<a href="http://_SERVER['HOST']?token=topsecret">  (Django, Gallery, others)

....甚至直接从中导入脚本:

<script src="http://_SERVER['HOST']/misc/jquery.js?v=1.4.4">

情况3。isomorphic-unfetch是我们将用来获取数据的库。这是浏览器访存API的简单实现,但在客户端和服务器环境中均可使用。

详细了解:

  1. Isomorphic unfetch - Switches between unfetch & node-fetch for client & server
  2. Prevent http host headers attack
  3. Fetching Data for Pages

答案 2 :(得分:2)

@Shanker's answer 类似,但如果您不想为此安装额外的软件包,请按以下步骤操作。

async getInitialProps({ req }) {
    const protocol = req.headers['x-forwarded-proto'] || 'http'
    const baseUrl = req ? `${protocol}://${req.headers.host}` : ''

    const res = await fetch(baseUrl + '/api/products')
}

答案 3 :(得分:1)

这个简单的解决方案对我有用,而无需添加其他配置文件,

安装

npm install --save next-absolute-url

用法

import absoluteUrl from "next-absolute-url";

async getInitialProps({ req }){
  const { origin } = absoluteUrl(req, req.headers.host);
  console.log('Requested URL ->',origin); 
  // (or) other way
  const host = absoluteUrl(req, req.headers.host);
  console.log('Requested URL ->',host.origin); 
}

答案 4 :(得分:1)

如果您的项目托管在支持它的提供商上,您可以使用环境变量。

env.local

// Local
URL="http://localhost:3000"

// Production
URL="https://prod.com"

然后你可以使用以下内容。

const { URL } = process.env;
const data = await fetcher(URL + '/api');

答案 5 :(得分:0)

在NextJS 9.5中,我们也可以使用StorageFile
process.cwd()将为您提供执行Next.js的目录。

process.cwd()

参考:https://nextjs.org/docs/basic-features/data-fetching#reading-files-use-processcwd

答案 6 :(得分:0)

如果您有 absolute 路径问题。尝试使用 swr 访问数据。

注意:这是一个 React 钩子,所以你必须在组件内部调用。

import useSWR from 'swr';

// optionally you can use unfetch package from npm or built yours to handle promise.
const fetcher = (...args) => fetch(...args).then(res => res.json())

export const AllProducts = () => {
  const { data, error } = useSWR('/api/products', fetcher)
  if (error) return <div>failed to load</div>
  if (!data) return <div>loading...</div>
  
  return (
    <div>{data}</div>
  );
};

在生产中导出或部署

每当您尝试在 Vercel 上部署时,您都可能会遇到错误。例如`

warn - Statically exporting a Next.js application via `next export` disables API routes`. 

这意味着您正在尝试导出数据,而 NextJS 不支持从 pages/api/* 目录中提取数据。为避免出错,最好将构建和导出命令分开。

// package.json

{
 "scripts": {
    "dev": "next",
    "build": "next build",   // No next export command
    "start": "next start"
  },
}

感谢大家做出的巨大贡献,我希望分享的答案也能对某人有所帮助。

答案 7 :(得分:0)

这听起来很傻但值得一提。如果您在 web 应用程序中使用 SSR,则 fetch 调用将在客户端上使用相对链接,但在服务器上将失败。只有服务器需要绝对链接!

如果您想阻止服务器发出请求,只需将其包装在逻辑中

if(global.window){
   const req = fetch('/api/test');
   ...
}

答案 8 :(得分:-1)

在nock之后使用.log(console.log),这样您将获得准确的不匹配和预期的url。 示例:

     nock("http://localhost")
.log(console.log)
.persist()
.get("/api/config")
.reply(200, { data: 1234 })

答案 9 :(得分:-1)

fetch方法中的URL似乎有问题。 对我来说,它是通过在fetch方法中更改url来解决的。 请注意URL地址的正确性。