【问题标题】:Yii2: Defining a REST API Parameter other than 'ID'Yii2:定义“ID”以外的 REST API 参数
【发布时间】:2017-03-15 14:43:59
【问题描述】:

我正在尝试利用 YII2 REST API(基于高级模板)来创建我自己的服务。我目前正在成功使用以下 URL 向我返回一条“文章”记录:

http://service/articles/view?id=1

我现在正在尝试复制此代码,以便它适用于另一种记录类型。我的新记录有一个名为“key”的主键,我想用它来搜索。因此,我需要将参数名称“id”更改为“key”。

有人能解释一下如何在此 URL 上指定除“id”之外的参数吗?每当我省略 id 作为参数时,我都会收到“错误请求:缺少必需参数:id”。我不明白这个必需参数来自哪里以及如何更改或添加它。

相关类如下所示:

class ArticleController extends ActiveController
{
    /**
     * @var string
     */
    public $modelClass = 'frontend\modules\api\v1\resources\Article';
    /**
     * @var array
     */
    public $serializer = [
        'class' => 'yii\rest\Serializer',
        'collectionEnvelope' => 'items'
    ];

    /**
     * @inheritdoc
     */
    public function actions()
    {
        return [
            'index' => [
                'class' => 'yii\rest\IndexAction',
                'modelClass' => $this->modelClass,
                'prepareDataProvider' => [$this, 'prepareDataProvider']
            ],
            'view' => [
                'class' => 'yii\rest\ViewAction',
                'modelClass' => $this->modelClass,
                'findModel' => [$this, 'findModel']
            ],
            'options' => [
                'class' => 'yii\rest\OptionsAction'
            ]
        ];
    }

    /**
     * @return ActiveDataProvider
     */
    public function prepareDataProvider()
    {
        return new ActiveDataProvider(array(
            'query' => Article::find()->published()
        ));
    }

    /**
     * @param $id
     * @return array|null|\yii\db\ActiveRecord
     * @throws HttpException
     */
    public function findModel($id)
    {
        $model = Article::find()
            ->published()
            ->andWhere(['id' => (int) $id])
            ->one();
        if (!$model) {
            throw new HttpException(404);
        }
        return $model;
    }
}

class Article extends \common\models\Article implements Linkable
{
    public function fields()
    {
        return ['id', 'slug', 'category_id', 'title', 'body', 'published_at'];
    }

    public function extraFields()
    {
        return ['category'];
    }

    /**
     * Returns a list of links.
     *
     * @return array the links
     */
    public function getLinks()
    {
        return [
            Link::REL_SELF => Url::to(['article/view', 'id' => $this->id], true)
        ];
    }
}

提前致谢。

编辑:“前端”文件夹结构中 urlManager 的规范如下:

<?php
return [
    'class'=>'yii\web\UrlManager',
    'enablePrettyUrl'=>true,
    'showScriptName'=>false,
    'rules'=> [
        // Pages
        ['pattern'=>'page/<slug>', 'route'=>'page/view'],

        // Articles
        ['pattern'=>'article/index', 'route'=>'article/index'],
        ['pattern'=>'article/attachment-download', 'route'=>'article/attachment-download'],
        ['pattern'=>'article/<slug>', 'route'=>'article/view'],

        // Api
        ['class' => 'yii\rest\UrlRule', 'controller' => 'api/v1/article', 'only' => ['index', 'view', 'options']],
        ['class' => 'yii\rest\UrlRule', 'controller' => 'api/v1/user', 'only' => ['index', 'view', 'options']]
    ]
];

【问题讨论】:

  • 查看ViewAction文档:yiiframework.com/doc-2.0/yii-rest-viewaction.html#run()-detail,默认需要接收$id参数。我要做的是创建一个新操作,比如说view2,然后添加您需要的功能。
  • 看起来您使用的规则与 default yii2 routing for REST 通常实施的规则不同。您能否也显示您的urlManager 设置?
  • 感谢您的回复,@gmc。如果我按照您的建议创建一个新方法,您是否知道(或者可以让我参考文档)在哪里配置“id”值?
  • @Salem,我不确定在哪里可以找到 urlManager 设置(我有点新手)。
  • 您会在配置文件中找到它。这是定义您的 url rules 的地方。你会在我之前评论中链接的文档中看到它。

