dongnai3960 2014-07-17 10:48
浏览 57
已采纳

遵循基于条件的REST路由

Context: I'm developing a turn-based game that requires client-server communication. The client uses RESTful requests via HTTPS and the server uses JSON responses. I'm not using REST because I need to expose the API, but because the REST paradigm was comfortable to get the client/server interaction sorted out. The server is to be written in PHP.

To help me out with routing the REST requests I've been searching for a useful routing library. There are a staggering amount of them available, but I have difficulties finding one that fits my specific needs. My problem is this: a player should only be allowed to follow certain routes if certain conditions are met. Obviously I can check if the conditions are met in the method that is called after a route match, but this seems an error-prone approach because conditions are the same for many routes. It would be much simpler to first do a partial match, then decide some conditions must be met to follow more specific routes under the partial match.

To illustrate, there are 4 levels of 'authentication' in the game:

  1. Not authenticated
  2. Authenticated as {id} (we know which player it is)
  3. Authenticated as {id} and participant in session {sesid}
  4. Authenticated as {id}, participant in session {sesid}, AND it's the player's turn

Assume for now that authentication happens in the background. Routes you can follow on each level, progressively:

LEVEL 1:

  • POST \Players- sign up

LEVEL 2:

  • PUT \Players\{id} - change profile
  • GET \Players\{id}\Sessions - get list of sessions
  • POST \Players\{id}\Sessions - create session
  • POST\Players\{id}\Inventory - buy items
  • PUT \Players\{id}\Invites\{sesid} - join invitation for session
  • DELETE \Players\{id}\Invites\{sesid} - decline invitation for session

LEVEL 3:

  • GET \Players\{id}\Sessions\{sesid} - get session state
  • DELETE \Players{id}\Sessions\{sesid} - cancel session

LEVEL 4:

  • PUT \Players\{id}\Sessions\{sesid}\... - set several session state parameters

So I wish to be check a few parameters up front before matching the next set of routes. I've spent quite some Google and Packagist time finding a suitable routing package (I've looked at the docs for Klein, Zend, PHP-Router, Fat_free, Slim, TORO, Aura, FlightPHP, Phalcon, FuelPHP to name but a few) but almost all libraries require that you define routes up front and find a single match in one go - sometimes allowing you to set an order, and sometimes going from most specific to least specific, but mostly executing only one route.

I would be somewhat helped if I could do a partial route match in a defined order - for example, any route starting with players\{id} first checks authentication, exits when it's not there, whilst continuing to check the next pattern in defined order if authentication is OK.

Any routing library that would allow me to match and execute a route on the spot would also help - provided that it can do partial matches so that I can pick up parameters I require to check the authentication level requirements (id, sesid). Obviously, to keep things slim I'd prefer a library that is not part of a larger framework.

From the documentation pages I've read at Packagist, I have difficulties finding out if you can partially match routes in certain libraries - match parameters, yes, but routes? - and sometimes it's unclear if the first match found is the only match. Any pointers?

Or am I missing a more straightforward solution here?

  • 写回答

1条回答 默认 最新

  • dsbruqxgt820011351 2014-07-17 19:06
    关注

    Well, to start with, Klein will let you execute more than one route for the same callback, like this:

    // This route matches everything
    $klein->respond('*', function ($request, $response, $service) { myAuthFunction($request); });
    //other routes
    // This route matches only a specific path
    $klein->respond('GET', '/Players/[i:id]/Sessions', function ($request, $response, $service) { echo "This is the Sessions page for User ID $request->id"; })
    

    Check out the section on routing, and scroll to the paragraph that starts with "Note." If I remember correctly, the routes are executed in the order they are declared, so you catch all route you use for checking credentials would need to be first, before the more specific ones.

    In your auth function, you need to throw an exception, in order to prevent the later route from being run, and then catch it. This link shows how to catch throw HTTP errors. To throw one, simply call $router->abort(404) inside your callback. You have to send $router (your Klein instance ) using use, so your callback would actually be:

    $klein->respond('*', function ($request, $response, $service) use ($klein) { myAuthFunction($request); });
    

    There's also a routing namespace system, which I looked at, but haven't used myself, but might be helpful for what you're trying to do.

    Lastly, what I ended up doing, was grouping my actions into Controllers, and doing a permission check in the constructor for the controller. I don't want to waste a lot of time explaining how to set that up if you don't think it's going to be relevant for you, but I can provide more details upon request.

    A final note, the docs say to install Klein like this:

    php composer.phar require klein/klein v2.0.x
    

    But I've found that the dev-master code works better, so I would recommend doing this:

    php composer.phar require klein/klein dev-master
    

    Hope that helps!

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 luckysheet
  • ¥15 ZABBIX6.0L连接数据库报错,如何解决?(操作系统-centos)
  • ¥15 找一位技术过硬的游戏pj程序员
  • ¥15 matlab生成电测深三层曲线模型代码
  • ¥50 随机森林与房贷信用风险模型
  • ¥50 buildozer打包kivy app失败
  • ¥30 在vs2022里运行python代码
  • ¥15 不同尺寸货物如何寻找合适的包装箱型谱
  • ¥15 求解 yolo算法问题
  • ¥15 虚拟机打包apk出现错误