PHP 后端表单验证和请求处理

创建好前端的联系表单视图后,接下来,我们来编写提交表单后后端的 PHP 处理逻辑。

数据表和模型类

我们会将用户提交的请求数据保存到 messages 表中,所以我们需要在数据库中新增这张数据表:

--
-- 数据库: `blog`
--

-- --------------------------------------------------------

--
-- 表的结构 `messages`
--

CREATE TABLE `messages` (
  `id` int UNSIGNED NOT NULL,
  `name` varchar(50) COLLATE utf8mb4_general_ci NOT NULL,
  `email` varchar(100) COLLATE utf8mb4_general_ci NOT NULL,
  `phone` varchar(20) COLLATE utf8mb4_general_ci NOT NULL,
  `content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `created_at` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='消息表';

然后在 blog 项目的 app/model 目录下新建对应的模型类 Message

<?php
namespace App\Model;

use Illuminate\Database\Eloquent\Model;

class Message extends Model
{
    public $timestamps = false;
}

表单数据处理逻辑

做好上述准备后,接下来,我们在 HomeController 控制器的 contact 方法中,编写表单数据获取、验证和保存代码:

public function contact()
{
    if ($this->request->getMethod() == 'GET') {
        // 联系表单页面
        ...
    } else {
        // POST 提交表单处理逻辑
        $name = $this->request->get('name');
        $email = $this->request->get('email');
        $phone = $this->request->get('phone');
        $content = $this->request->get('message');
        // 验证表单输入数据
        if (empty($name)) {
            throw new ValidationException('用户名不能为空');
        }
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new ValidationException('请输入正确的邮箱地址');
        }
        if (!preg_match('/^1[34578]{1}\d{9}$/', $phone)) {
            throw new ValidationException('请输入正确的手机号码');
        }
        if (empty($content)) {
            throw new ValidationException('消息内容不能为空');
        }
        $message = new Message();
        $message->name = filter_var($name, FILTER_SANITIZE_STRING);
        $message->email = $email;
        $message->phone = $phone;
        $message->content = filter_var($content, FILTER_SANITIZE_STRING);
        $message->created_at = Carbon::now();
        if ($message->save()) {
            (new Response('消息保存成功', 200))->send();
        }
        (new Response('保存消息失败,请重试!', 500))->send();
    }
}

这里,我们通过 $this->request->get 方法获取表单请求数据,然后对这些表单数据进行简单的验证,比如用户名和消息内容不能为空、邮箱格式必须合法(使用 PHP 内置的 filter_var 方法进行过滤,该方法通过传入的第二个验证过滤器常量参数对变量值进行验证,还可以支持 IP、URL 等其他字符串格式的校验)、手机号符合正则匹配规则,如果验证不通过会抛出 ValidationException 异常,关于异常响应的处理,我们稍后会介绍。

如果所有请求数据通过验证,就可以通过 Message 模型类实例将其保存到数据库中了。这里,对于用户名和消息内容,我们还调用了 filter_var 方法,并在第二个参数传入「消毒」过滤器常量参数对其进行处理,以避免字符串中包含 HTML 标签,出现 XSS 攻击隐患。

因此,filter_var 函数可以根据传入的第二个参数标识进行不同的操作,既可以用于字段验证,也可以用作消毒处理,还可以通过回调函数进行额外的自定义操作,更多细节请参考 PHP 官方文档关于该函数的介绍。

消息保存成功后,我们通过 Response 对象发送响应给客户端。

异常响应处理

在测试表单请求处理逻辑之前,我们来介绍下对异常响应的处理。

在上面的代码中,当请求字段验证失败后,会抛出 ValidationException 异常(该异常类定义在 app/http/exception 目录下):

<?php
namespace App\Http\Exception;

class ValidationException extends \Exception
{

}

此时,程序就终止了,不能继续往后执行了,那么这种情况下该如何将异常信息发送给客户端呢?

这里,我们可以借助之前在 PHP 错误和异常处理教程中介绍的全局异常处理器来捕获程序中抛出的所有未处理异常,进行兜底处理。

打开 app/bootstrap.php 文件,在里面定义一个注册全局异常处理器的方法:

// 注册全局异常处理器
function registerExceptionHandler()
{
    set_exception_handler(function ($exception) {
        $response = new Response();
        if ($exception instanceof ValidationException) {
            $response->setStatusCode(422);
            $response->setContent($exception->getMessage());
        } else {
            $response->setStatusCode(500);
            $response->setContent( '服务器异常');
        }
        $response->send();
    });
}

我们通过 set_exception_handler 注册全局异常处理器,在定义异常处理逻辑的回调函数中,可以看到,如果捕获到的异常是 ValidationException 实例,则将响应状态码设置为 422,然后通过 Response 响应实例发送验证错误信息给客户端,对于其他异常,目前先统一返回 500 错误。

最后在启动应用的 bootApp 方法中,调用这个注册全局异常处理器的 registerExceptionHandler 方法即可:

/**
 * 启动应用
 * @param Container $container
 * @return Container
 */
function bootApp(Container $container)
{
    registerExceptionHandler();
    ...
}

在联系表单页面发起请求

至此,我们就完成了 POST 表单请求的所有后端处理逻辑,在浏览器打开联系表单页面,如果输入了错误的手机号,会返回对应的验证错误消息:

如果所有表单数据都通过验证,则会看到消息发送成功提示:

当然,这里还有可以优化的地方,比如,在请求数据验证失败后,返回提交的请求数据填充对应的输入框,以免用户重新输入。

访问数据库,在 messages 表中应该可以看到最新插入的记录:

这样,完整的前后端表单请求功能就完成了,博客前端功能也就告一段落了,下篇教程,学院君会给大家如何纯手工搭建博客后台管理系统。

上一篇: 创建联系表单页面并通过 Ajax 提交表单请求数据

下一篇: 引入 SB Admin 2 作为后台管理系统主题