如何组织服务器端的ajax脚本

时间:2011-09-16 09:37:28

标签: php ajax jquery-ui

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

现在,ajax请求脚本基本上只有大约一百个if-else块,它们基于它们的功能而分成不同的文件。

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

我在考虑制作某种调度界面。 (不知道这是一个好的还是可行的想法。)我可以在哪里注册行动并以某种方式表明他们需要什么数据。然后,调度员将从适当的位置调用该函数。然后我可以通过一个脚本路由所有ajax请求,然后组织我想要的功能。我可以概述在不逐行读取实现的情况下调用某个操作所需的数据。我可能会从客户端访问我的服务器端类heirarchy。

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

2 个答案:

答案 0 :(得分:2)

我使用RPC风格的机制来实现我想你想要的。 免责声明:我已经在JS + PHP和JS + Python中成功实现了这个方案,因此可行。但它可能不安全。您必须采取所有适当的验证步骤,以确保它安全(特别是w.r.t.到代码/ SQL注入和XSS攻击)

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

例如,在客户端:

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

会写

  

对象(ID = 1532,名字= “foo” 的,无论= “栏”)

在控制台上。

我使用的通信协议如下:

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

    这里有许多特定于应用程序的假设,包括可能引发的异常类型,保留关键字等......

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

答案 1 :(得分:0)

您可以在此处查看:http://www.phpapi.org/

来自说明:

“这是您可以通过简单的Web计算器开发Web系统到最软化的CRM / ERP / CMS / ETC的框架.PHP-API提供的是:代码的一般结构,非常简单的可扩展API代码结构,与API的JavaScript连接(以及添加新模块/方法处理程序的简单方法),....“