สร้างหน้าเว็บเพจใหม่เพื่อส่งข้อมูลไปที่ Google Form กันดีกว่า (มี Reverse engineer API นิดนึง)

Cover image

คำเตือน

เนื้อหาในบทความนี้อ้างอิงจาก Google Form ในวันที่ 12 ตุลาคม 2019 เท่านั้น ในอานาคตหลักการและวิธีในนี้อาจจะใช้ไม่ได้อีก

Google Form ไม่ดีตรงไหน ทำไมต้องปรับแต่ง?

รู้ๆกันอยู่นะครับว่า Google Form นั้นมีข้อดีมากมายหลายอย่าง เพิ่มความสะดวกสบายให้กับชีวิตเราได้มากเลย ที่สำคัญคือมันฟรีด้วย แต่ข้อเสียคือเครื่องมีอในการปรับแต่งหน้าตาของ Form มีค่อนข้างจำกัด เช่น เปลี่ยนรูปที่ header หรือเปลี่ยนสีพื้นหลังได้ ใครอยากได้มากกว่านี้ทำไม่ได้แล้วครับ (เปล่ี่ยน font ภาษาไทยยังไม่ได้เล้ย)

บทความนี้ก็จะขอบอกเลยว่า ตอนนี้เราไม่สามารถปรับแต่งมากไปกว่าที่ Google เค้าให้มาครับ แต่เรายังสามารถสร้าง webpage ขึ้นมาใหม่แล้ว ยิง request ไปที่ api ของ Google Form ได้ครับ โดยเราจะสามารถหาวิธีการส่งข้อมูลพวกนั้นได้ Google Chrome และ Postman ครับผม

เริ่มกันเลย!

สร้าง form

อันนี้ผมว่าหลายคนรู้ว่าจะทำยังไงก็เข้าไปที่ Google Drive แล้วก็เลือกสร้าง Form ใช่ไหมครับ แต่เชื่อว่าหลายคนไม่รู้ว่าทำแบบนี้ได้ ลองพิมพ์ form.new ที่ address bar ของ browser แล้ว enter ดูสิครับ

create form 1

คราวนี้ก็สร้างฟอร์มตามใจเลยครับ วันนี้ผมจะสมมติว่าตัวเองเป็นเจ้าของสวนมังคุดออร์แกนิก ที่อยากเปิด pre-order ล่วงหน้า

create form 2

เสร็จแล้วก็ลองกรอกข้อมูลพร้อมกับส่ง

create form 3

ดูผลลัพธ์ก็จะได้แบบนี้ตามปกตินะครับ

create form 4

ลอง reverse engineer กันแบบงูๆปลาๆกันครับ

ก่อนอื่นกลับไปที่หน้า form ก่อนนะครับ ใครอยู่ที่หน้าสร้างก็ไปที่ "แสดงตัวอย่าง" ครับ

reverse engineer form api 0

เปิด Developer Tools เลยครับผม (Google Chrome Menu > More Tools > Developer Tools)

reverse engineer form api 1

เมื่อหน้าต่าง Developer Tools ปรากฏขึ้นมาแล้ว ให้ไปเปิด tab "Network" เอาไว้นะครับ เราจะรอดูสิ่งที่เกิดขึ้นระหว่างที่ส่งข้อมูลเข้า Google Form

reverse engineer form api 2

ลองกรอกข้อมูลแล้วส่งไปใหม่อีกสักรอบครับผม

reverse engineer form api 2

เราจะเห็นอะไรหลายๆอย่างโผล่ขึ้นมาใน Network ใช่ไหมครับ ใครที่งงว่ามันคืออะไร มันก็คือสิ่งที่ browser ทำการ request ไปหา server ปลายทางในการกด submit ข้อมูลเมื่อสักครู่นั่นแหละครับ การจะดูออกว่าอะไรคืออะไร อันนี้จำเป็นต้องทำความเข้าใจการทำงานของ web และ server ปลายทางอยู่บ้างนะครับ แต่ในบทความนี้จะขออธิบายสั้นๆเลยนะครับ ให้ลองมองหา POST request ที่เกิดขึ้น แล้วก็ลองมองหาว่าตัวไหนที่มีข้อมูลที่เราพึ่งกรอกติดไปใน body ด้วย ในตัวอย่างของผมคือ formResponse อันแรกนั่นเองครับ

reverse engineer form api 3

เพื่อให้สะดวกในการล้วงแคะแกะเกา ผมจะเอาส่วนนี้ไปทำงานต่อบน application Postman โดยการคลิกขวาที่ตัว request แล้วเลือก Copy > Copy as cURL ที่ต้องทำแบบนี้ก็เพราะว่า Postman รองรับกับ Import ด้วย text ครับ Copy as cURL เนี่ยเหมาะที่สุดเพราะเป็นคำสั่งที่รวมทุกอย่างไว้ให้ครบเลย

reverse engineer form api 4

