从 PHP 控制器使用 request.post() 调用 python 脚本永远不会返回。为啥?

Posted

技术标签:

【中文标题】从 PHP 控制器使用 request.post() 调用 python 脚本永远不会返回。为啥?【英文标题】:Calling python script with request.post() from PHP controller never returns. Why?从 PHP 控制器使用 request.post() 调用 python 脚本永远不会返回。为什么? 【发布时间】:2021-02-24 18:53:01 【问题描述】:

我正在尝试从 Laravel 8 应用程序执行 python 脚本。 python 脚本使用 Laravel 应用程序的 REST API,因此它必须进行身份验证。

如果 python 脚本在命令行上运行(开发平台是 Windows 10),它自己运行很好,但如果通过 shell_exec() 或 exec() 或 symfony/process() 运行,它会将 Laravel 应用程序删除到我需要在不同的端口上重新启动应用程序或在控制台、laravel 日志中没有错误的情况下重新启动。

如果我从 python 脚本中取出 'requests.post()' 并将其替换为 print("Hello World") 它与 shell_exec() 一起工作正常

问题:有人知道会发生什么吗?这似乎是权限问题,但我不确定。

脚注:我尝试了 Symfony,但收到了许多其他人报告的错误“无法获取随机数来初始化 Python 运行时”这似乎是 Windows 开发盒上的常见问题。解决了这个问题,但结果是一样的。对于将来看到此 python 初始化错误的任何人:在 windows 构建环境中,您需要添加环境变量 SystemRoot,如下所示:$process = new Process([$python, $process_path, $argument1, $argument2], null, ['SystemRoot' => 'C:\WINDOWS']);

控制器

    public function startprocess(Request $request)
    

        $python = "C:/Users/dev/AppData/Local/Programs/Python/Python38/python.exe";
        $process_path = "C:/Apache24/htdocs/project/resources/python/lambda/test.py";
        $complete_path = $python." ".$process_path;
        $output = [];
        $return_var = "";

//        exec($complete_path, $output, $return_var);
        $process = new Process([$python, $process_path], null, ['SystemRoot' => 'C:\WINDOWS']);
        $process->run();

        return view('admin.scheduledtasks_data', compact('output', 'return_var', 'complete_path'))->render();
    

Python 代码

import sys
import requests
from requests.exceptions import HTTPError
import re

url_login = 'http://127.0.0.1:8089/api/apilogin'

data = 
    'email': 'myemail@email.com',
    'password': 'password'

returned_data = requests.post(url_login, data=data)  #<<THIS TAKES DOWN THE SERVER

这是 API 解析到的内容

   public function APIlogin(Request $request)
    

        $user = User::where('email', $request->email)->first();

        if (!$user || !Hash::check($request->password, $user->password)) 
            return response([
                'message' => ['Please submit a valid email address and password combination.']
            ], 404);
        

        $token = $user->createToken('user-token')->plainTextToken;

        $response = [
            'user' => $user,
            'token' => $token
        ];

        return response($response, 201);
    

这是一个好的请求在命令行中的样子

send: b'POST /api/apilogin HTTP/1.1 
Host: 127.0.0.1:8098 
User-Agent: python-requests/2.23.0 
Accept-Encoding: gzip, deflate 
Accept: */* 
Connection: keep-alive 
Content-Length: 48 
Content-Type: application/x-www-form-urlencoded'
send: b'email= (deleted) &password= (deleted)'
reply: 'HTTP/1.1 201 Created'
header: Host: 127.0.0.1:8098
header: Date: Fri, 13 Nov 2020 19:07:10 GMT
header: Connection: close
header: X-Powered-By: php/7.4.2
header: Cache-Control: no-cache, private
header: Date: Fri, 13 Nov 2020 19:07:10 GMT
header: Content-Type: application/json
header: X-RateLimit-Limit: 60
header: X-RateLimit-Remaining: 59

这是我使用 requests.post(url_login, data=data, timeout=3) 从 laravel 应用程序调用它并添加了“超时”但登录永远不会发生时的响应。如果没有超时,请求将永远不会返回并且服务器会关闭

HTTP/1.1 200 OK
Host: 127.0.0.1:8102
Date: Fri, 13 Nov 2020 23:05:38 GMT
Connection: close
X-Powered-By: PHP/7.4.2
Content-Type: text/html; charset=UTF-8
Cache-Control: no-cache, private
Date: Fri, 13 Nov 2020 23:05:38 GMT
Set-Cookie: XSRF-TOKEN= (deleted); 
    expires=Sat, 14-Nov-2020 01:05:38 GMT; 
    Max-Age=7200; 
    path=/
Set-Cookie: icollect_session=(deleted); 
    expires=Sat, 14-Nov-2020 01:05:38 GMT; 
    Max-Age=7200; 
    path=/; 
    httponly
<td>
<textarea class="form-control" id="FormControlTextarea" rows="10" cols="300">
send: b&#039;POST /api/apilogin HTTP/1.1\r\n
Host: 127.0.0.1:8102\r\n
User-Agent: python-requests/2.23.0\r\n
Accept-Encoding: gzip, deflate\r\n
Accept: */*\r\n
Connection: keep-alive\r\n
Content-Length: 48\r\n
Content-Type: application/x-www-form-urlencoded\r\n\r\n&#039;
send: b&#039;email=(deleted)&amp;password=(deleted);
</textarea>
</td>

更新:该帖子从未命中 api.php,因此问题一定与 python 脚本无法回发到调用它的应用程序有关...但是为什么呢?

更新: 我正在使用 JS ajax 来调用生成 python 脚本的 PHP 控制器。如果我在控制器中使用 PHP symfony process() 调用来调用包含 request.post() 的 python 脚本,则一旦 Symfony 抛出计时错误,ajax 最终会在 1 分钟后出错并出现 500 错误,然后 python 脚本继续和作品。现在只是为了了解为什么以及如何重新设计这个......

message: "Maximum execution time of 60 seconds exceeded",…
exception: "Symfony\Component\ErrorHandler\Error\FatalError"
file: "C:\Apache24\htdocs\laravel\vendor\symfony\process\Pipes\WindowsPipes.php"
line: 145
message: "Maximum execution time of 60 seconds exceeded"
trace: []

【问题讨论】:

【参考方案1】:

摆脱了 Symfony process() 并改为这个。发生的事情是 laravel 应用程序阻止 request.post 完成,因此服务器锁定......

Found here--谢谢@LucaM

function execInBackground($cmd) 
    if (substr(php_uname(), 0, 7) == "Windows")
        pclose(popen("start /B ". $cmd, "r")); 
    
    else 
        exec($cmd . " > /dev/null &");  
    
 

【讨论】:

以上是关于从 PHP 控制器使用 request.post() 调用 python 脚本永远不会返回。为啥?的主要内容,如果未能解决你的问题,请参考以下文章

Django 为 post.request 获得正确的返回值

php ajax请求和返回

只有 Django request.body 包含数据(不是 request.POST)

无法从 Django request.POST 获取 POST 数据

Django获得post.request的正确返回值

Django request.POST 从输入中获取数据