Nginx 作为 Dart 服务器的代理未通过 POST 请求 body

Nginx as proxy for Dart server does not pass POST request body

我有一个用 Dart 实现的服务器,想解析 POST 请求 body。我正在使用 Postman / curl(和 Flutter 应用程序)来测试服务器。当我使用 localhost 访问服务器时,POST 请求 body 被正确处理并且服务器按预期工作。但是,当我尝试使用域名和 Nginx 作为反向代理处理来访问它时,服务器在尝试处理请求时崩溃 body.

使用相同的 nginx 配置,但 NodeJS 作为服务器(在其他项目中)带有 body-parser 包,NodeJS 服务器能够读取 body.

请求的curl版本:

curl --location --request POST 'https://insanichess.com/api/auth/register' \
--header 'Content-Type: application/json; charset=utf-8' \
--data-raw '{
    "email": "test@stelynx.com",
    "password": "test_pwd"
}'

Nginx 配置文件(使用 Certbot 生成):

server {
    server_name insanichess.com www.insanichess.com;

    location / {
        proxy_pass http://localhost:4040;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }

    listen [::]:443 ssl; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/insanichess.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/insanichess.com/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.insanichess.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    if ($host = insanichess.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80;
    listen [::]:80;

    server_name insanichess.com www.insanichess.com;
    return 404; # managed by Certbot
}

Dart代码的重要部分:

// insanichess_server.dart

  /// Starts the server.
  Future<void> start() async {
    final InternetAddress address = InternetAddress.loopbackIPv4;
    final int port =
        int.parse(Platform.environment['INSANICHESS_PORT'] ?? '4040');

    final HttpServer server = await HttpServer.bind(address, port);
    _logger.info('InsanichessServer.create', 'Server listening on port $port');

    await _handleRequests(onServer: server);
  }

  /// Passes every request [onServer] to [_router].
  Future<void> _handleRequests({required HttpServer onServer}) async {
    await for (final HttpRequest request in onServer) {
      await _router.handle(request);
    }
  }

// auth_controller.dart
  Future<void> handleRegistration(HttpRequest request) async {
    if (request.method != 'POST' ||
        request.contentLength <= 0 ||
        request.headers.contentType?.value != ContentType.json.value) {
      return respondWithBadRequest(request);
    }

    final String content = await utf8.decodeStream(request);
    final Map<String, dynamic> body = jsonDecode(content);
    //...
  }

服务器崩溃

Unhandled exception:
FormatException: Unexpected end of input (at character 1)

^

#0      _ChunkedJsonParser.fail (dart:convert-patch/convert_patch.dart:1405:5)
#1      _ChunkedJsonParser.close (dart:convert-patch/convert_patch.dart:523:7)
#2      _parseJson (dart:convert-patch/convert_patch.dart:41:10)
#3      JsonDecoder.convert (dart:convert/json.dart:506:36)
#4      JsonCodec.decode (dart:convert/json.dart:157:41)
#5      jsonDecode (dart:convert/json.dart:96:10)
#6      AuthController.handleRegistration (package:insanichess_server/src/controller/api/auth/auth.dart:79:39)
<asynchronous suspension>
#7      ApiRouter.handle (package:insanichess_server/src/router/api/api.dart:29:16)
<asynchronous suspension>
#8      ICRouter.handle (package:insanichess_server/src/router/ic_router.dart:27:16)
<asynchronous suspension>
#9      InsanichessServer._handleRequests (package:insanichess_server/src/insanichess_server.dart:38:7)
<asynchronous suspension>
#10     InsanichessServer.start (package:insanichess_server/src/insanichess_server.dart:32:5)
<asynchronous suspension>
#11     main (file:///home/campovski/insanichess/server/bin/main.dart:17:3)
<asynchronous suspension>

我尝试按照类似 Whosebug 答案中的建议在 nginx 中设置 CORS 和 X-** headers,但错误始终相同。 x-www-form-urlencoded 版本也没有帮助。

jsonDecode 之前打印 content.length 给出 0,但是在处理程序中打印 Content-Length header 值给出正确的值(我认为它是 63 或其他)。

这是处理程序在请求中收到的所有 header:

user-agent: curl/7.64.1
content-type: application/json; charset=utf-8
connection: upgrade
accept: */*
content-length: 63
host: insanichess.com

我假设问题出在使用 nginx 进行代理,因为直接在本地主机上查询是有效的,但是几乎相同的设置与 NodeJS 作为后端一起工作似乎很奇怪。该项目的完整源代码可在 GitHub 上找到,以备不时之需。

好像有问题

final Map<String, dynamic> body = jsonDecode(content);

或在 NGINX 上具有重定向状态。您应该使用 307 响应代码。 Source

出于某种原因,从 nginx 配置中的 location / 块中删除除 proxy_pass 之外的所有内容都有帮助。这个配置现在可以了:

server {
    server_name insanichess.com www.insanichess.com;

    location / {
        proxy_pass http://localhost:4040;
    }

    listen [::]:443 ssl; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/insanichess.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/insanichess.com/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.insanichess.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    if ($host = insanichess.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80;
    listen [::]:80;

    server_name insanichess.com www.insanichess.com;
    return 404; # managed by Certbot
}