Chuyện là thế này, dạo này tôi với Tôi đi code dạo đang ấp ủ nhiều dự án, và để khởi đầu thì chúng tôi có thành lập hội Tam Hoàng
, à không, hội anh em lập trình viên cool ngầu bựa nhất Việt Nam là The Coder Gang Vietnam để nhằm gắn kết lập trình viên, chia sẻ kinh nghiệm, giao lưu, kết hợp để làm những dữ án hay ho khác, vân vân và vân vân.
Trong quá trình đó tụi tôi có làm một số mẫu áo thun, mục đích là để anh em trong hội có cái mặc ra đường loè thiên hạ, cũng như gây quỹ để hỗ trợ những dự án tiếp theo, offline, workshop các kiểu.
Trong quá trình bán áo thì, tôi sử dụng Giao hàng tiết kiệm làm dịch vụ vận chuyển hàng hoá tới các bạn, phải nói là dịch vụ này khá tốt, hỗ trợ gần như là mọi miền tổ quốc nên tôi rất là tin tưởng.
Đến một ngày, khi mẫu áo mới nhất ra mắt (các bạn muốn coi thử áo thì bấm vào đây nè) thì vì một số lý do đặc biệt liên quan tới chi phí khiến chúng tôi không thể hỗ trợ phần phí dịch vụ giao hàng nữa (tức là không freeship toàn quốc nữa á), nói tóm lại là các bạn khách hàng phải trả phí ship. Từ đây mới phát sinh ra vấn đề, vấn đề là gì, hồi sau sẽ rõ.
Vấn đề là gì? Bạn trực bán hàng của chúng tôi than với tôi rằng:
- Anh ơi, mỗi lần bán hàng cho khách em cứ phải chuyển tab để vào giaohangtietkiem.vn kiểm tra phí ship rồi báo cho khách mất thời gian quá có cách nào, giải quyết không anh?
Đêm về, nằm trằn trọc không ngủ, vắt tay lên trán suy nghĩ vì Đức bị loại khỏi World Cup, à không suy nghĩ để làm sao để bạn nhân viên kia có thể làm việc dễ dàng hơn thì tôi mới chợt nhớ đến Chrome Extension.
Đây mới là nội dung chính nè, phần trên là mở bài thôi đó nhe!
Tôi mở máy tính bắt đầu code một cái Chrome Extension để tính giá ship hàng của giaohangtietkiem.vn.
Khởi tạo project
Tôi quyết định dùng React/Redux để viết cái Extension này. Tại vì sao?, tại vì tôi thích xài React/Redux.
Thông thường, khi nghĩ đến việc khởi tạo một React app thì cái mà người ta hay nghĩ đến đầu tiên là create-react-app.
Nhưng tôi thì không, dạo gần đây tôi cực kì thích parcel-bundler, đối với tôi thì cái này khá là xịn. Xịn vì sao thì tôi sẽ giới thiệu trong một bài viết khác nhé! Vì thế mà tôi viết sẵn ra luôn một cái starter kit cho React/Redux app sử dụng bundler là Parcel dưới cái tên rerepa-starter-kit, bạn nào có nhu cầu thì ghé qua lấy về xài, nhớ bấm sao là được, haha. À mà đừng hỏi tôi tên đó nghĩ là gì vì tôi cũng chả biết đâu.
Thêm manifest file
Okay, giới thiệu thế thôi, tiếp theo để làm được Chrome Extension thì cái quan trọng là chúng ta phải khai báo file manifest.json
. File này phải được đính kèm trong gói mà chúng ta đưa ra cho người dùng. Cụ thể file này có những gì thì các bạn có thể tham khảo ở đây.
Còn trong trường hợp của tôi thì tôi khai báo như sau.
{ | |
"manifest_version": 2, | |
"name": "Giao hàng tiết kiệm", | |
"description": "Giao hàng tiết kiệm", | |
"version": "0.0.1", | |
"browser_action": { | |
"default_popup": "index.html", | |
"default_title": "Open the popup" | |
}, | |
"icons": { | |
"16": "ico16.png", | |
"48": "ico48.png", | |
"128": "ico128.png" | |
} | |
} |
Dễ hiểu ha, tôi khỏi cần phải giải thích nhiều chỗ này.
File này tôi đặt trong đường dẫn src/public/manifest.json
. Thư mục public này sẽ có giá trị về sau, các bạn cứ bình tĩnh đọc tiếp nha.
Build extension
Chắc các bạn nghĩ starter kit có sẵn rồi, để build dùng yarn build
thôi chứ có gì đâu mà cũng bày vẽ viết thành một mục đúng không?
Ừ thì đúng là dùng yarn build
là đủ rồi, nhưng đừng quên extension đòi hỏi manifest.json
file, cũng như một số file hình ảnh, icon khác nên phải copy qua nữa nhé. Việc này sẽ đơn giản hơn nếu các bạn để mọi thứ public trong một thư mục, đó là lý do của việc tạo thư mục public
bên trên đó.
Sửa phần scripts
trong file packages.json
tí xíu là được.
{ | |
"scripts": { | |
"build": "NODE_ENV=production parcel build src/index.html -d dist --detailed-report --no-source-maps", | |
"clean": "rimraf dist", | |
"prebuild": "yarn run clean && mkdir dist && cp -R src/public/* dist", | |
} | |
} |
Sau đó thì chạy yarn build
nhé.
Thêm extension vào Chrome
Tất nhiên là phải mở Chrome lên trước rồi mới thêm extension được nhé! Các bạn gõ chrome://extensions/
vào thanh địa chỉ rồi nhấn enter. Nhớ enable Developer mode, thì các bạn mới thấy nút Load unpacked nhé! Sau đó các bạn chọn đường dẫn tới thư mục dist
là được.
Nỗi kinh hoàng xuất hiện
Nói chung đến đây rồi thì các bạn chỉ cần implement như một web app bình thường thôi. Nhưng vấn đề của tôi thì vẫn chưa xuất hiện mãi cho đến khi tôi gọi API của giaohangtietkiem.vn.
Các bạn giaohangtietkiem.vn làm RESTful API docs cũng ok, ai muốn xem có thể ngó qua ở đây. Tôi bắt đầu gọi thử API thì vẫn ok cho đến khi tôi thật sự integrate nó vào extension thì nỗi kinh hoàng bắt đầu xuất hiện.
Nếu các bạn vẫn còn nhớ bài viết gần đây nhất của tôi về CORS thì chắc các bạn đều biết việc browser sẽ tự động send một cái gọi là Preflight Request để xem server có hỗ trợ CORS hay không, có rất nhiều điều kiên để browser tự gửi một Preflight Request, các bạn có thể tham khảo thêm ở link này.
Lỗi xảy ra khi Preflight Request của tôi bị trả về 403 Forbiden
, tôi tự nghĩ, cái này là public API mà, chưa kể mới chỉ là Preflight, browser nó chỉ hỏi có gửi được request hay không thôi mà, sao cấm nó.
Mò mẫm một hồi tôi mới phát hiện ra, ngay cả OPTIONS request các bạn bên giaohangtietkiem cũng bắt phải truyền thêm header Token
, các bạn ơi, cái request này là của browser nó tự sinh ra, sao mà tôi thêm custom header vào để gửi cho các bạn được. Huhu.
Trong hoàn cảnh bế tắc, nhưng tôi vẫn không đầu hàng, với bản tính luôn vượt qua nghịch cảnh tôi phải tự tìm cách giải quyết cho mình trước khi kêu réo các bạn bên giaohangtietkiem.vn (có kêu réo chắc cũng chẳng được giải quyết đâu). Tôi suy nghĩ ra một số cách, cuối cùng, tôi dùng sức mạnh của extension để intercept request trước khi gửi đi, và thêm header Token
kia vào.
Để làm được chuyện đó tôi phải xin thêm permission từ browser. Cách làm là sửa file manifest.json
như sau.
{ | |
"permissions": [ | |
"webRequest", | |
"webRequestBlocking", | |
"https://services.giaohangtietkiem.vn/" | |
], | |
"background": { | |
"scripts": ["background.js"] | |
} | |
} |
Sau sau đó các bạn tạo thêm file src/public/background.js
const targetPage = 'https://services.giaohangtietkiem.vn/*' | |
const addTokenHeader = (e) => { | |
e.requestHeaders.push({name: 'Token', value: 'token'}) | |
return {requestHeaders: e.requestHeaders} | |
} | |
browser.webRequest.onBeforeSendHeaders.addListener( | |
addTokenHeader, | |
{urls: [targetPage]}, | |
['blocking', 'requestHeaders'] | |
) |
Đoạn code trong file background.js
chính là sử dụng onBeforeSendHeaders
, để chặn request tới target tương ứng, chỉnh sửa header rồi mới tiếp tục gửi request lên server.
Với cách này, tôi làm cho cái extension của mình chạy được, và bạn nhân viên bán hàng đã rất vui vì, bạn ấy có thể kiểm tra phí ship ngay tại màn hình chat với khách.
Bài học rút ra
Bài học rút ra đó là không nên đầu hàng nghịch cảnh, là một người software engineer chân chính chúng ta luôn phải đối diện với vấn đề để giải quyết nó. Đó cũng là nguyên nhân ra mắt của cái áo này.
Đùa thế thôi chứ tôi hy vọng qua bài viết này các bạn đã hiểu thêm phần nào về cách viết Chrome Extension.
Cũng như nhắn gửi các bạn developer của giaohangtietkiem.vn, tắt dùm mình cái kiểm tra custom header trên OPTIONS request nha, các bạn làm vậy thì bên thứ ba muốn làm web client sử dụng trực tiếp RESTful API của các bạn không được đâu, mà chắc trước giờ chẳng có ai làm nên các bạn không biết, tuy nhiên thì tắt đi để người đi sau đỡ phải cực như mình hen.
À, source code của extension tôi có public trên github nhé! Các bạn vào https://github.com/codeaholicguy/ghtk-ext bấm sao nè!
Chưa hết
Câu chuyện tưởng chừng đã tốt đẹp cho đến khi bạn nhân viên lại nói với tôi:
- Anh ơi, lâu lâu em phải chat với khách bằng điện thoại, mà cứ phải check phí ship trên Chrome thì cực quá, có cách nào giải quyết không anh?
Quảng cáo: Các bạn có thể tham khảo các mẫu áo của The Coder Gang Vietnam ở link này, nếu các bạn muốn mua hàng vui lòng inbox để tụi mình có thể tư vấn và hỗ trợ các bạn mua hàng qua messenger taị m.me/codergangvn nhé!
Văn phong hay vậy, đọc cuốn hút :) (note: Từ toidicodao sang nhé :D)
Cho tôi xin địa chỉ E_Mail để tiện trao đổi công việc mà tôi đang rất cần nhé,
Bạn có thể liên hệ với mình qua thông tin mạng xã hội bên trên header nhé
chào anh, em cũng đang thử làm nhưng cái document của giaohangtietkiem nó tệ quá, test thử postman toàn xịt thôi,ko biêt anh đã request thử bằng postman chưa, có thể giúp em được ko, em cảm ơn
anh thấy document của ghtk cũng khá hoàn thiện, không biết em xem document nào?
có lẽ với bảng tính là một lập trình viên không đầu hàng nghịch cảnh ảnh đã nhỏ nhẹ nói với bạn kia rằng “Cực thì đừng chat bằng điện thoại nửa, lên chrome mà chat” :v
Lộ hết kịch bản của anh rồi :'(
hóng câu trả lời thứ 2 của a :D