เปิด Postman ขึ้นมาครับ ใครยังไม่มี ก็ไปโหลดมาติดตั้งได้เลยที่ https://getpostman.com นะครับ เปิดขึ้นมาแล้วไปที่ปุ่ม Import ด้านบนเลยครับ หลังจากนั้นก็เลือก Paste Raw Text วาง cURL ลงไปใน text area แล้วกด Import เลยครับผม

reverse engineer form api 5

เราจะได้ Request ใหม่ขึ้นมาหนึ่งตัวที่มี Url, Header และ Body มาเรียบร้อย

reverse engineer form api 6

ผมจะลองเปลี่ยนข้อมูลใน body ใหม่จากส่วนที่เราเข้าใจนะครับ สังเกตจากข้อมูลที่ติดมาด้วยเราน่าจะพอเดาออกว่าอะไรเป็นอะไร อะไรที่ไม่รู้ทิ้งไว้ก่อนก็ได้ครับ ลองแก้แล้วลองกด Send ดูครับผม

reverse engineer form api 7

หลังจากส่งไปแล้วเราจะได้ Response กลับมาเป็น HTML Code ซึ่งถ้าอยากดูสะดวกๆก็ลองเปิดเป็น Preview ดูได้ครับ ถ้าใครจำได้นี่มันก็คือหน้าตอบกลับของ Google Form นี่เอง

reverse engineer form api 8

ลองกลับไปดูที่ Form ก็พบว่าข้อมูลที่ส่งเมื่อกี้มันเข้ามาแล้ว

reverse engineer form api 9

เพื่อความมั่นใจเราต้องหาว่าอะไรจำเป็นหรือไม่จำเป็นในการส่ง request ครั้งนี้ก่อน โดยผมจะเริ่มจากตัด body ที่เราไม่รู้จักออกโดยคลิกเอาเครื่องหมายถูกออกจาก body ที่เราไม่รู้จักและเดาว่าไม่น่าจะได้ใช้

reverse engineer form api 10

เมื่อส่งแล้วคำตอบก็ยังคงเข้าไปตามปกติดี.. แต่มันยังเหลืออีกหลายจุดนะอย่าพึ่งดีใจ

reverse engineer form api 11

ผมมาดูที่ Header เพราะมันมักจะมี Token หรืออะไรเยอะแยะไปหมด ผมเริ่มจากเอาออกให้หมดเลย

reverse engineer form api 12

เอาให้แน่นอนต้องไปลบ Cookies ของ Postman ออกด้วยนะ

reverse engineer form api 13

ตอนนี้ผมลบออกหมดแล้ว ไม่เหลืออะไรแล้วพร้อมสำหรับการทดสองยิงครั้งต่อไป

reverse engineer form api 14

ลองเปลี่ยนข้อมูลแล้ว request ไปอีกรอบ

reverse engineer form api 15

เรียบร้อย แปลว่ามันต้องการแค่ body ของเราเท่านั้นเองครับ

reverse engineer form api 16

ถ้าจะลองสรุปออกมา API ที่ผมแกะออกมามันก็จะมีหน้าตาประมาณนี้

{
    "URL": "https://docs.google.com/forms/d/e/1FAIpQLSeRSWxqaW7BvoF47cl_muvGTvw39sm7Qt6DNYL0CkxQNVR0Ww/formResponse",
    "Method": "POST",
    "Header": {
        "Content-Type": "application/x-www-form-urlencoded"
    },
    "Body": {
        "entry.1819278497": "ชื่อ-นามสกุล",
        "entry.1633641327": "เบอร์โทรศัพท์",
        "entry.828954501": "จำนวนที่จอง",
    }
}

สร้าง Form ใหม่กันเถอะ

เพื่อความรวดเร็วผมจะไม่ขอลงรายละเอียดในส่วนของการทำหน้า page นะครับ ต่างคนก็น่าจะมีสไตล์การเขียนที่ต่างกัน แต่ในบทความนี้ผมจะเลือกใช้ Vuetify และ Vue.js ในการสร้างหน้าฟอร์มขึ้นมาใหม่นะครับ ที่เลือก Vuetify เพราะว่ามี components พร้อมอยู่แล้ว ประกอบกับ Document ที่มีให้ก็เขียนไว้ดีมากเลย

https://vuetifyjs.com/en/getting-started/quick-start

create new form page 1

ผมสร้าง form แบบลวกๆขึ้นมาด้วย Vue.js และยัดทุกอย่างไว้ที่ App.vue หน้าเดียวเลยก็จะได้หน้าตาประมาณนี้