标签: yii2 yii2-advanced-app


【解决方案1】:

这可以通过在配置文档中向您的 urlmanager 添加规则来完成,可以找到 here

例子:

'rules' => [
    [   'class' => 'yii\rest\UrlRule',
        'pluralize' => false,
        'controller' => [
            'yourController',
        ],
        'patterns' => [
            'PUT,PATCH {id}' => 'update',
            'DELETE {id}' => 'delete',
            'GET,HEAD {id}' => 'view',
            'GET,HEAD {key}' => 'view',
            'POST' => 'create',
            'GET,HEAD' => 'index',
            '{id}' => 'options',
            'OPTIONS' => 'options',
            'PUT,PATCH {key}' => 'update',
        ],
         'tokens' => [
            '{id}' => '<id:\\d[\\d,]*>',
            '{type}' => '<type:\\w[\\w,]*>',
            '{key}' => '<key:\\w[\\w,]*>',
        ],
    ],
]

将动作映射添加到您的控制器

class YourController extends ActiveController {

    public $modelClass = 'your\model\Class';

    public function actions() {
        return array_merge(parent::actions(), [
            'view' => [
                'class' => \rest\your\ViewAction',
                'modelClass' => $this->modelClass,
            ],
        ]);
    }
}

并将关键参数添加到您的操作中

class ViewAction  extends \yii\rest\ViewAction {

    public function run($id = null, $key = null)
    {
        // your code
    }
}

现在你应该可以做到了

GET your/{keyparam} -&gt; wil resolve to yourController -&gt; ViewAction

【讨论】:

  • 嗨@Tim,你能告诉我在我的例子中我需要的具体设置,只接受GET URL中的“key”而不是“id”吗?我已经尝试了所有这些设置的许多排列,但无法弄清楚如何省略“id”参数。不幸的是,文档对我来说不是不言自明的。我试过添加一个模式,说 'GET,HEAD {key}' =>'view' 但这不起作用。
  • @SimpleGuy 为我迟到的回复道歉,但我已经更新了如何配置控制器和相应视图的答案,希望对您有所帮助
【解决方案2】:

我不知道这是否是官方的做法,但我创建了一个额外的 Action 来处理这种情况。似乎“id”参数来自反映动作中的“run”方法调用,所以我可以使用称为“key”的不同参数的唯一方法是完全像这样定义函数:

class ExtraAction extends Action  // in yii/rest
{
    public function run($key)  // NOTE: The name 'key' is reflected and then becomes an expected parameter
    {
        $model = $this->findModel($key);

        if (!$model) {
            throw new HttpException(404);
        }
        return $model;
    }
}

完成后,我需要像这样更改我的 actions() 定义:

    'view' => [
        'class' => 'yii\rest\ExtraAction',
        'modelClass' => $this->modelClass,
        'findModel' => [$this, 'findModel']
    ],

然后控制器中的findModel($id) 将接收在GET URL 中定义的'key' 参数(例如api/v1/article?key=blah123)。

然后我可以根据我拥有的不同唯一键执行查找:

public function findModel($id)
{
    $model = Record::find()
    ->andWhere(['key_field' => $id])
    ->one();    
} 

我不知道这是否是正确的做法,但它似乎确实适用于这种情况。

【讨论】:

    【解决方案3】:

    像往常一样在ArticleController 中创建新方法:

    public function actionView2($your_desired_parameter) {
        $article = Article::find()->where(['your_desired_parameter'=>$your_desired_parameter]);
        if (!$article->exists()) 
            throw new NotFoundHttpException();
        return $article->one();
    }
    

    【讨论】:

    • 谢谢@gmc,虽然我还需要在控制器中添加“view2”操作吗?如果是这样,我的“类”属性应该是什么?如果我将此设置为“ViewAction”,它将再次坚持我需要提供一个 ID。
    • 我不是 100% 确定,但我认为您不需要在此处添加它
    • 不幸的是,仅添加“actionView2”不起作用(“找不到页面”)。我似乎需要添加一个动作,但如果我将“view”动作复制并粘贴为“view2”,我再次需要提供一个“id”(即使使用您提到的代码)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-24
    • 1970-01-01
    • 2021-07-24
    • 2022-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多