3

我在索引操作app/controllers/UsersController.php中做了一个简单的操作。Users::find('all');

该路径/users/index呈现用户数据的普通 HTML 输出。该路径/users/index.json呈现 HTML 输出的 JSON 等价物,这很好,除了它还公开了密码(它是散列的,但仍然......)。

我看到了两种避免这种情况的选择:

  1. fields在我的查找器中明确指定。
  2. 过滤Media::render()和取消设置任何敏感数据。

从长远来看,我觉得#2 可能更容易维护。有什么意见吗?是否有第三种更好的替代方案?

这就是我实现#2的方式:

<?php

namespace app\controllers;

use \lithium\net\http\Media;

class UsersController extends \lithium\action\Controller {
    protected function _init() {
        Media::applyFilter('render', function($self, $params, $chain) {
            if ($params['options']['type'] === 'json') {
                foreach ($params['data']['users'] as $user) {
                    $user->set([
                        'password' => null,
                        'salt' => null
                    ]);
                }
            }
            return $chain->next($self, $params, $chain);
        });
        parent::_init();
    }
}
?>

任何意见,将不胜感激。

4

4 回答 4

3

这个问题可能有很多答案和方法,具体取决于您的应用程序、可维护性、架构的优雅等......如果您只想删除用户密码等敏感字段,您的解决方案可以完成这项工作.

但!

过滤Media::render()似乎根本不是一个好主意。您在这里混合了关注点,最终会得到一个臃肿的过滤器,您可以在其中调整对象以删除您不想在 json 响应中公开的内容。

如果您每次都必须为应用程序中的每个控制器添加字段,那么使用字段可能还不够好。更糟糕的是,如果您的实体有 30 多个字段,并且根据当前用户,显示不同的信息 (OMG)!你最终会得到一个臃肿的控制器,在这里,你再次混合了关注点和责任:find()负责读取你的数据,而fields事情只是改变你的数据的表示(某种视图)。

所以?我们能做什么?

复制控制器逻辑
您可以通过将控制器中的过滤逻辑封装到 a 中来分离控制器中的过滤逻辑。if ($this->request->is('json')) { ... } 这意味着如果请求是htmljson(公共 api),相同的控制器操作会做出不同的响应。
这也不好 :)
稍微好一点的方法是通过复制控制器来拆分一些东西 => 第一组负责您的 json api,第二组负责响应 html 的“经典”控制器。
您可以通过添加controllers/api命名空间并重新配置 Dispatcherjson以在请求/响应的情况下使用此路径来使用Lithium 轻松完成此操作。

li3_jbuilder
在某些情况下,我对复制控制器并不满意。更好的方法是使用V部分MVC但这次来呈现 json 响应,并将它们作为第一类对象处理:json 视图!
这可以通过调整Media类配置并具有回退机制(如果*.json.php未找到 a,json_encode则对象没有过滤字段)来轻松完成。
我为 Lithium 构建了 li3_jbuilder,以便轻松构建 json 响应、嵌套对象、使用帮助程序以及将“表示”方面移动到视图层。
Jbuilder 的灵感来自 Rails 的jbuilder。仅供参考,红宝石社区也获得了RABL

Presenter 模式
虽然这种方法看起来很简单,但还有另一种有趣的方法,它更面向对象:使用 Presenter 模式(或装饰器)。
用户模型与 UserPresenter 类(普通的旧 php 类)相关联,负责提供要“呈现”的对象,尤其是在 json 响应(或应用程序中的任何位置)中。
Presenters 也可以帮助您清理复杂的视图逻辑,可测试且非常灵活。
演示者需要了解模型和它将处理的视图,因此您将把它们传递给initialize方法并将它们分配给实例变量。
只需在 Google 上搜索“Presenter 模式”或“Rails 演示者”(我使用的唯一使用此模式的框架),以了解有关该主题的更多信息

于 2013-08-30T16:52:43.257 回答
1

I had the same problem, I was printing emails and passwords when you added .json to the path.

So, since I am using MySql and I declared my $_schema in all models I did a little trick... I added a 'public' => true to all fields that I want to be requested from the database and used that in all queries like this:

$users = Users::find(array('fields' => Users::publicFields()));

and the publicFields method looks like this:

public static function publicFields() {
    $self = static::_object();

    $className = $self->meta()['name'];
    $schema = $self->schema();

    $fields = array_filter($schema->fields(), function($var) {
        return !empty($var['public']);
    });

    $names = array_keys($fields);

    for ($i = 0, $iMax = count($names); $i < $iMax; $i++) {
        $names[$i] = $className . '.' . $names[$i];
    }

    return $names;
}
于 2013-08-31T14:14:37.870 回答
1

显式指定fields有几个优点:

  • 您不会获得不需要的数据,因此它可能会更快
  • 如果您忘记取消设置,就不会意外泄漏数据
  • 当您指定需要哪些字段时,如果JSON格式发生变化,您会收到早期警告

SELECT * FROM这与在SQL中不做的原理相似。

于 2013-08-30T13:49:55.943 回答
0

同样在这里。unset(var)方法非常肮脏和危险。

我需要我的所有视图中的用户对象来呈现主菜单并进行一些用户交互。Controller::Render 方法通过$this->set()提供了这个额外的类。

在我的用户控制器中,我创建了一个新的 php 类“DSMember” 这个对象在我的视图中需要一些我需要的公共属性。这里不提供密码和安全相关的东西。

所以你在用户展示逻辑(DS = Display)和核心相关的东西之间有一个清晰的界限。

class DSMember
{
    public $id;
    public $profile;
    public $uuid;
    public $messages;

    function __construct ($user) //$user is the Users::Object
    {
        $this->id = $user->id;
        $this->uuid = $user->uuid;
        $this->profile = $user->user_profile;
        $this->messages = $user->messages;
    }

}

渲染方法被重载:::

public function render (array $options = array())
    {
        if ($this->session)
        {
            $member = new DSMember ($this->member);
            $this->set (compact ('member'));
        }
        parent::render ($options);
    }

因此 DSMember 对象在所有 HTML 视图和 JSON 渲染输出中都可用。热门用户模型是隐藏的。

上面提到了很多不同的方法。对于我的应用程序,这种方式似乎很好。

于 2013-12-30T12:06:35.950 回答