Yii2 REST POST请求报错:缺少必填参数words
我在Yii2框架开发翻译功能,通过REST POST请求发送单词列表到服务器,翻译后返回结果,但发送请求时浏览器JS控制台返回400错误,错误信息:
name "Bad Request", message "Missing required parameters: words", code 0, status 400, type "yii\web\BadRequestHttpException"
使用php yii serve启动服务,相关代码如下:
JavaScript代码
window.onload = (event) => { document.querySelector("#translate").addEventListener("click", translate) } function translate() { const textarea = document.getElementById("words"); const words = textarea.value.replace(/\r\n/g, "\n").split("\n"); post('http://localhost:8080/index.php/rest-translation/translate', words) } async function post(url, words) { console.log(words) let options = { method: "POST", body: JSON.stringify({words: words}) } const response = await fetch(url, options) if (response.ok) { response.json() } }
视图代码
<?php use yii\helpers\Html; use yii\widgets\DetailView; use yii\widgets\ActiveForm; use app\assets\TranslationAsset; /** @var yii\web\View $this */ /** @var app\models\Book $book */ /** @var app\models\BookSection $section */ $this->title = $section->title; $this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Section'), 'url' => ['book']]; $this->params['breadcrumbs'][] = $this->title; \yii\web\YiiAsset::register($this); ?> <div class="translate-section"> <h1><?= Html::encode($this->title) ?></h1> <?= DetailView::widget([ 'model' => $book, 'attributes' => [ 'id', 'name', ], ]) ?> <?= DetailView::widget([ 'model' => $section, 'attributes' => [ 'title', ], ]) ?> <div> <?php $form = ActiveForm::begin(); ?> <table> <tr> <td><textarea id="words"></textarea></td> <td><textarea id="first-translation"></textarea></td> <td><textarea id="second-translation"></textarea></td> </tr> </table> <button id="translate" type="button">Translate</button> <div class="form-group"> <?= Html::submitButton(Yii::t('app', 'Save'), ['class' => 'btn btn-success']) ?> </div> <?php ActiveForm::end(); ?> </div> <?php TranslationAsset::register($this) ?> </div>
REST控制器代码
<?php namespace app\controllers; use app\models\Translation; use yii\rest\ActiveController; class RestTranslationController extends ActiveController { public $modelClass = 'app\models\Translation'; public function actionTranslate($words) { return Translation::translate($words); } public function behaviors() { $behaviors = parent::behaviors(); // remove authentication filter $auth = $behaviors['authenticator']; unset($behaviors['authenticator']); // add CORS filter $behaviors['corsFilter'] = [ 'class' => \yii\filters\Cors::class, ]; // re-add authentication filter $behaviors['authenticator'] = $auth; // avoid authentication on CORS-pre-flight requests (HTTP OPTIONS method) $behaviors['authenticator']['except'] = ['options']; return $behaviors; } }
模型代码
<?php namespace app\models; use Yii; /** * This is the model class for table "translation". * * @property int $id * @property int $sourceEntryId * @property string $languageId * @property string $typeId * @property int $translationEntryId */ class Translation extends \yii\db\ActiveRecord { /** * {@inheritdoc} */ public static function tableName() { return 'translation'; } /** * {@inheritdoc} */ public function rules() { return [ [['sourceEntryId', 'languageId', 'typeId', 'translationEntryId'], 'required'], [['sourceEntryId', 'translationEntryId'], 'integer'], [['languageId', 'typeId'], 'string', 'max' => 10], ]; } /** * {@inheritdoc} */ public function attributeLabels() { return [ 'id' => Yii::t('app', 'ID'), 'sourceEntryId' => Yii::t('app', 'Source Entry ID'), 'languageId' => Yii::t('app', 'Language ID'), 'typeId' => Yii::t('app', 'Type ID'), 'translationEntryId' => Yii::t('app', 'Translation Entry ID'), ]; } public static function translate($words) { //<TO DO>:Find and return the translation. } /** * {@inheritdoc} * @return TranslationQuery the active query used by this AR class. */ public static function find() { return new TranslationQuery(get_called_class()); } }
config/web.php配置代码
<?php $params = require __DIR__ . '/params.php'; $db = require __DIR__ . '/db.php'; $config = [ 'id' => 'basic', 'basePath' => dirname(__DIR__), 'bootstrap' => ['log'], 'aliases' => [ '@bower' => '@vendor/bower-asset', '@npm' => '@vendor/npm-asset', ], 'components' => [ 'request' => [ // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation 'cookieValidationKey' => 'xxxxxxxx', 'parsers' => [ 'application/json' => 'yii\web\JsonParser', ] ], /*'response' => [ 'format' => yii\web\Response::FORMAT_JSON, 'charset' => 'UTF-8', // ... ],*/ 'cache' => [ 'class' => 'yii\caching\FileCache', ], 'user' => [ 'identityClass' => 'app\models\User', 'enableAutoLogin' => true, ], 'errorHandler' => [ 'errorAction' => 'site/error', ], 'mailer' => [ 'class' => \yii\symfonymailer\Mailer::class, 'viewPath' => '@app/mail', // send all mails to a file by default. 'useFileTransport' => true, ], 'log' => [ 'traceLevel' => YII_DEBUG ? 3 : 0, 'targets' => [ [ 'class' => 'yii\log\FileTarget', 'levels' => ['error', 'warning'], ], ], ], 'db' => $db, 'urlManager' => [ 'enablePrettyUrl' => true, 'showScriptName' => true, 'enableStrictParsing' => false ] ], 'params' => $params, ]; if (YII_ENV_DEV) { // configuration adjustments for 'dev' environment $config['bootstrap'][] = 'debug'; $config['modules']['debug'] = [ 'class' => 'yii\debug\Module', // uncomment the following to add your IP if you are not connecting from localhost. //'allowedIPs' => ['127.0.0.1', '::1'], ]; $config['bootstrap'][] = 'gii'; $config['modules']['gii'] = [ 'class' => 'yii\gii\Module', // uncomment the following to add your IP if you are not connecting from localhost. //'allowedIPs' => ['127.0.0.1', '::1'], ]; } return $config;
环境信息:PHP 8.1.2-1ubuntu2.14 (cli)、Zend Engine v4.1.2、Zend OPcache v8.1.2-1ubuntu2.14、Xdebug v3.2.1、Yii2 2.0.48.1。
问题修复方案
1. 前端请求添加Content-Type头
Yii2的JsonParser需要请求头Content-Type: application/json才能正确解析JSON格式的请求体,当前前端未设置该头,导致框架无法识别请求体内容。
修改前端post函数:
async function post(url, words) { console.log(words) let options = { method: "POST", headers: { 'Content-Type': 'application/json' // 新增这行 }, body: JSON.stringify({words: words}) } const response = await fetch(url, options) if (response.ok) { return response.json() // 补充return,让调用方能获取返回结果 } }
2. 调整控制器参数获取方式
Yii2动作方法的参数默认从URL或表单数据中读取,JSON请求体的参数需要从请求对象中主动获取,不能直接作为方法参数。
修改RestTranslationController的actionTranslate方法:
public function actionTranslate() { $words = Yii::$app->request->getBodyParam('words'); if ($words === null) { throw new \yii\web\BadRequestHttpException('Missing required parameters: words'); } return Translation::translate($words); }
3. 可选:优化URL配置(推荐)
在config/web.php的urlManager中添加REST路由规则,简化访问URL:
'urlManager' => [ 'enablePrettyUrl' => true, 'showScriptName' => false, // 去掉URL中的index.php 'enableStrictParsing' => true, 'rules' => [ 'POST rest-translation/translate' => 'rest-translation/translate', ], ],
修改后前端请求URL可简化为http://localhost:8080/rest-translation/translate。
验证步骤
- 重启
php yii serve服务 - 在前端textarea输入单词,点击Translate按钮,检查控制台是否仍有400错误
- 可在控制器方法中添加
var_dump($words); exit;验证参数是否正确获取
内容的提问来源于stack exchange,提问作者Jaimitove

