GET http://api:1337/games net::ERR_NAME_NOT_RESOLVED for nuxt.js pages using asyncData

GET http://api:1337/games net::ERR_NAME_NOT_RESOLVED for nuxt.js pages using asyncData

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

打开 /other-page 时在控制台中发现错误:

获取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

Visiting index page or /pages/_id pages I have no errors. But when I try to open /other-page it crashes.

重新制定:

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

这里发生了什么?

异步数据

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

第一页加载时

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

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

现在有些不同。

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

为什么?

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

asyncData is called every time before loading the page component. It will be called server-side once (on the first request to the Nuxt app) and client-side when navigating to further routes.

  • 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 地址也无法访问。

可以做什么?

  • 您需要设置一个反向代理,将用户浏览器发出的请求转发到 url,从 http://yourserver/api/ 开始到 api 端口上的容器 1337 .
  • 并且您需要配置 nuxt.js 以便 link 到 api 使 client-side(从用户浏览器)使用 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

下面是使用 Traefik, but the documentation state that the proxy is incompatible with the usage of the baseURL option. The prefix 选项为您的项目设置反向代理的示例,必须改用。

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

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

如果 strapi 是 运行 并且在 http://localhost:1337 处响应,这在您的开发计算机上运行良好。但这在容器中不起作用,因为我们需要用 http://api:1337 替换 http://localhost: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