开发者

How to organize server side ajax scripts

开发者 https://www.devze.com 2023-04-05 20:27 出处:网络
Much of my work lately has involved expanding and bug fixing ajax actions. But the action lists are fairly unmanageable in size, and since I was not the original author and the comments are sparse, I

Much of my work lately has involved expanding and bug fixing ajax actions. But the action lists are fairly unmanageable in size, and since I was not the original author and the comments are sparse, I spend a great deal of time tracing the code paths and trying to figure out which jquery event triggered the action and if it sent the proper data with the request.

Right now the ajax request scripts are basically just about a hundred if-else blocks split up into different files based loosely on their function.

Is there a relevant design pattern or a php idiom to help me better organize the php part of the ajax requests?

I was thinking of maybe making some sort of dispatch interface. (Don't know if it is a good or workable idea.) where I can register actions and somehow indicate what data they require. The dispatcher would then call the function from the appropriate place. Then I could route all ajax requests through a single script and organize my functions however I want. And I can have an overview of what data is required to call a certain action without reading its implementation line by line. I would potentially have access to my server-side class heirarchy from the client side.

Does this sound workable? Is this safe? Are there other ways that might w开发者_如何学Pythonork better? The inspiration for this was basically smalltalk style message passing. My major worry is that I am going to introduce a cross-side request forgery vulnerability or that there is already one present in the code and due to it being difficult to read, I missed it.


I use a RPC-style mechanism to achieve what I think you want. Disclaimer: I've successfully implemented this scheme in JS+PHP and JS+Python, so it is workable. But it might not be secure. You have to take all appropriate verification steps to make sure it is secure (especially w.r.t. to code/SQL injection and XSS attacks)

The idea is to have a single PHP script that processes the RPC requests, receiving the method name and its argument through both GET and POST, and outputs JSON back to the Javascript side.

For instance, on the client side:

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

would write

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

on the console.

The communication protocol I use is the following:

  1. the client sends an HTTP request to the RPC handler script, using either GET or POST. The restrictions are that the 'method' must always be provided in the GET, and that all arguments must be URL-encoded. Otherwise, all arguments are given as key=value pairs and can be part of the request (GET) or the payload (POST)
  2. the server always responds with an HTTP 200 (otherwise it means that a very nasty thing happened). It responds only with JSON data. The returned object has at least 2 members.
    • the 'success' member is always there, and indicates if the call succeeded - i.e. that no exception was thrown
    • if successful, the 'ret' members contains the return value of the function
    • if an exception was thrown, the 'message' member contains the exception message (I prefer sending the whole backtrace here, but that's certainly not good for sensitive environments)

(1) On the javascript side (assuming jQuery, coding as I think, so this may be buggy):

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);
        }
      },
    });
  }
}

You can then add shortcut functions such as syncRPC() to perform synchronous calls, etc.

(2) On the PHP side (slightly modified running code):

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();

There are many application-specific assumptions here, including the kind of exceptions that may be thrown, the reserved keywords, etc ...

Anyway I hope this provides a good starting point for your problem.


You can have a look here: http://www.phpapi.org/

From description:

"This is the skeleton upon which you can develop a web-system from a simple Web Calculator to the most sofisticated CRM/ERP/CMS/ETC. What PHP-API provides is: a general structure of the code, a very simple extendable API code structure,JavaScript connectivity with the API ( with an easy way of adding new modules/method handlers ), ...."

0

精彩评论

暂无评论...
验证码 换一张
取 消