CodeIgniter - AJAX 注册表单验证

Posted

技术标签:

【中文标题】CodeIgniter - AJAX 注册表单验证【英文标题】:CodeIgniter - AJAX sign up form validation 【发布时间】:2019-09-15 20:54:35 【问题描述】:

我正在为我的学校项目使用 CodeIgniter 和 Bootstrap 构建一个小型 Web 应用程序,目前我正在尝试使用 AJAX 配置表单验证。我怀疑问题出在控制器上,但我无法弄清楚它到底是什么。当我输入有效数据时,一切正常,但当验证出现错误或所有字段为空时,我会被重定向到“http://[::1]/appname/index.php/users/signup”并看到以下消息:

"title":"Sign up","messages":"csrf_test_name":"","name":"

The Name field is required.<\/p>","email":"

The Email field is required.<\/p>","password":"

The Create password field is required.<\/p>","password2":""

另外,请注意我在“autoload.php”文件中加载“form_validation”库和“form”助手。提前致谢!

这是我的注册表单视图(没有页眉和页脚,它们是模板化的):

<div class="container">
<br>
    <div class="text-center">
        <h1 class="display-4">Create Account</h1>
    </div>
<br>
<div class="row justify-content-md-center">

<div class="col-6 card bg-light">
<article class="card-body mx-auto">

<?php echo form_open('users/signup', array('id'=>'signup_form')); ?>
    <div class="form-group input-group">
        <div class="input-group-prepend">
            <span class="input-group-text"> <i class="fa fa-user"></i> </span>
         </div>
        <input class="form-control" name="name" placeholder="Name" type="text">
    </div> <!-- form-group// -->
    <div class="form-group input-group">
        <div class="input-group-prepend">
            <span class="input-group-text"> <i class="fa fa-envelope"></i> </span>
         </div>
        <input class="form-control" name="email" placeholder="Email" type="email">
    </div> <!-- form-group// -->
    <div class="form-group input-group">
        <div class="input-group-prepend">
            <span class="input-group-text"> <i class="fa fa-lock"></i> </span>
        </div>
        <input class="form-control" name="password" placeholder="Create password" type="password">
    </div> <!-- form-group// -->
    <div class="form-group input-group">
        <div class="input-group-prepend">
            <span class="input-group-text"> <i class="fa fa-lock"></i> </span>
        </div>
        <input class="form-control" name="password2" placeholder="Repeat password" type="password">
    </div> <!-- form-group// -->
    <div class="form-group">
        <button type="submit" class="btn btn-primary btn-block"> Create Account  </button>
    </div> <!-- form-group// -->
    <p class="text-center">Have an account? <a href="login">Log In</a> </p>
<?php echo form_close(); ?>
</article>
</div> <!-- card.// -->

</div>
</div>

这是 AJAX 脚本:

<script type="text/javascript">
var sUrequest;
$("#signup_form").submit(function(e)
    e.preventDefault();
    if(sUrequest)
        sUrequest.abort();
    
    var inputs=$(this).find("input, select, button, textarea"),
    serializedData=$(this).serialize(),
    postURL=$(this).attr("action");
    inputs.prop("disabled",true);
    sUrequest=$.ajax(
        url:postURL,
        type:"post",
        data:serializedData,
    );
    sUrequest.done(function(response,statusText,jqXHR)
        $.each(response.messages, function(key, value) 
            var element = $('#' + key);

            element.closest('div.form-group input-group')
            .removeClass('has-error')
            .addClass(value.length > 0 ? 'has-error' : 'has-success')
            .find('.text-danger')
            .remove();

            element.after(value);
        );
    );
    sUrequest.fail(function(jqXHR,statusText,thrownError)
        console.log("jq:"+jqXHR+" st:"+statusText+" te:"+thrownError);
    );
    sUrequest.always(function()
        inputs.prop("disabled",false);
    );
);

</script>

这是用户控制器:

