Angular本地中转报错:[HPM] Error occurred while trying to proxy request
代理配置
在前端开发中了,为了解决浏览器跨域问题,一般都会使用 webpack 的 devServer.proxy 功能,来中转接口。
根据 Angular 的文档,只需要四步即可使用代理。
(1) 在 src/
目录下创建 proxy.conf.json
配置文件.
(2) 在 proxy.conf.json 配置文件中写上中转规则:
{
"/api": {
"target": "http://localhost:3000",
"secure": false
}
}
(3) 在 angualr.json
配置文件中,加上 proxyConfig 的配置:
"architect": {
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "your-application-name:build",
"proxyConfig": "src/proxy.conf.json"
},
(3) 使用 ng serve
重新启动项目.
这样在Angular项目中访问 http://localhost:4200/api 就会自动中转到 http://localhost:3000/api 接口了。
莫名其妙的问题
就这样,在开发环境中一直可以正常运行,上周五重启电脑,本周一再运行居然报错了。
浏览器接口请求的报错信息如下:
Error occured while trying to proxy to: localhost:4201/adminapi/login?AdminLogin
在终端中报错如下:
[HPM] POST /adminapi/login?AdminLogin -> http://localhost:8001/
[HPM] Error occurred while trying to proxy request /adminapi/login?AdminLogin
from localhost:4201 to http://localhost:8001/
(ECONNREFUSED)
(https://nodejs.org/api/errors.html#errors_common_system_errors)
在HTTP拦截器中打印的 HttpErrorResponse 错误如下:
HttpErrorResponse {
headers: HttpHeaders,
status: 504,
statusText: "Gateway Timeout",
url: "http://localhost:4201/adminapi/login",
ok: false
}
看一下项目的配置吧。
前端使用 Angular 11, 后端使用 PHP lumen 8+,前端后端同时开发。
为了方便开发,直接使用了 PHP 内置的web服务器,没有使用 Vagrant + Homestead 环境。
$ php -S localhost:8001 -t ./public
[Mon Mar 22 17:31:34 2021] PHP 8.0.3
Development Server (http://localhost:8001)
started
接口配置如下:
// src/environments/environment.ts
export const environment = {
production: false,
// 当前ng启动的地址,使用proxy.conf.json配置中转到配置里的地址
apiHost: 'http://localhost:4201/api',
adminApi: 'http://localhost:4201/adminapi',
};
中转文件 src/proxy.conf.json 配置如下:
{
"/adminapi/*": {
"target": "http://localhost:8001/",
"secure": false,
"changeOrigin": true,
"logLevel": "debug"
},
"/api/*": {
"target": "http://localhost:8001/",
"secure": false,
"changeOrigin": true,
"logLevel": "debug"
}
}
网上的解决方法
(1) 配置文件 proxy.conf.json 未指定 http 或 https: 【排除,不是该问题】
# 未加 http,报错
target: 'localhost:8081',
# 正常
target: 'http://localhost:8081',
(2) 后端接口没启动: 【排除,不是该问题】
可以看到接口是正常的。直接在浏览器复制curl请求,在终端执行请求也是正常的。
$ curl -XPOST localhost:8001
{"Status":401,"Msg":"Unauthorized.","Data":"","metadata":{}}%
(3) proxy 配置规则不正确。 【排除,不是该问题】
查看 webpack 的 devServer.proxy 配置,也未见异常。
而且,proxy.conf.json 配置文件并未改动,之前都是正常的。
(4) 删掉 node_module 重新下载依赖包。重装了 nodejs (v15.6)。都不行。
(5) 有网友指出要将 localhost 改成 ip 访问。
✔ Compiled successfully.
[HPM] POST /adminapi/login?AdminLogin -> http://127.0.0.1:8001/
[HPM] Error occurred while trying to proxy request /adminapi/login
from localhost:4201 to http://127.0.0.1:8001/
(ECONNREFUSED)
(https://nodejs.org/api/errors.html#errors_common_system_errors)
也是不行。继续报错。
其实这里就有问题了,潜意识里,一直认为 localhost 就是等于 127.0.0.1
的。
$ curl -XPOST 127.0.0.1:8001
curl: (7) Failed to connect to 127.0.0.1 port 8001: Connection refused
不知为何,这里居然是访问不了的。但是 ping 查看 localhost 也是访问的 127.0.0.1 的。
$ ping localhost
PING localhost (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.061 ms
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.255 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.181 ms
^C
这时,看到了这篇文章《一个localhost引发的血案》。
webpack 的 devServer.proxy 配置,其实是用的 http-proxy-middleware
包。
在 node_modules 找到这个包,找个跑出错误地方查看。
找到 logError
函数,可以达到
// node_modules/http-proxy-middleware/lib/index.js
function logError(err, req, res) {
var hostname =
(req.headers && req.headers.host) || (req.hostname || req.host)
var target = proxyOptions.target.host || proxyOptions.target
var errorMessage =
'[HPM] Error occurred while trying to proxy request %s from %s to %s (%s) (%s)'
var errReference =
'https://nodejs.org/api/errors.html#errors_common_system_errors'
// <------- 在这里打印日志
logger.error("==========> 打印日志: ")
logger.error(err)
logger.error(
errorMessage,
req.url,
hostname,
target,
err.code || err,
errReference
)
}
在控制台可以看到输出的错误日志。
[HPM] Error occurred while trying to proxy request /adminapi
from localhost:4201 to http://127.0.0.1:8001/
(ECONNREFUSED)
(https://nodejs.org/api/errors.html#errors_common_system_errors)
[HPM] POST /adminapi?AdminGetTransactionList -> http://127.0.0.1:8001/
==========> 打印日志:
Error: connect ECONNREFUSED 127.0.0.1:8001
at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1138:16) {
errno: -61,
code: 'ECONNREFUSED',
syscall: 'connect',
address: '127.0.0.1',
port: 8001
}
可以看到原始日志,connect ECONNREFUSED 127.0.0.1:8001,果然是因为 127.0.0.1 无法访问的缘故。
当前问题的解决办法:
(1) 在服务端后台解决跨域问题(上线后接口配置)
比如可以使用 Homestead + Vagrant 配置后端开发环境。在 Nginx 里使用 add_header 配置 Access-Control-Allow-Origin 策略。
(2) 使用jsonp解决跨域问题。(这个在以前用jQuery开发时比较常用)
(3) 开发继续使用 PHP 内置web服务,前端继续使用代理自己解决跨域。
重启PHP后台接口,使用 ip:
$ php -S localhost:8001 -t ./public
改成:
$ php -S 127.0.0.1:8001 -t ./public
参考链接
http://blog.epoos.com/2018/05/21/proxy-error/
https://angular.io/guide/build
https://webpack.js.org/configuration/dev-server/#devserverproxy