【问题标题】:GET http://api:1337/games net::ERR_NAME_NOT_RESOLVED for nuxt.js pages using asyncData使用 asyncData 获取 nuxt.js 页面的 http://api:1337/games net::ERR_NAME_NOT_RESOLVED
【发布时间】:2019-03-10 16:48:19
【问题描述】:

我的 docker 设置有些复杂。一切都按预期工作,除了我有这个奇怪的问题。 访问 index 页面或 /pages/_id 页面我没有错误。但是当我尝试打开 /other-page 时,它会崩溃。所有人都使用相同的 API url。

打开/other-page时发现控制台出错:

GET http://api:1337/gamesnet::ERR_NAME_NOT_RESOLVED

不知道该怎么做,有什么建议吗?

nuxt.config.js

  axios: {
    baseURL: 'http://api:1337'
  },

docker-compose.yml

version: '3'

services:
  api:
    build: .
    image: strapi/strapi
    environment:
      - APP_NAME=strapi-app
      - DATABASE_CLIENT=mongo
      - DATABASE_HOST=db
      - DATABASE_PORT=27017
      - DATABASE_NAME=strapi
      - DATABASE_USERNAME=
      - DATABASE_PASSWORD=
      - DATABASE_SSL=false
      - DATABASE_AUTHENTICATION_DATABASE=strapi
      - HOST=api
      - NODE_ENV=production
    ports:
      - 1337:1337
    volumes:
      - ./strapi-app:/usr/src/api/strapi-app
      #- /usr/src/api/strapi-app/node_modules
    depends_on:
      - db
    restart: always
    links:
      - db

  nuxt:
    # build: ./app/
    image: "registry.gitlab.com/username/package:latest"
    container_name: nuxt
    restart: always
    ports:
      - "3000:3000"
    links:
      - api:api
    command:
      "npm run start"


  nginx:
    image: nginx:1.14.2
    expose:
      - 80
    container_name: nginx
    restart: always
    ports:
      - "80:80"
    volumes:
      - ./nginx:/etc/nginx/conf.d
    depends_on:
      - nuxt
    links:
      - nuxt

index.vue

...
  async asyncData({ store, $axios }) {
    const games = await $axios.$get('/games')
    store.commit('games/emptyList')
    games.forEach(game => {
      store.commit('games/add', {
        id: game.id || game._id,
        ...game
      })
    })
    return { games }
  },
...

page.vue

...
  async asyncData({ store, $axios }) {
    const games = await $axios.$get('/games')
    store.commit('games/emptyList')
    games.forEach(game => {
      store.commit('games/add', {
        id: game.id || game._id,
        ...game
      })
    })
    return { games }
  },
...

Nginx 配置

upstream webserver {
  ip_hash;
  server nuxt:3000;
}

