【问题标题】:Next.js - Error: only absolute urls are supportedNext.js - 错误:仅支持绝对网址
【发布时间】:2017-11-04 15:36:00
【问题描述】:

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

第 1 步:我点击产品链接

第 2 步:它将显示数据库中的产品。

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

服务器代码(查看/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)
})

页面 - 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

【问题讨论】:

    标签: reactjs react-redux next.js


    【解决方案1】:

    如果您正在使用下一个环境配置,请在您的变量前面加上 NEXT_PUBLIC_,如此处提到的 Exposing Environment Variables to the Browser

    【讨论】:

      【解决方案2】:

      您可以在 dockerfile(您用来部署的工具)上的 SERVER_URL 环境变量中添加您的域名,因此它将是一个绝对路径,这就是 getServerSideProps 所需要的。

      示例:

      API_URL=https://deployement.com/api
      

      【讨论】:

        【解决方案3】:

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

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

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

        【讨论】:

          【解决方案4】:

          类似于@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')
          }
          

          【讨论】:

            【解决方案5】:

            如果您有 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"
              },
            }
            

            感谢大家的巨大贡献,我希望分享的答案也能帮助别人。

            【讨论】:

              【解决方案6】:

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

              env.local

              // Local
              URL="http://localhost:3000"
              
              // Production
              URL="https://prod.com"
              

              那么你就可以使用下面的了。

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

              【讨论】:

                【解决方案7】:

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

                【讨论】:

                  【解决方案8】:

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

                  import path from 'path'
                  import fs from "fs";
                  
                  export const getStaticProps: GetStaticProps = async () => {
                      const dataFilePath = path.join(process.cwd(), "jsonFiles", "data.json");
                      console.log(dataFilePath);     // will be YourProject/jsonFiles/data.json
                  
                      const fileContents = fs.readFileSync(dataFilePath, "utf8");
                      const data: TypeOfData[] = JSON.parse(fileContents);
                      return { props: { data } };
                  };
                  

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

                  【讨论】:

                    【解决方案9】:

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

                    安装

                    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); 
                    }
                    

                    【讨论】:

                    • req 仅在服务器端可用,因此您需要为客户端渲染添加一些检查/处理
                    【解决方案10】:

                    案例 1。这不是错误。 isomorphic-unfetch 是通过 SSR 模式运行的,因此 Node.js 需要知道从中获取的绝对 url,因为后端不知道您的浏览器设置。

                    案例2.另一个场景是防止http主机中毒headers攻击。

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

                    <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

                    【讨论】:

                      【解决方案11】:

                      在 nock 之后使用 .log(console.log) ,因此您将获得完全不匹配和预期的 url 。 示例:

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

                      【讨论】:

                        【解决方案12】:

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

                        解决此问题的一种方法是将服务器地址硬编码到您的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
                          }
                        }
                        

                        【讨论】:

                        • 但是当您的数据真正存储在本地时呢?我在本地有一些本地化的静态 json
                        • @PaulT: 'undefined' === typeof window ? 'http://localhost:3000' : 'https://your_deployment.server.com';
                        猜你喜欢
                        • 2019-01-02
                        • 1970-01-01
                        • 2019-08-14
                        • 2021-03-20
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2011-09-28
                        • 2010-10-28
                        相关资源
                        最近更新 更多