<template>
  <v-app>
    <v-content>
      <v-row
        class="ma-5"
        justify="center"
      >
        <v-col
          cols="12"
          md="8"
          lg="6"
        >
          <v-card :elevation="24">
            <v-row class="pa-6">
              <v-col
                cols="12"
                md="5"
                lg="4"
              >
                <v-img
                  src="@/assets/mangosteen.jpg"
                  position="center center"
                  height="100%"
                >
                </v-img>
              </v-col>
              <v-col
                cols="12"
                md="7"
                lg="8"
              >
                <div class="title">
                  มังคุดออร์แกนิก ออนไลน์
                </div>
                <v-form
                  ref="form"
                  action=""
                  v-model="valid"
                  lazy-validation
                >
                  <v-text-field
                    v-model="name"
                    :counter="280"
                    :rules="nameRules"
                    label="ชื่อ-นามสกุล ผู้จอง"
                    required
                  ></v-text-field>

                  <v-text-field
                    v-model="phone"
                    :rules="phoneRules"
                    label="เบอร์โทรศัพท์"
                    hint="ไม่ต้องเติม - ในเบอร์โทรศัพท์"
                    required
                  ></v-text-field>

                  <v-select
                    v-model="selectedPackage"
                    :items="packages"
                    :rules="[v => !!v || 'กรุณาเลือกจำนวนที่ต้องการจอง']"
                    label="จำนวนที่ต้องการจอง"
                    required
                  ></v-select>

                  <v-row
                    class="px-5"
                    justify="space-between"
                  >
                    <v-btn
                      color="error"
                      @click="reset"
                      :loading="loading"
                    >
                      <v-icon left>mdi-delete</v-icon>
                      ล้างข้อมูล
                    </v-btn>

                    <v-btn
                      color="success"
                      @click="submit"
                      :loading="loading"
                    >
                      <v-icon left>mdi-shopping</v-icon>
                      สั่งจองเลย
                    </v-btn>
                  </v-row>
                </v-form>
              </v-col>
            </v-row>
          </v-card>
        </v-col>
        <v-snackbar v-model="snackbar">
          {{ alertMessage }}
          <v-btn
            color="pink"
            text
            @click="snackbar = false"
          >
            ปิด
          </v-btn>
        </v-snackbar>
      </v-row>
    </v-content>
  </v-app>
</template>

<script>
import qs from 'querystring';

export default {
  name: 'App',
  data: () => ({
    loading: false,
    valid: true,
    name: '',
    snackbar: false,
    alertMessage: '',
    nameRules: [
      v => !!v || 'กรุณากรอกชื่อผู้จอง',
      v => (v && v.length <= 280) || 'ชื่อไม่ควรเกิน 280 ตัวอักษร',
    ],
    phone: '',
    phoneRules: [
      v => !!v || 'กรุณาระบุเบอร์โทรศัพท์ของท่าน',
      v => (/\d{9,10}/.test(v) && v.length <= 10) || 'เบอร์โทรศัพท์ไม่ถูกต้อง',
    ],
    selectedPackage: null,
    packages: ['5 กก.', '10 กก.', '15 กก.', '20 กก.'],
  }),
  methods: {
    reset() {
      this.$refs.form.reset();
    },
    async submit() {
      if (this.$refs.form.validate()) {
        const url =          'https://docs.google.com/forms/d/e/1FAIpQLSeRSWxqaW7BvoF47cl_muvGTvw39sm7Qt6DNYL0CkxQNVR0Ww/formResponse';
        const body = {
          'entry.1819278497': this.name,
          'entry.1633641327': this.phone,
          'entry.828954501': this.selectedPackage,
        };

        try {
          this.loading = true;
          await this.postData(url, body);
          this.reset();
          this.alertMessage = 'สั่งจองให้เรียบร้อยแล้ว';
        } catch (error) {
          this.alertMessage = error.message;
        } finally {
          this.loading = false;
          this.snackbar = true;
        }
      }
    },
    postData(url, data) {
      return fetch(url, {
        method: 'POST',
        mode: 'no-cors',
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: qs.stringify(data),
      });
    },
  },
};
</script>

เพียงแค่กด "สั่งจอง" ข้อมูลก็จะถูกส่งไปที่ Google Form ได้เลยทันที

create new form page 2

หลายคนเขียนส่ง request ไปที่ API แล้วจะเจอปัญหา CORS ผมก็จะแก้ปัญหาด้วยการเปลี่ยน mode เป็น no-cors ใน function fetch() นะครับ

postData(url, data) {
  return fetch(url, {
    method: 'POST',
    mode: 'no-cors',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: qs.stringify(data),
  });
}

ใครยังไม่เห็นภาพลองดูตัวอย่าง code และเว็บไซต์ใหม่ที่ผมทำขึ้นมาได้เลยตาม link ด้านล่างครับ

ตัวอย่าง

หวังว่าบทความนี้จะช่วยให้หลายๆท่านเห็นภาพการ Reverse Engineering API แบบง่ายๆเพื่อเอาไปใช้ประยุกต์กับงานต่อไปได้นะครับ สำหรับท่านใดมีคำถามสามารถสอบถามเข้ามาที่ Inbox ของ Facebook Page ได้เลยนะครับ