<?php
  class Users extends CI_Controller 
    # Register new user
    public function signup() 
      $data['title'] = 'Sign up';

      array('success' => FALSE, 'messages' => array());

      $this->form_validation->set_error_delimiters('<p class="text-danger">', '</p>');
      $this->form_validation->set_rules('name', 'Name', 'required');
      $this->form_validation->set_rules('email', 'Email', 'required|callback_check_email_exists');
      $this->form_validation->set_rules('password', 'Create password', 'required');
      $this->form_validation->set_rules('password2', 'Repeat password', 'matches[password]');

      if($this->form_validation->run() === FALSE) 
        // I am not sure if I need this commented part, since AJAX is supposed to perform all the actions without reloading the page.
        // $this->load->view('templates/header');
        // $this->load->view('pages/signup', $data);
        // $this->load->view('templates/footer');
        foreach ($_POST as $key => $value) 
          $data['messages'][$key] = form_error($key);
        
      
      else 
        #Encrypting password
        $hashed_password = password_hash($this->input->post('password'),PASSWORD_DEFAULT);

        $this->user_model->signup($hashed_password);

        #Setting a flash message
        $this->session->set_flashdata('user_registered', 'You were successfully registered! You can now log in.');
        redirect('../home');
      
      echo json_encode($data);
    

    # Check if the email is already taken.
    public function check_email_exists($email) 
      $this->form_validation->set_message('check_email_exists', 'There is a user registered with this email');
      if ($this->user_model->check_email_exists($email)) 
        return TRUE;
       else 
        return FALSE;
      
    
  

这是我的用户模型:

<?php
  class User_model extends CI_Model 
    # Sign up user
    public function signup($hashed_password) 
      # User data array
      $data = array (
        'name' => $this->input->post('name'),
        'email' => $this->input->post('email'),
        'password' => $hashed_password
      );

      # Insert user in the database
      return $this->db->insert('users', $data);
    

    # Check if email is already taken
    public function check_email_exists($email) 
      $query = $this->db->get_where('users', array('email' => $email));
      if (empty($query->row_array())) 
        return TRUE;
       else 
        return FALSE;
      
    
  

任何帮助将不胜感激。

【问题讨论】:

我不明白为什么会这样。我可以说您永远不应该将视图呈现为响应,除非它是预期的,并且您永远不应该在 ajax 请求中重定向(将其视为 iframe - 它不会更改用户视图端口,只有请求“视图”所以说话)。所以一定要删除这个:redirect('../home'); 并在你的 ajax 的成功部分处理你的重定向。这一行也没有意义:array('success' =&gt; FALSE, 'messages' =&gt; array()); .. 复制粘贴错误?也不要忘记在json_encode() 之后exit(),虽然这里不是 100% 必要,但还是建议这样做。 还有check_email_exists 并且所有这些功能都可以用带有自定义错误消息的is_unique[users.email] 替换(表单验证库有一些关于如何实现这一点的示例)。让您的代码保持简单。 在您显示的网址中,http://[::1]/appname/index.php/users/signup [::1] 部分不自然。 Th 通常在未设置 $config['base_url'] 时出现。必须设置该配置项。它必须包含协议、完整的域名,并以斜杠结尾,例如$config['base_url'] = 'http://example.com/'; 感谢is_unique的提示! @DFriend,你说得对,我忘了配置['base_url']。谢谢! 【参考方案1】:

更新:我认为您遇到的主要问题是您没有将 jquery 包装在准备好的文档中!

查询:

$(document).ready(function () 
    var sUrequest;
    $("#signup_form").submit(function (e) 
        e.preventDefault();
        if (sUrequest) 
            sUrequest.abort();
        
        var inputs = $(this).find("input, select, button, textarea"),
                serializedData = $(this).serialize(),
                postURL = $(this).attr("action");
        inputs.prop("disabled", true);
        sUrequest = $.ajax(
            url: postURL,
            type: "post",
            data: serializedData,
            dataType: 'json'
        );
        sUrequest.done(function (response, statusText, jqXHR) 
            if (response.status == 'error') 
                $.each(response.messages, function (key, value) 
                    var element = $('[name=' + key + ']');
                    element.closest('div.form-group input-group')
                            .removeClass('has-error')
                            .addClass(value.length > 0 ? 'has-error' : 'has-success')
                            .find('.text-danger')
                            .remove();

                    element.after(value);
                );
             else 
                window.location.href = response.url;
            
        );
        sUrequest.fail(function (jqXHR, statusText, thrownError) 
            console.log("jq:" + jqXHR + " st:" + statusText + " te:" + thrownError);
        );
        sUrequest.always(function () 
            inputs.prop("disabled", false);
        );
    );
);

