[ExpressJS] Bài 9 – Viết Code Điều Hành Blog Cá Nhân (Tiếp Theo)

Trong bài này, chúng ta sẽ bắt đầu viết code cho tuyến xử lý yêu cầu đầu tiên của nhóm route/article – đó là yêu cầu dạng /artice/view/:id được gửi tới server khi người dùng muốn xem trang đơn hiển thị nội dung đầy đủ của một bài viết. Tổng quan code xử lý route

Trong bài này, chúng ta sẽ bắt đầu viết code cho tuyến xử lý yêu cầu đầu tiên của nhóm route/article – đó là yêu cầu dạng /artice/view/:id được gửi tới server khi người dùng muốn xem trang đơn hiển thị nội dung đầy đủ của một bài viết.

Tổng quan code xử lý route

Do ở bài viết trước chúng ta đã tái cấu trúc lại code xử lý của route đầu tiên là yêu cầu xem trang chủ của blog và di chuyển các chi tiết vào các sub-procedure; Ở đây chúng ta sẽ khởi đầu ngay với code sử dụng các sub-procedure để định hình tổng quan các tác vụ nhỏ cần xử lý.

/* requires... */const router = express.Router();

router.get("/:id",async(request, response, next)=>{var{ id }= request.params;var data =newData();try{awaitgetDataForMeta(data,"article","view", id);awaitgetDataForTopnav(data);awaitgetDataForArticle(data,"view", id);}catch(error){returnoopsRouter(request, response, next);}

   response.render("index",{
      layout:"article",
      action:"view",
      data
   });});// router.get

module.exports = router;

Tham số id được tách lấy từ request.params và sử dụng làm tham số truy vấn bản ghi article tương ứng trong database. Kết quả khi thực hiện các sub-procedure để truy vấn dữ liệu có thể sẽ tìm được bản ghi phù hợp hoặc không. Và trong trường hợp không tìm được bản ghi article phù hợp với yêu cầu vì bất kỳ lý do gì – bao gồm cả việc code sub-procedure chúng ta viết có logic hoạt động không tốt – sẽ cần phản hồi cho người dùng trang đơn thông báo lỗi.

Như vậy chúng ta sẽ cần chuyển hướng yêu cầu tới router xử lý yêu cầu ngoại lệ là oopsRouter – tên mặc định do Express Generator tạo ra là error nhưng mình đã đổi lại.

get-data-for-meta

Đối với sub-procedure này chúng ta đã tạo ra các tham số tùy chọn bổ sung trong bài trước và bây giờ sẽ chỉ cần bổ sung thêm trường hợp xử lý rẽ nhánh cho khối code điều kiện chính. Sau đó tạo thêm một procedure nội bộ trong module này để truy vấn thông tin title từ bản ghi trong database.

/* requires... */

module.exports = async (
   out_data = new Data(),
   in_layout = "home",  /* home | category | article | admin | oops */
   in_action = "view",  /* view | add | edit | login */
   in_id = "Infinity"
) => {
   if (in_layout == "home")
      getMetaForHome(out_data);
   else if (["article", "oops"].includes(in_layout))
      await getMetaForArticle(out_data, in_action, in_id);
   else
      throw new Error("Unsupported layout");
};

/* ... */

const getMetaForArticle = async (
   out_data = new Data(),
   in_action = "view",  /* view | add | edit */
   in_id = "Infinity"
) => {
   var maybeUnpublished = new Article();
   await databaseManager.execute(
      Article.name, "select-by-id",
      in_id, maybeUnpublished
   );

   if (["view", "add", "edit"].includes(in_action))
      out_data.set("title", maybeUnpublished.get("title"));
   else
      throw new Error("Unsupported action type");
};

Như đã nói trước đó thì mình có sử dụng một convention riêng cho blog của mình đó là các bản ghi có idInfinity sẽ được sử dụng làm nội dung cho trang thông báo ngoại lệ không tìm thấy bài viết. Do đó trong trường hợp layout được truyền vào là oops thì mình cũng cho xử lý giống với logic của article.

