2016/12/20

Webpack With Vendor

前言

之前寫過這篇 webpack,單一設定 webpack 的方式,一般來說為了減少 request,所以會希望將 js 檔案 compile 成一個,這種條件你不是從頭到尾打造這個網站的作者,了解每個頁面 dom 布局的話是相當困難的,而且從 HTTP/2 的新標準出現以後,這招已經沒有明顯的優勢了,可以參考 「你的網站升級到 HTTP/2 了嗎?」這篇文章。

所以這篇文章會重新教學怎麼設定,讓你每個網頁有屬於自己的 js 檔案,但共用的套件如 jQuery 或 bootstrap 卻可以獨立,減少獨立檔案的大小。

安裝相關套件

npm i --save-dev webpack babel-core babel-loader babel-preset-es2015
npm i --save jquery bootstrap

目前主要還是用 babel 編譯較新穎的 JavaScript 語法。

檔案布局

webpack.config.js
const webpack = require('webpack');
const path = require('path');

module.exports = {
    entry: {
        a: path.resolve(__dirname, 'js/src/a'),
        b: path.resolve(__dirname, 'js/src/b'),
        vendor: ['jquery', 'bootstrap']
    },
    output: {
        path: path.resolve(__dirname, 'js/build/'),
        filename: '[name].js'
    },
    plugins: [
        new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.bundle.js'),
        new webpack.ProvidePlugin({
            $: 'jquery',
            jQuery: 'jquery'
        }),
        new webpack.optimize.UglifyJsPlugin({
            compress: {
                warnings: false
            }
        })
    ],
    module: {
        loaders: [
            { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' }
        ]
    }
};

這個設定檔預計要將 js/src/ 下的 a.js 以及 b.js compile 到 js/build/,並將 jQuery 以及 bootstrap 抽出來成為 vendor,設定完成後,在根目錄執行 webpack,編譯後在 js/build/ 下會看到 a.js, b.js, vendor.bundle.js 三個檔案。

.babelrc
{
  "presets": ["es2015"]
}

這是 babel 的設定檔。

js/src/a.js
$(function() {
    $('body').append('this is a');
});
js/src/b.js
$(function() {
    $('body').append('this is a');
});
js/src/b.js
$(function() {
    $('body').append('this is b');
});
a.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>webpack page a</title>
    <script src="js/build/vendor.bundle.js"></script>
    <script src="js/build/a.js"></script>
</head>
<body>
    <h3>this is a</h3>
</body>
</html>
b.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>webpack page b</title>
    <script src="js/build/vendor.bundle.js"></script>
    <script src="js/build/b.js"></script>
</head>
<body>
    <h3>this is b</h3>
</body>
</html>

執行 a.html以及 b.html 就會看到各自的效果了。

2016/12/18

Google reCAPTCHA On Codeigniter

前言

為了避免網站被 robot 掃模做重複性的動作,例如加入會員,申請忘記密碼等等,我們會放一個必須人為輸入的欄位並且讓 robot 難以辨識破解,常見的會有文字以及數字的圖像化輸入,但有的機器人如果拿到規律以及足夠的樣本,純文字的輸入方式是可能被破解的,Google 出的 reCaptcha 屬於圖像化的選擇驗證,不管是排列位置或是種類的多樣性都難以去破解,在這邊示範一下怎麼整合 ci 使用

實做

套件安裝

首先安裝 google/recaptcha 這個套件,我在 codeigniter 上有使用 composer,在 config/config.php 下可以打開,使用方法跟在 Laravel 上一樣

程式碼

application/controllers/Recaptcha.php
<?php

defined('BASEPATH') OR exit('No direct script access allowed');

class Recaptcha extends CI_Controller
{
    public function index()
    {
        $this->load->helper('form');
        $this->load->helper('url');
        $this->load->view('recaptcha/form');
    }

    public function result()
    {
        $this->load->library('form_validation');

        $status = 'done';
        $message = 'done';
        $secret = '6LfsIQ8UAAAAAOveAzcFb6A_3Yiv_QdfR-qo5qld';

        try {
            $this->form_validation->set_rules('username', 'username', 'required');
            $this->form_validation->set_rules('g-recaptcha-response', 'captcha', 'required');

            if (!$this->form_validation->run()) {
                throw new Exception(validation_errors(null, null));
            }

            $gRecaptchaResponse = $this->input->post('g-recaptcha-response');
            $remoteIp = $this->input->ip_address();
            $recaptcha = new \ReCaptcha\ReCaptcha($secret);
            $resp = $recaptcha->verify($gRecaptchaResponse, $remoteIp);

            if (!$resp->isSuccess()) {
                $errors = $resp->getErrorCodes();
                throw new Exception(current($errors));
            }
        } catch (Exception $e) {
            $status = 'fail';
            $message = $e->getMessage();
        }

        $this->output
            ->set_content_type('application/json')
            ->set_output(json_encode(compact('status', 'message')));
    }
}
application/views/recaptcha/form.php
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>reCAPTCHA Demo</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
    <script src='https://www.google.com/recaptcha/api.js'></script>
    <script type="text/javascript" charset="utf-8">
    var baseUrl = "<?php echo base_url(); ?>";

    $(function() {
        $('#recaptcha_form').on('submit', function() {
            var data = $(this).serialize();

            $.post(baseUrl + 'recaptcha/result', data, function(response) {
                alert(response.message);

                if (response.status === 'done') {
                    grecaptcha.reset();
                }
            }, 'json');

            return false;
        });
    });
    </script>
</head>
<body>
    <?php echo form_open('recaptcha/result', ['id' => 'recaptcha_form']); ?>
    <div><?php echo form_input('uername', 'chan'); ?></div>
    <div class="g-recaptcha" data-sitekey="6LfsIQ8UAAAAAFmYCwSLpfKgWtqK1SlVfMcPn2Q6"></div>
    <div><?php echo form_submit('go', 'go'); ?></div>
    <?php echo form_close(); ?>
</body>
</html>

使用 reCAPTCHA 要先去申請 key 跟 secret,這部份我就不講解了,這份程式碼主要功能就是驗證是否有輸入 username 以及 reCAPTCHA,有的話再進一步驗證 captcha 正確性,如果你不想使用套件的錯誤訊息,那一旦報錯你只要顯示 captcha 錯誤即可,有些表單送出後不一定會換頁,所以我加了個 script 如果偵測到這次的行為正確的時候 refresh 一個新的 captcha,語法的使用時機可以看使用者的使用情況去調整搭配