我租用了一个 VPS 来运行一些个人项目。首先,我尝试将其设置为使用 Node.js 托管 REST API,因为我只使用过 Spring Boot 来做这件事。
我根据以下指南实施了解决方案:
https://www.robinwieruch.de/node-express-server-rest-api(大部分 API 代码如下所示)
https://itnext.io/building-restful-api-with-node-js-express-js-and-postgresql-the-right-way-b2e718ad1c66(但我正在慢慢转向使用这里的标准)
对于实际部署,我已更改为将 API 与 Babel 捆绑在一起,并使用 PM2 进行部署。
https://www.nginx.com/blog/deploying-nginx-plus-as-an-api-gateway-part-1/(但不是 Plus)
(所有这些链接都可以通过http://web.archive.org/所以他们近期不会去任何地方)
我使用创建了另一个 Node.js 项目axios测试 REST 请求。在同一个 VPS 中运行它可以工作,但我不得不更改 API 代码以绑定到localhost
。之前,由于未指定绑定地址,它绑定到 IPv6 localhost
(如果我理解正确的话),并且由于我的域不支持 IPv6,我将坚持使用 IPv4。
在 NGINX 方面,我做了最多的更改,因为我现在只有 1 个 API,而且我不会使用负载平衡。此外,我还更改了命名策略。我将使用example.com/app_or_project_name/api_or_web_or_other_kind_of_interface/project_specific_routes
。
以下是我的 NGINX 设置。我已将其匿名化,并更改为使用与 NGINX 示例相同的名称:
api_backends.conf
upstream warehouse {
zone api 64k;
server 127.0.0.1:some_port_number;
}
这是 REST API 主机和端口号。
api_conf.d/warehouse_api.conf
# Warehouse API
#
location /warehouse/api/ {
# Policy configuration here (authentication, rate limiting, logging, more...)
#
access_log /var/log/nginx/warehouse_api.log main;
auth_request /_validate_apikey;
# URI routing
#
proxy_pass http://warehouse;
return 404; # Catch-all
}
api_gateway.conf
include api_backends.conf;
include api_keys.conf;
server {
access_log /var/log/nginx/api_access.log main; # Each API may also log to a separate file
listen 443 ssl;
server_name my-domain.net;
# TLS config
ssl_certificate /etc/letsencrypt/live/my-domain.net/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/my-domain.net/privkey.pem; # managed by Certbot
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_protocols TLSv1.2 TLSv1.3;
# API definitions, one per file
include api_conf.d/*.conf;
# Error responses
# error_page 404 = @400; # Invalid paths are treated as bad requests
proxy_intercept_errors on; # Do not send backend errors to the client
include api_json_errors.conf; # API client friendly JSON error responses
default_type application/json; # If no content-type then assume JSON
# API key validation
location = /_validate_apikey {
internal;
if ($http_apikey = "") {
return 401; # Unauthorized
}
if ($api_client_name = "") {
return 403; # Forbidden
}
return 204; # OK (no content)
}
}
一旦它开始工作,我可能会设置proxy_intercept_errors
为off
。我必须做一些测试,看看响应会有什么变化。
api_json_errors.conf
与示例相同。
默认配置文件
server {
server_name www.my-domain.net;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/my-domain.net/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/my-domain.net/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = www.my-domain.net) {
return 301 https://$host$request_uri;
} # managed by Certbot
if ($host = my-domain.net) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name my-domain.net www.my-domain.net;
return 404; # managed by Certbot
}
我必须在这里做出一些改变,因为地址和端口的组合有重复的server
配置。
nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log info;
pid /var/run/nginx.pid;
load_module /etc/nginx/modules/ngx_http_js_module.so;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
include /etc/nginx/api_gateway.conf; # All API gateway configuration
include /etc/nginx/conf.d/*.conf; # Regular web traffic
}
当我在 VPS 之外运行相同的测试项目时,得到以下结果:
{
message: 'Request failed with status code 404',
name: 'Error',
description: undefined,
number: undefined,
fileName: undefined,
lineNumber: undefined,
columnNumber: undefined,
stack: '...',
config: {
url: 'https://my-domain.net/warehouse/api/messages',
method: 'get',
headers: {
Accept: 'application/json, text/plain, */*',
'Access-Control-Allow-Origin': '*',
'User-Agent': 'axios/0.21.1'
},
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 0,
adapter: [Function: httpAdapter],
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
validateStatus: [Function: validateStatus],
apikey: '...',
data: undefined
},
code: undefined
}
我设法弄清楚的一件事是,404 错误是由以下原因返回的,warehouse_api.conf
因为如果我更改return 404;
为另一个代码,那就是我将获得的代码。
我已经在 NGINX 中启用了调试,但是即使经过一番搜索,我也无法理解输出:
2021/07/22 11:54:17 [debug] nginx_pid#nginx_pid: *757 using location: @404 "/warehouse/api/messages?"
2021/07/22 11:54:31 [debug] nginx_pid#nginx_pid: *758 http cl:-1 max:1048576
2021/07/22 11:54:31 [debug] nginx_pid#nginx_pid: *758 rewrite phase: 3
2021/07/22 11:54:31 [debug] nginx_pid#nginx_pid: *758 http finalize request: 404, "/warehouse/api/messages?" a:1, c:1
2021/07/22 11:54:31 [debug] nginx_pid#nginx_pid: *758 http special response: 404, "/warehouse/api/messages?"
2021/07/22 11:54:31 [debug] nginx_pid#nginx_pid: *758 test location: "@400"
2021/07/22 11:54:31 [debug] nginx_pid#nginx_pid: *758 test location: "@401"
2021/07/22 11:54:31 [debug] nginx_pid#nginx_pid: *758 test location: "@403"
2021/07/22 11:54:31 [debug] nginx_pid#nginx_pid: *758 test location: "@404"
2021/07/22 11:54:31 [debug] nginx_pid#nginx_pid: *758 using location: @404 "/warehouse/api/messages?"
我尝试了几种不同的方法来搜索这一切,但没有找到任何线索。
那么,发生了什么事,出了什么问题以及我该如何解决?
提前致谢。
更新 2021-08-04
根据@jose-fernando-lopez-fernandez的回答,我改为api_conf.d/warehouse_api.conf
以下内容:
# Warehouse API
#
location /warehouse/api/ {
# Policy configuration here (authentication, rate limiting, logging, more...)
#
access_log /var/log/nginx/warehouse_api.log main;
auth_request /_validate_apikey;
# URI routing
#
location /warehouse/api/ {
proxy_pass http://warehouse;
}
return 404; # Catch-all
}
以使其与我所遵循的示例保持一致。我再次测试,结果却出现了 401 错误。我检查了一下,发现我传递的参数apikey
不正确。
我修复了它,再次测试,再次得到 404。但现在我得到了更多的输出nginx-debug
。
我已将其匿名化。我用我生成的另一个来替换它apikey
,并且不会用于任何用途。
我还用长度相同的随机字节字符串替换了posix_memalign
、http cleanup add
、free
和chain writer in
。malloc
我不知道它是否应该是匿名的。如果解决这个问题需要它,请提出问题,我会根据需要将它们重新添加回来。
开始:
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http cl:-1 max:1048576
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 rewrite phase: 3
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 post rewrite phase: 4
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 generic phase: 5
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 generic phase: 6
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 generic phase: 7
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 access phase: 8
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 access phase: 9
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 access phase: 10
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 auth request handler
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http subrequest "/_validate_apikey?"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http posted request: "/_validate_apikey?"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 rewrite phase: 1
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 test location: "/warehouse/api/"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 test location: "/_validate_apikey"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 using configuration "=/_validate_apikey"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http cl:-1 max:1048576
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 rewrite phase: 3
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http script var
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http script var: "o6ZlKSX24MCY/uPwCRl80WAS"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http script value: ""
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http script equal
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http script equal: no
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http script if
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http script if: false
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http script var
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http map started
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http script var: "o6ZlKSX24MCY/uPwCRl80WAS"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http map: "o6ZlKSX24MCY/uPwCRl80WAS" "client_one"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http script var: "client_one"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http script value: ""
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http script equal
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http script equal: no
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http script if
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http script if: false
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http finalize request: 0, "/_validate_apikey?" a:1, c:2
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 auth request done s:204
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http wake parent request: "/warehouse/api/messages?"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http posted request: "/warehouse/api/messages?"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 access phase: 10
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 auth request handler
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 auth request set variables
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 post access phase: 11
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 generic phase: 12
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 generic phase: 13
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 posix_memalign: 218512C89A2ED401:4096 @16
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http init upstream, client timer: 0
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 epoll add event: fd:3 op:3 ev:80002005
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http script copy: "Host"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http script var: "warehouse"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http script copy: "Connection"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http script copy: "close"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http script copy: ""
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http script copy: ""
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http proxy header: "Accept: application/json, text/plain, */*"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http proxy header: "Access-Control-Allow-Origin: *"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http proxy header: "apikey: o6ZlKSX24MCY/uPwCRl80WAS"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http proxy header: "User-Agent: axios/0.21.1"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http proxy header:
"GET /warehouse/api/messages HTTP/1.0
Host: warehouse
Connection: close
Accept: application/json, text/plain, */*
Access-Control-Allow-Origin: *
apikey: o6ZlKSX24MCY/uPwCRl80WAS
User-Agent: axios/0.21.1
"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http cleanup add: 90C9DA232086B6FA
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 get rr peer, try: 1
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 stream socket 15
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 epoll add connection: fd:15 ev:80002005
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 connect to 127.0.0.1:some_port_number, fd:15 #2
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http upstream connect: -2
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 posix_memalign: A9E50626EC2A1D36:128 @16
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 event timer add: 15: 60000:878601635
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http finalize request: -4, "/warehouse/api/messages?" a:1, c:2
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http request count:2 blk:0
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http run request: "/warehouse/api/messages?"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http upstream check client, write event:1, "/warehouse/api/messages"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http upstream request: "/warehouse/api/messages?"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http upstream send request handler
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http upstream send request
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http upstream send request body
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 chain writer buf fl:1 s:225
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 chain writer in: 4C4F626384F523C9
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 writev: 225 of 225
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 chain writer out: 0000000000000000
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 event timer del: 15: 878601635
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 event timer add: 15: 60000:878601636
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http upstream request: "/warehouse/api/messages?"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http upstream process header
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 malloc: 1D36E73206B5EE11:4096
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 recv: eof:1, avail:-1
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 recv: fd:15 444 of 4096
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http proxy status 404 "404 Not Found"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http proxy header: "X-Powered-By: Express"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http proxy header: "Access-Control-Allow-Origin: *"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http proxy header: "Content-Security-Policy: default-src 'none'"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http proxy header: "X-Content-Type-Options: nosniff"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http proxy header: "Content-Type: text/html; charset=utf-8"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http proxy header: "Content-Length: 168"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http proxy header: "Date: Wed, 04 Aug 2021 17:43:08 GMT"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http proxy header: "Connection: close"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http proxy header done
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 finalize http upstream request: 404
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 finalize http proxy request
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 free rr peer 1 0
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 close http upstream connection: 15
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 free: A9E50626EC2A1D36, unused: 48
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 event timer del: 15: 878601636
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 reusable connection: 0
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http finalize request: 404, "/warehouse/api/messages?" a:1, c:1
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 http special response: 404, "/warehouse/api/messages?"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 test location: "@400"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 test location: "@401"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 test location: "@403"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 test location: "@404"
2021/08/04 17:43:08 [debug] nginx_pid#nginx_pid: *1 using location: @404 "/warehouse/api/messages?"
我觉得很奇怪,它似乎被视为warehouse
主机名。另一方面,NGINX 确实用该名称定义了一些地址,因此它可能与此相关。
答案1
我假设您只是在仓库 API 文件中删去了与代理相关的机制,以便稍后处理,但正如您所发现的,这实际上是行不通的。
# Warehouse API
#
location /warehouse/api/ {
# Policy configuration here (authentication, rate limiting, logging, more...)
#
access_log /var/log/nginx/warehouse_api.log main;
auth_request /_validate_apikey;
# URI routing
#
proxy_pass http://warehouse;
return 404; # Catch-all
}
将负责 URI 路由的一行与您链接的示例中版本进行比较。
# Warehouse API
#
location /api/warehouse/ {
# Policy configuration here (authentication, rate limiting, logging, more...)
#
access_log /var/log/nginx/warehouse_api.log main;
auth_request /_validate_apikey;
# URI routing
#
location /api/warehouse/inventory {
proxy_pass http://warehouse_inventory;
}
location /api/warehouse/pricing {
proxy_pass http://warehouse_pricing;
}
return 404; # Catch-all
}
该示例有效而您的无效的原因与指令的“优先级”(“即时性”可能是更好的术语)有关return
。
根据文档,该return
指令立即导致 Nginx 停止处理当前请求并立即返回1。这意味着您的proxy_pass
指令甚至没有机会尝试执行。
但是,在示例中,块内有两个嵌套的基于前缀的位置,这意味着 Nginx 将选择最长的匹配。这就是示例请求成功的原因;请求的 URI 与https://api.example.com/api/warehouse/pricing/item001
两个嵌套位置块中的第二个匹配,因此请求按预期进行代理。
总之,proxy_pass
如果您想复制该示例,则需要添加一个嵌套的位置块,以便指令在其中执行。否则,看起来您应该能够简单地删除该return
指令,并且您配置的上游后端将可以自由地尝试执行其操作。