php - 如何组织服务器端ajax脚本

标签 php ajax jquery-ui

我最近的大部分工作都涉及扩展和修复 ajax 操作。但是操作列表的大小相当难以管理,而且由于我不是原作者并且评论很少,我花了很多时间跟踪代码路径并试图找出哪个 jquery 事件触发了操作以及它是否发送了请求的正确数据。

现在,ajax 请求脚本基本上只有大约一百个 if-else block ,根据它们的功能松散地分成不同的文件。

是否有相关的设计模式或 php 习惯用法来帮助我更好地组织 ajax 请求的 php 部分?

我正在考虑制作某种调度界面。 (不知道这是一个好主意还是可行的主意。)我可以在哪里注册操作并以某种方式指示它们需要什么数据。然后调度员会从适当的地方调用该函数。然后我可以通过单个脚本路由所有 ajax 请求,并根据需要组织我的函数。而且我可以在不逐行阅读其实现的情况下大致了解调用某个操作所需的数据。我可能可以从客户端访问我的服务器端类层次结构。

这听起来可行吗?这样安全吗?还有其他可能效果更好的方法吗?其灵感基本上是 smalltalk 风格的消息传递。我主要担心的是我将引入一个跨端请求伪造漏洞,或者代码中已经存在一个漏洞,但由于难以阅读,我错过了。

最佳答案

我使用 RPC 风格的机制来实现我认为你想要的。 免责声明:我已经在 J​​S+PHP 和 JS+Python 中成功实现了这个方案,所以它是可行的。但它可能不安全。您必须采取所有适当的验证步骤以确保它是安全的(尤其是针对代码/SQL 注入(inject)和 XSS 攻击)

想法是让一个 PHP 脚本处理 RPC 请求,通过 GET 和 POST 接收方法名称及其参数,并将 JSON 输出回 Javascript 端。

例如,在客户端:

API.rpc('getItemById', 1532, function(item) { console.log(item); });

会写

Object(id=1532,name="foo",whatever="bar")

在控制台上。

我使用的通信协议(protocol)如下:

  1. 客户端向 RPC 处理程序脚本发送 HTTP 请求,使用 GET 或 POST。限制是“方法”必须 始终在 GET 中提供,并且所有参数都必须 URL 编码。否则,所有参数都以键=值对的形式给出,并且可以是请求 (GET) 或有效负载 (POST) 的一部分
  2. 服务器总是以 HTTP 200 响应(否则意味着发生了非常糟糕的事情)。它仅响应 JSON 数据。返回的对象至少有 2 个成员。
    • “成功”成员始终存在,并指示调用是否成功 - 即没有抛出异常
    • 如果成功,'ret'成员包含函数的返回值
    • 如果抛出异常,'message' 成员包含异常消息(我更喜欢在这里发送整个回溯,但这对于敏感环境肯定不利)

(1) 在 javascript 方面(假设是 jQuery,编码如我所想,所以这可能有问题):

API = function() {
  this.rpc = function(method, args, callback) {
    return $.ajax({
      url: 'rpcscript.php?method='+encodeURIComponent(args.method),
      data: args,
      type: 'post', //only the method name is sent as a GET arg
      dataType: 'json'
      error: function() {
        alert('HTTP error !'); // This is e.g. an HTTP 500, or 404
      },
      success: function(data) {
        if (data.success) {
          callback(data.ret);
        } else {
          alert('Server-side error:\n'+data.message);
        }
      },
    });
  }
}

然后可以添加syncRPC()等快捷函数进行同步调用等

(2) PHP端(稍微修改运行代码):

class MyAPI
{
    function getItemById($id)
    {
            // Assuming the $db is a database connection returning e.g. an associative array with the result of the SQL query. Note the (int) typecast to secure the query - all defensive measures should be used as usual.
        return $db->query("SELECT * FROM item WHERE id = ".(int)$id.";");
    }
}

class RemoteProcedureCall
{
    function __construct()
    {
        $this->api = new MyAPI();
    }

    function serve()
    {
        header("Content-Type: application/json; charset=utf-8");

        try
        {
            if (!isset($_GET['method']))
                throw new Exception("Invalid parameters");

            $methodDesc = array($this->api, $_GET['method']);

            if (!method_exists($methodDesc[0], $methodDesc[1]) || !is_callable($methodDesc))
                throw new Exception("Invalid parameters");

            $method = new ReflectionMethod($methodDesc[0], $methodDesc[1]);
            $params = array();
            foreach ($method->getParameters() as $param)
            {
                // The arguments of the method must be passed as $_POST, or $_GET
                if (isset($_POST[$param->getName()]))
                    // OK, arg is in $_POST
                    $paramSrc = $_POST[$param->getName()]; 

                elseif (!in_array($param->getName(),array('action','method')) 
                    && isset($_GET[$param->getName()])
                    && !isset($paramSrc[$param->getName()]))
                    // 'action' and 'method' are reserved $_GET arguments. Arguments for the RPC method
                    // can be any other args in the query string, unless they are already in $_POST.
                    $paramSrc = $_GET[$param->getName()];

                if (!isset($paramSrc))
                {
                    // If the argument has a default value (as specified per the PHP declaration
                    // of the method), we allow the caller to use it - that is, not sending the
                    // corresponding parameter.
                    if ($param->isDefaultValueAvailable())
                        $p = $param->getDefaultValue();
                    else
                        throw new Exception("Invalid parameters");
                }
                else
                {
                    $p = $paramSrc;
                }

                $params[$param->getName()] = $p;
                unset($paramSrc);
            }

            $ret = $method->invokeArgs($db, $params);

            echo json_encode(array('success' => true, 'ret' => $ret));
        }
        catch (Exception $e)
        {
            echo json_encode(array('success' => false, 'message' => $e->getMessage()."\n".$e->getBacktrace()));
        }
    }
};

$rpc = RemoteProcedureCall();
$rpc->serve();

这里有很多应用特有的假设,包括可能抛出的异常种类、保留关键字等……

无论如何,我希望这能为您的问题提供一个好的起点。

关于php - 如何组织服务器端ajax脚本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7442754/

相关文章:

JQuery 对话框 : How to do partial page refresh and get new dialogs each time

jquery - jquery mobile 中刷新单选按钮的问题

php - 切换内容时 jQuery .load 问题

javascript - 网页如何确定是否/何时查看某些内容?

javascript - 使用 headless Chrome 以编程方式捕获 AJAX 流量

jQuery UI selectable 不显示选择轮廓,为什么?

php - WP 增强媒体库插件 - 如何按查询中的媒体类别进行过滤?

php - 如果 mySQL 行的innerHTML X,则将其移动到表的顶部

php - min 函数在 mysql 和 wordpress 插件中不起作用

php - jQuery Ajax 返回 html 和 json 数据