注意:我还冒昧地修复了您的错误消息,因为您的字段没有 ID。

PHP:

function signup() 
    $data = array();

    $this->form_validation->set_error_delimiters('<p class="text-danger">', '</p>');
    $this->form_validation->set_rules('name', 'Name', 'required');
    $this->form_validation->set_rules('email', 'Email', 'required|callback_check_email_exists');
    $this->form_validation->set_rules('password', 'Create password', 'required');
    $this->form_validation->set_rules('password2', 'Repeat password', 'matches[password]');

    if (!$this->form_validation->run()) 
        $data['status'] = 'error';
        foreach ($_POST as $key => $value) 
            $data['messages'][$key] = form_error($key);
        
     else 
        $hashed_password = password_hash($this->input->post('password'), PASSWORD_DEFAULT);

        $this->user_model->signup($hashed_password);

        $this->session->set_flashdata('user_registered', 'You were successfully registered! You can now log in.');
        $data['status'] = 'success';
        $data['url'] = base_url() . 'home';
    
    echo json_encode($data);
    exit;

【讨论】:

我仍然收到与 OP 中相同的消息,唯一的区别是第一行不是"title":"Sign up","messages":"csrf_test_name":"","name":",而是"status":"error","messages":"csrf_test_name":"","name":" 非常感谢,亚历克斯!在我将&lt;script src="vendor/jquery/jquery.min.js"&gt;&lt;/script&gt; 行放在页眉中后,您的更新工作了(我猜想加载顺序可能存在问题,因为该行最初位于页脚中)。 太棒了! jquery 对它的加载位置非常挑剔。如果您在页脚中执行此操作,只需添加之后使用它的脚本。不要忘记将答案标记为已接受。【参考方案2】:

通过在控制器中简单地回显json_encode($data),您并没有向客户端部分发出信号,表明该响应应被视为 XHR 响应。此时,您所做的相当于加载视图以显示数据。

我们将使用output 类(自动加载)通过Ajax 返回响应。将echo json_encode($data) 更改为:

$this->output
->set_header('HTTP/1.1 200 Ok)
->set_status_header(200)
->set_content_type('application/json')
->set_output(json_encode($data));

这正是您所需要的。 请注意,您正在链接多个方法,因此无需使用; 终止每个方法...只需最后一个。

-&gt;set_output 之后控制器终止,因此之后不会执行任何操作

【讨论】:

那仍然不应该是必需的,不是吗?我曾经用他的方式用简单的回声来做这件事,它奏效了。 这是我在使用 CodeIgniter 时取得成功的唯一方法。显然,如果您不这样做,CI 假设您打算直接输出到视图/屏幕(尽管我不确定这是否是 GET 与 POST 的事情)......随着时间的推移,我习惯了这样做以这种方式,我所有的 XHR 交互都以这种方式响应以保持一致性 啊,我明白你的意思了,当我使用上述方法时,我总是写:dataType: 'json' ...我认为这基本上告诉 jquery 将响应视为 json 而不管标题 ahhh 好点...这可能与我在我的大多数项目中不使用 jQuery 而是使用普通的旧 vanilla Javascript 相关:) 但是你的回答完全是好的做法,这就是我现在所做的。我实际上打算将它包含在我的答案中,但感觉好像会增加混乱。

以上是关于CodeIgniter - AJAX 注册表单验证的主要内容,如果未能解决你的问题,请参考以下文章

在 codeigniter 中使用 jquery ajax 进行表单验证

CodeIgniter 在带有 ajax 回调的表单字段旁边单独显示错误

将自定义回调添加到 Codeigniter 表单验证

Thinkphp框架 表单自动验证登录注册 ajax自动验证登录注册

当数据在javascript codeigniter php中使用ajax成功功能时,如何禁用页面重新加载?

Thinkphp框架 表单自动验证登录注册 ajax自动验证登录注册