server {
  listen 80;
  access_log off;
  connection_pool_size 512k;
  large_client_header_buffers 4 512k;

  location / {
    proxy_pass http://webserver;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_max_temp_file_size 0;
  }

更新:

尝试了 Thomasleveil 的建议。现在我收到以下错误:

nuxt | [2:09:35 PM] 错误:连接 ECONNREFUSED 127.0.0.1:80

所以,现在 /api 似乎被转发到 127.0.0.1:80。不知道为什么^^

nuxt.config.js

  axios: {
    baseURL: '/api'
  },
  server: {
    proxyTable: {
      '/api': {
         target: 'http://localhost:1337',
         changeOrigin: true,
         pathRewrite: {
           "^/api": ""
         }
      }
    }
  }

docker-compose.yml

version: '3'

services:
  reverse-proxy:
    image: traefik # The official Traefik docker image
    command: --api --docker # Enables the web UI and tells Traefik to listen to docker
    ports:
      - "80:80"     # The HTTP port
      - "8080:8080" # The Web UI (enabled by --api)
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock # listen to the Docker events
    networks:
      - mynet

  api:
    build: .
    image: strapi/strapi
    container_name: api
    environment:
      - APP_NAME=strapi-app
      - DATABASE_CLIENT=mongo
      - DATABASE_HOST=db
      - DATABASE_PORT=27017
      - DATABASE_NAME=strapi
      - DATABASE_USERNAME=
      - DATABASE_PASSWORD=
      - DATABASE_SSL=false
      - DATABASE_AUTHENTICATION_DATABASE=strapi
      - HOST=api
      - NODE_ENV=development
    ports:
      - 1337:1337
    volumes:
      - ./strapi-app:/usr/src/api/strapi-app
      #- /usr/src/api/strapi-app/node_modules
    depends_on:
      - db
    restart: always
    networks:
      - mynet
    labels:
      - "traefik.backend=api"
      - "traefik.docker.network=mynet"
      - "traefik.frontend.rule=Host:example.com;PathPrefixStrip:/api"
      - "traefik.port=1337"

  db:
    image: mongo
    environment:
      - MONGO_INITDB_DATABASE=strapi
    ports:
      - 27017:27017
    volumes:
      - ./db:/data/db
    restart: always
    networks:
      - mynet

  nuxt:
    # build: ./app/
    image: "registry.gitlab.com/username/package:latest"
    container_name: nuxt
    restart: always
    ports:
      - "3000:3000"
    command:
      "npm run start"
    networks:
      - mynet
    labels:
      - "traefik.backend=nuxt"
      - "traefik.frontend.rule=Host:example.com;PathPrefixStrip:/"
      - "traefik.docker.network=web"
      - "traefik.port=3000"

networks:
  mynet:
    external: true

【问题讨论】:

  • 您的 api url 只能通过 docker 容器解析。如果您从浏览器访问它,它将无法正常工作
  • 我不是从它自己的容器“访问”它,axios 是。
  • 您正在访问它。当您在浏览器中转到该页面时,asyncData 也在客户端上执行(不是第一次加载)
  • 啊,所以当我访问主页(索引)时它在服务器上执行,然后它可以工作,但是当我点击 /other-page 时,它​​也在客户端上执行,因此它失败了?
  • 是的,对,ssr 仅在初始页面加载时执行。然后,当您在应用程序中导航时,一切都发生在客户端

标签: docker nuxt.js traefik strapi


【解决方案1】:

访问索引页面或 /pages/_id 页面我没有错误。但是当我尝试打开 /other-page 时它崩溃了。

重新表述:

  • 我在/ 有一个主页,其中显示了一些指向/pages/_id 页面的链接(其中_id 是一个有效的游戏ID)
  • 当我打开//pages/_id 时,会显示内容
  • 但是,如果我从页面 / 指向 /pages/xxx(其中 xxx 是有效 ID)单击链接,则会出现错误
  • 此外,如果我刷新页面,我会看到内容而不是错误
  • 这些页面的内容来自 api 服务器。页面应该通过调用 api 服务器来获取内容,然后使用响应呈现页面内容。

这里发生了什么?

异步数据

asyncData 在 nuxt.js 中的工作方式如下:

在第一页加载

  1. 用户在浏览器中输入网址http://yourserver/pages/123
  2. nuxt Web 服务器处理请求、解析路由并为该页面挂载 vue 组件
  3. vue 组件中的asyncData 方法是从nuxt.js 服务端调用的
  4. nuxt.js 服务器(不是用户浏览器)然后通过对 http://api:1337/games/123 进行不同的调用来获取内容,接收响应并呈现内容。

当用户点击另一个页面的链接时

现在发生了一些不同的事情。

  1. 用户仍在页面http://api:1337/games/123 上,该页面有一个指向列出所有游戏的主页的链接 (http://yourserver/),然后单击它。
  2. 浏览器不会加载任何新页面。相反,用户浏览器对http://api:1337/games 进行ajax 调用以尝试获取新内容。由于名称解析错误而失败

为什么?

这是 nuxt.js 为您带来的一项功能,用于加快页面内容加载时间。来自documentation,重要的信息是:

asyncData 每次在加载页面组件之前都会被调用。它将在服务器端调用一次(在对 Nuxt 应用的第一次请求时)和在导航到更多路由时在客户端调用。

  • server-side 表示从nuxt服务器到api服务器的调用
  • client-side 表示从用户浏览器调用api服务器

现在是有趣的部分:

  • nuxt 服务器正在第一个容器中运行
  • api 服务器正在第二个容器中运行,并且正在侦听端口 1337
  • 在nuxt容器中,调用api服务器的url是http://api:1337/,可以正常使用
  • 从用户浏览器调用http://api:1337 失败(net::ERR_NAME_NOT_RESOLVED),因为用户计算机不知道如何将域名api 转换为IP 地址。即使可以,这个 IP 地址也将无法访问。

可以做什么?

  • 您需要设置一个反向代理,它将用户浏览器发出的请求转发到以http://yourserver/api/ 开头的url 到端口1337 上的api 容器。
  • 并且您需要配置 nuxt.js 以使指向 client-side(来自用户浏览器)的 api 的链接使用 url http://yourserver/api 而不是 http://api:1337/
  • 并且您需要配置 nuxt.js 以便它不断调用 http://api:1337 来调用 server-side

为来自 nuxt (server-side) 的调用添加反向代理

由于您使用nuxt.js Axios module 来调用 api 容器,所以您已经完成了一半。

Axios 模块有一个proxy 选项,可以在nuxtjs.config.js 中设置为true

Bellow 是使用 Traefik 为您的项目设置反向代理的示例,但文档指出该代理与 baseURL 选项的使用不兼容。必须改用prefix 选项。

你的 nuxt.config.js 应该如下所示:

  axios: {
    prefix: '/api',
    proxy: true
  }, 
  proxy: {
    '/api/': {
      target: 'http://localhost:1337',
      pathRewrite: {
        '^/api/': ''
      }
    }
  },

这在您的开发计算机上运行良好,如果 strpi 正在运行并在http://localhost:1337 响应。但这在容器中不起作用,因为我们需要将 http://localhost:1337 替换为 http://api:1337。 为此,我们可以引入一个环境变量(STRAPI_URL):

  axios: {
    prefix: '/api',
    proxy: true
  }, 
  proxy: {
    '/api/': {
      target: process.env.STRAPI_URL || 'http://localhost:1337',
      pathRewrite: {
        '^/api/': ''
      }
    }
  },

我们稍后会在 docker-compose.yml 文件中设置STRAPI_URL

为来自用户浏览器的调用添加反向代理 (client-side)

由于我在使用 docker 时放弃了使用 nginx 实现反向代理,所以这里有一个 Traefik 的示例:

docker-compose.yml:

version: '3'

services:
  reverseproxy:  # see https://docs.traefik.io/#the-traefik-quickstart-using-docker
    image: traefik:1.7
    command: --docker
    ports:
      - "80:80"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

  api:
    image: strapi/strapi
    environment:
      - ...
    expose:
      - 1337
    labels:
      traefik.frontend.rule: PathPrefixStrip:/api
      traefik.port: 1337

  nuxt:
    image: ...
    expose:
      - 3000
    command:
      "npm run start"
    labels:
      traefik.frontend.rule: PathPrefixStrip:/
      traefik.port: 3000

现在用户浏览器向http://yourserver 发出的所有 HTTP 请求都将由 Traefik 反向代理处理。

Traefik 将通过查看nuxtapi 容器上以traefik. 开头的标签来配置转发规则。

发生了什么变化?

您现在有 2 个反向代理:

  • 一个用于server-side 请求(nuxt.js Proxy module
  • 一个用于client-side 请求(Traefik)

还没有完成

我们现在需要指示 nuxt.js 代理模块,它必须将请求转发到 http://api:1337/。我们将为此使用STRAPI_URL 环境变量。

我们需要指示nuxt Axios 模块,用户浏览器必须调用http://yourserver/api 上的api。这是通过API_URL_BROWSER 环境变量完成的。


大家一起

nuxt.config.js

  axios: {
    prefix: '/api',
    proxy: true
  }, 
  proxy: {
    '/api/': {
      target: process.env.STRAPI_URL || 'http://localhost:1337',
      pathRewrite: {
        '^/api/': ''
      }
    }
  },

docker-compose.yml

version: '3'

services:
  reverseproxy:  # see https://docs.traefik.io/#the-traefik-quickstart-using-docker
    image: traefik:1.7
    command: --docker
    ports:
      - "80:80"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

  api:
    image: strapi/strapi
    environment:
      - ...
    expose:
      - 1337
    labels:
      traefik.frontend.rule: PathPrefixStrip:/api
      traefik.port: 1337

  nuxt:
    image: ...
    expose:
      - 3000
    command:
      "npm run start"
    environment:
      NUXT_HOST: 0.0.0.0
      STRAPI_URL: http://api:1337/
      API_URL_BROWSER: /api
    labels:
      traefik.frontend.rule: PathPrefixStrip:/
      traefik.port: 3000

【讨论】:

  • 谢谢,但在我深入研究之前,可以用 nginx 完成吗?我已经将它用作第四个容器。
  • 将我的 nginx 详细信息添加到 docker-compose.yml 部分。或者你会推荐使用 traefik 代替 nginx 吗?
  • 这可以用 nginx 完成,这是我在遇到 Traefik 之前实现反向代理的方式,这在这件事上可以节省时间。它将处理 HTTP/2、websockets、负载平衡、标头转发、断路器、健康检查,甚至为您设置带有 Letsencrypt 的 SSL。如果你想出一个 nginx 配置来完成所有你会在 StackOverflow 上提出很多问题的问题 ;)
  • :) 好吧'我会试试的。令我困惑的一件事是目标:'localhost:1337'我应该在生产中使用api:1337(容器网址)还是example.com:1337
  • 将应用部署到生产环境时,永远不会使用 nuxt.config.js 中的 target: 'http://localhost:1337'。因为这部分配置仅适用于webpack-dev-server。目标应该是从strapi start 命令运行时可以加入您的 api 的 url
猜你喜欢
  • 2019-08-08
  • 2020-12-17
  • 2021-07-03
  • 2019-12-17
  • 2019-09-11
  • 2019-11-23
  • 2022-08-23
  • 2020-08-16
  • 2023-04-04
相关资源
最近更新 更多