get-data-for-topnav

Do thiết kế thanh điều hướng đơn giản không có trạng thái thay đổi tùy theo trang đơn hiển thị nên ở đây mình không bổ sung thêm code xử lý gì cả. Nếu như bạn muốn hiển thị liên kết của category tương ứng với hiệu ứng đặc biệt thì sẽ càn truyền vào tham số bổ sung để truy vấn bản ghi article rồi sau đó tìm tới bản ghi category tương ứng.

get-data-for-article

Đây là sub-procedure mới được tạo ra để dành riêng cho tác vụ truy vấn dữ liệu cho thành phần #article trong cấu trúc các trang đơn hiển thị nội dung đầy đủ của một bài viết. Ở đây chúng ta sẽ có hai trường hợp là khi tham số action được truyền vào có giá trị add (thêm mới bản ghi) và khi giá trị truyền vào là view|edit. Đối với trường hợp của add thì chúng ta có thể xem như đang yêu cầu truy vấn dữ liệu của một bản ghi mới – chưa có nội dung gì.

/* requires... */

module.exports =async(
   in_data =newData(),
   in_action ="view",/* view | add | edit */
   in_id ="Infinity")=>{if(in_action =="add")getNewArticle(in_data);elseif(["view","edit"].includes(in_action))awaitgetExistedArticle(in_data, in_id);elsethrownewError("Unsupported action type");};/* ... */

Và sau đó viết code chi tiết cho hai procedure nội bộ của module này là getNewArticlegetExistedArticle.

/* module.exports... */const getNewArticle =(
   in_data =newData())=>{var newArticle =newArticle();
   newArticle.set("title","Bài Viết Mới");
   in_data.set("article", newArticle);};/* --- */const getExistedArticle =async(
   in_data =newData(),
   in_id ="Infinity")=>{var selected =newArticle();await databaseManager.execute(
      Article.name,"select-by-id",
      in_id, selected
   );var contentMarkdown = selected.get("content");var contentHTML = marked.parse(contentMarkdown);
   selected.set("content", contentHTML);
   in_data.set("article", selected);};

Code xử lý trang đơn thông báo lỗi

/* requires... */const router = express.Router();

router.get("*",async(request, response, next)=>{var data =newData();try{awaitgetDataForMeta(data,"oops","view","Infinity");awaitgetDataForTopnav(data);awaitgetDataForArticle(data,"view","Infinity");}catch(error){
      console.error(error);}

   response.status(404).render("index",{
      layout:"oops",
      action:"view",
      data
   });});// router.get

module.exports = router;

Chạy test kiểm tra hoạt động của code

npm start

Server started

http://localhost:8080/article/view/0000

http://localhost:8080/article/view/1234

Kết thúc bài viết

Như vậy là chúng ta đã viết xong code xử lý cho tuyến đầu tiên của nhóm route/article tương ứng với thao tác view. Trong bài viết tiếp theo, chúng ta sẽ thực hiện code cho thao tác add yêu cầu xem giao diện soạn thảo bài viết mới.

(Sắp đăng tải) [ExpressJS] Bài 10 – Viết Code Điều Hành Blog Cá Nhân (Tiếp Theo)

Nguồn: viblo.asia

Bài viết liên quan

WebP là gì? Hướng dẫn cách để chuyển hình ảnh jpg, png qua webp

WebP là gì? WebP là một định dạng ảnh hiện đại, được phát triển bởi Google

Điểm khác biệt giữa IPv4 và IPv6 là gì?

IPv4 và IPv6 là hai phiên bản của hệ thống địa chỉ Giao thức Internet (IP). IP l

Check nameservers của tên miền xem website trỏ đúng chưa

Tìm hiểu cách check nameservers của tên miền để xác định tên miền đó đang dùn

Mình đang dùng Google Domains để check tên miền hàng ngày

Từ khi thông báo dịch vụ Google Domains bỏ mác Beta, mình mới để ý và bắt đầ