Danh sách câu hỏi của đề

Câu hỏi: SDN302 Server-Side development with NodeJS, Express, and MongoDB – Bộ ôn tập thi lại số 2

50 câu trắc nghiệm kèm lời giải của môn Server-Side development with NodeJS, Express, and MongoDB, xếp đúng thứ tự trong đề.

Danh sách câu hỏi

20/50 câuCòn 30
1Which decorator is used in Nest.js to mark a class as a controller?4 lựa chọnTrung bình40% trả lời đúng
A@Service
B@Injectable
C@Controller
D@Component
Lời giải
Đáp án đúng: "@Controller". Trong NestJS, @Controller() đặt trên một class để Nest biết đó là controller, tức lớp tiếp nhận request và trả response; đối số như @Controller('cats') đặt tiền tố đường dẫn, còn các method bên trong gắn @Get(), @Post() để ánh xạ route, và class phải được khai báo trong mảng controllers của một module. "@Injectable" đánh dấu class là provider (thường là service) để tiêm phụ thuộc, không đăng ký route. "@Service" không phải decorator của NestJS, đó là cách đặt tên trong Spring hay một số framework khác. "@Component" cũng không tồn tại trong NestJS bản hiện hành; nó là khái niệm của Angular hay Spring, còn NestJS dùng @Injectable cho mọi provider.
Mở trang câu hỏi này →
Đề bài
const express = require('express');
const exphbs = require('express-handlebars');
const app = express();
app.engine('handlebars', exphbs({ defaultLayout: false }));
app.set('view engine', 'handlebars');
app.get('/profile', (req, res) => {
  res.render('profile', { name: 'Charlie', hobbies: ['reading', 'gaming'] });
});
app.listen(3000);

// views/profile.handlebars
<div>
  <h1>{{name}}</h1>
  <ul>
    {{#each hobbies}}
      <li>{{this}}</li>

{{/each}} </ul> </div>

A<div> <h1>Charlie</h1> <ul> <li>reading</li> <li>gaming</li> </ul> </div>
B<div> <h1>Charlie</h1> <ul></ul> </div>
C<div> <h1>undefined</h1> <ul> <li>reading</li> <li>gaming</li> </ul> </div>
DThrows an error due to invalid Handlebars syntax
Lời giải

Đáp án đúng: "

Lời giải
<div>
  <h1>Charlie</h1>
  <ul>
    <li>reading</li>
    <li>gaming</li>
  </ul>
</div>

". Lần theo mã: app.engine đăng ký express-handlebars cho đuôi handlebars với defaultLayout false, nghĩa là không bọc layout ngoài, và app.set chọn nó làm view engine. Khi GET tới /profile, res.render tìm tệp views/profile.handlebars và truyền dữ liệu gồm name là Charlie, hobbies là mảng reading, gaming. Trong template, {{name}} được thay bằng Charlie; khối each lặp qua mảng hobbies, và trong mỗi lượt {{this}} là phần tử hiện tại, nên sinh ra hai thẻ li là reading và gaming. Cách gọi exphbs() trực tiếp như hàm là kiểu của các bản express-handlebars cũ (bản mới dùng exphbs.engine()), câu hỏi xét kết quả render với cấu hình chạy được này. "<div> <h1>Charlie</h1> <ul></ul> </div> " chỉ xảy ra khi hobbies rỗng hoặc không được truyền, trong khi dữ liệu có hai phần tử. Kết quả có <h1>undefined</h1> sai vì name đã được truyền, và kể cả khi thiếu, Handlebars in chuỗi rỗng chứ không in chữ undefined. "Throws an error due to invalid Handlebars syntax" sai vì mọi thẻ đều đúng cú pháp và khối each được đóng đầy đủ.

Mở trang câu hỏi này →
ADeletes a document
BValidates and inserts a document into the database
CUpdates a document in the database
DFinds a document in the database
Lời giải
Đáp án đúng: "Validates and inserts a document into the database". Với một tài liệu mới tạo từ Model, lời gọi save() chạy kiểm tra hợp lệ (validation) theo schema như required, min, enum, kích hoạt hook pre save, rồi gửi lệnh chèn và trả về Promise chứa tài liệu đã lưu; nếu validation hỏng thì Promise bị từ chối với ValidationError và không gì được ghi. Với tài liệu đã lấy từ cơ sở dữ liệu, save() chỉ gửi lệnh cập nhật các trường đã đổi, nhưng ý cốt lõi được hỏi là kiểm tra rồi chèn. "Deletes a document" sai vì xoá là việc của deleteOne hay findByIdAndDelete. "Updates a document in the database" chỉ đúng một phần cho tài liệu đã tồn tại và bỏ mất bước kiểm tra hợp lệ; cập nhật trực tiếp thường dùng updateOne hoặc findByIdAndUpdate. "Finds a document in the database" là việc của find, findOne, findById.
Mở trang câu hỏi này →
AApache License
BMIT License
CGNU General Public License
DBSD License
Lời giải
Đáp án đúng: "MIT License". Express.js được phát hành theo giấy phép MIT, một giấy phép mã nguồn mở dễ dãi (permissive): ai cũng được dùng, sửa, phân phối, kể cả trong sản phẩm thương mại đóng mã, chỉ cần giữ nguyên thông báo bản quyền và nội dung giấy phép; điều này ghi trong tệp LICENSE của kho mã và trường license của package.json. "Apache License" cũng là giấy phép dễ dãi, có thêm điều khoản cấp quyền sáng chế, nhưng Express không dùng nó. "GNU General Public License" là giấy phép copyleft buộc sản phẩm phái sinh phải mở mã theo cùng giấy phép, khác hẳn MIT và không phải giấy phép của Express. "BSD License" gần giống MIT về tinh thần nhưng là một họ giấy phép khác, Express không phát hành theo nó.
Mở trang câu hỏi này →
Aconst http = require('http'); const fs = require('fs'); http.createServer((req, res) => { if (req.method === 'GET') { fs.readFile('file.txt', (err, data) => { if (err) throw err; res.end(data); }); } }).listen(8080);
Bconst http = require('http'); const fs = require('fs'); http.createServer((req, res) => { if (req.method === 'POST') { fs.readFile('file.txt', (err, data) => { res.end(data); }); } }).listen(8080);
Cconst http = require('http'); http.createServer((req, res) => { if (req.method === 'GET') { res.end('File content'); } }).listen(8080);
Dconst http = require('http'); const fs = require('fs'); http.createServer((req, res) => { if (req.method === 'GET') { fs.writeFileSync('file.txt', 'Hello'); res.end('File written'); } }).listen(8080);
Lời giải

Đáp án đúng: "const http = require('http'); const fs = require('fs');

Lời giải
http.createServer((req, res) => {
  if (req.method === 'GET') {
    fs.readFile('file.txt', (err, data) => {
      if (err) throw err; res.end(data);
    }
    );
  }
}

).listen(8080);". Lần theo mã: server tạo bằng http.createServer lắng nghe cổng 8080; khi request có req.method bằng GET, fs.readFile đọc file.txt bất đồng bộ, callback nhận data (Buffer chứa nội dung tệp) và res.end(data) gửi nội dung đó về client rồi đóng phản hồi; nếu đọc lỗi thì ném err. Đây là đoạn duy nhất vừa kiểm tra đúng phương thức GET vừa thực sự đọc tệp để trả về. Đoạn kiểm tra req.method === 'POST' sai vì nó chỉ phục vụ POST, request GET sẽ không bao giờ được trả lời và treo tới khi hết thời gian chờ. Đoạn gửi res.end('File content') sai vì nó trả một chuỗi cố định viết sẵn, không đọc tệp nào. Đoạn gọi fs.writeFileSync('file.txt', 'Hello') sai vì nó ghi đè tệp rồi trả chuỗi File written, tức là thay đổi dữ liệu thay vì trả nội dung tệp, đi ngược tính an toàn của GET.

Mở trang câu hỏi này →
Đề bài
const userSchema = new mongoose.Schema({
  name: String,
  age: Number
});
AA model definition
BA middleware function
CA schema structure for documents
DA MongoDB query
Lời giải

Đáp án đúng: "A schema structure for documents". Đoạn mã gọi new mongoose.Schema với hai trường, name kiểu String và age kiểu Number, tức là mô tả hình dạng mà mỗi document người dùng sẽ có; Mongoose dùng nó để ép kiểu (cast) và kiểm tra dữ liệu trước khi lưu. Bản thân userSchema chưa làm việc gì với cơ sở dữ liệu. "A model definition" sai vì model chỉ có khi đem schema biên dịch bằng mongoose.model('User', userSchema); khi đó mới gọi được find hay save. "A middleware function" sai vì middleware (hook) của Mongoose được gắn lên schema bằng userSchema.pre hoặc userSchema.post với một hàm, còn đoạn này chỉ khai báo trường. "A MongoDB query" sai vì truy vấn là lời gọi như User.find với bộ lọc, gửi yêu cầu tới máy chủ; khai báo schema không gửi gì tới MongoDB.

Mở trang câu hỏi này →
Afs.readFile
Bfs.readSync
Cfs.readFileSync
Dfs.read
Lời giải
Đáp án đúng gồm hai ý: "fs.readFile" và "fs.readFileSync". Module fs của Node.js đọc trọn nội dung một tệp theo đường dẫn bằng hai hàm này: fs.readFile chạy bất đồng bộ, trả kết quả qua callback theo quy ước lỗi trước (error-first), còn fs.readFileSync chặn luồng cho tới khi đọc xong và trả nội dung trực tiếp. "fs.read" là hàm cấp thấp có thật nhưng không đọc tệp theo đường dẫn: nó cần một file descriptor (fd) đã mở bằng fs.open cùng một Buffer, đọc từng đoạn byte, nên không phải cách đọc tệp thông thường mà câu hỏi nhắm tới. "fs.readSync" là bản đồng bộ cấp thấp của fs.read, cũng đòi fd và buffer chứ không nhận đường dẫn tệp, nên không được chọn cùng hai hàm đọc trọn tệp.
Mở trang câu hỏi này →
ATo set response status
BTo access incoming request metadata
CTo parse URL paths
DTo handle errors
Lời giải
Đáp án đúng: "To access incoming request metadata". Trong module http của Node.js, req là đối tượng IncomingMessage và req.headers là một đối tượng chứa các header của yêu cầu gửi tới, với tên header đã chuyển về chữ thường, ví dụ req.headers['content-type'] hay req.headers.authorization để đọc token; đó là siêu dữ liệu (metadata) mô tả yêu cầu. "To set response status" là việc của res, qua res.statusCode hoặc res.writeHead(). "To parse URL paths" thuộc về req.url kết hợp lớp URL hoặc module url, header không chứa đường dẫn cần phân tích. "To handle errors" không phải vai trò của header; lỗi được xử lý qua sự kiện error, try/catch hoặc middleware xử lý lỗi trong Express.
Mở trang câu hỏi này →
AMultithreaded
BAsynchronous and Event-Driven
CSynchronous and Blocking
DSingle-Threaded but Highly Scalable
Lời giải
Đáp án đúng gồm hai ý: "Asynchronous and Event-Driven" và "Single-Threaded but Highly Scalable". Node.js chạy mã JavaScript trên một luồng chính với vòng lặp sự kiện (event loop): các thao tác I/O như đọc tệp, truy vấn cơ sở dữ liệu, gọi mạng được giao cho hệ điều hành hoặc thư viện libuv xử lý bất đồng bộ, khi xong thì callback hay Promise được đưa lại vào hàng đợi sự kiện; nhờ không chặn luồng để chờ I/O, một tiến trình phục vụ được hàng nghìn kết nối đồng thời. "Multithreaded" sai vì mô hình lập trình của Node.js là đơn luồng; libuv có thread pool nội bộ và module worker_threads cho phép tạo luồng phụ, nhưng đó là công cụ bổ sung chứ không phải đặc điểm cốt lõi. "Synchronous and Blocking" ngược hẳn với triết lý của Node.js; hàm đồng bộ như fs.readFileSync có tồn tại nhưng làm nghẽn event loop và nên tránh trong máy chủ.
Mở trang câu hỏi này →
AFaster time to market
BBuilt-in server scaling
CCustom low-level hardware access
DPre-built authentication modules
Lời giải
Đáp án đúng: "Custom low-level hardware access". Câu hỏi phủ định: cần chọn thứ không phải lợi ích của BaaS (Backend as a Service). BaaS như Firebase hay Supabase gói sẵn hạ tầng sau những API dựng sẵn; đổi lại, người dùng không được chạm vào máy chủ vật lý, hệ điều hành hay phần cứng bên dưới, nên quyền truy cập phần cứng mức thấp tuỳ biến chính là thứ BaaS lấy đi chứ không mang lại. "Faster time to market" là lợi ích thật, vì không phải tự viết cơ sở dữ liệu, xác thực, lưu trữ nên sản phẩm ra mắt sớm hơn. "Built-in server scaling" cũng đúng, nhà cung cấp tự mở rộng tài nguyên khi lượng người dùng tăng. "Pre-built authentication modules" đúng nốt, ví dụ Firebase Authentication có sẵn đăng nhập bằng email, Google, số điện thoại. Ba điều này đều là lợi ích nên không được chọn.
Mở trang câu hỏi này →
Aexpress.urlencoded()
Bexpress.static()
Cexpress.json()
Dexpress.parse()
Lời giải
Đáp án đúng: "express.json()". express.json() là middleware tích hợp sẵn trong Express từ bản 4.16 (dựa trên body-parser), đọc body của request có Content-Type là application/json, phân tích chuỗi JSON và gán kết quả vào req.body; thiếu nó thì req.body là undefined. Thường khai báo app.use(express.json()) trước các route cần đọc body. "express.urlencoded()" phân tích body dạng application/x-www-form-urlencoded do form HTML gửi, không phải JSON. "express.static()" phục vụ tệp tĩnh như ảnh, CSS từ một thư mục, không động tới body của request. "express.parse()" không tồn tại trong Express, gọi nó sẽ báo lỗi không phải hàm.
Mở trang câu hỏi này →
A{ type: String }
B{ ref: 'User' }
C{ type: Schema.Types.ObjectId, ref: 'User' }
D{ id: ObjectId }
Lời giải
Đáp án đúng: "{ type: Schema.Types.ObjectId, ref: 'User' }". Một tham chiếu hợp lệ cần đủ hai phần: type là Schema.Types.ObjectId để trường lưu đúng kiểu _id của MongoDB, và ref là tên model User để populate() biết lấy tài liệu từ collection nào. "{ type: String }" chỉ là trường chuỗi, có thể chứa id dưới dạng văn bản nhưng không ép kiểu ObjectId và không có ref. "{ ref: 'User' }" thiếu type nên Mongoose coi đối tượng này như một đường dẫn lồng nhau chứ không phải tham chiếu, khai báo ref đơn độc không tạo được liên kết. "{ id: ObjectId }" khai báo một trường con tên id kiểu ObjectId, nhưng không có ref tới model nào nên không populate được.
Mở trang câu hỏi này →
ATo ensure that the cookie can only be accessed by the domain that created it.
BTo prevent the cookie from being sent over an unencrypted connection.
CTo prevent JavaScript code from accessing the cookie, which can help prevent cross-site scripting (XSS) attacks.
DTo set an expiration date for the cookie.
Lời giải
Đáp án đúng: "To prevent JavaScript code from accessing the cookie, which can help prevent cross-site scripting (XSS) attacks.". Cookie gắn cờ HttpOnly vẫn được trình duyệt gửi kèm mọi request tới máy chủ, nhưng không đọc được qua document.cookie trong JavaScript phía trình duyệt; vì vậy nếu trang bị chèn script độc (XSS), kẻ tấn công không lấy được cookie phiên hay token. Trong Express, cờ này đặt bằng tuỳ chọn httpOnly của res.cookie. "To ensure that the cookie can only be accessed by the domain" mô tả thuộc tính Domain cùng chính sách cùng nguồn, không phải HttpOnly. "To prevent the cookie from being sent over an unencrypted connection." là vai trò của cờ Secure, chỉ cho gửi cookie qua HTTPS. "To set an expiration date for the cookie." là việc của thuộc tính Expires hoặc Max-Age.
Mở trang câu hỏi này →
AdeleteMany
BremoveDocuments
CdeleteOne
DremoveMany
Lời giải
Đáp án đúng: "deleteMany". Phương thức deleteMany nhận một bộ lọc và xoá mọi document khớp điều kiện trong collection, ví dụ deleteMany với status bằng inactive xoá hết người dùng không hoạt động; kết quả trả về có deletedCount cho biết số document đã xoá. Truyền bộ lọc rỗng sẽ xoá toàn bộ document, nên phải cẩn thận. Phương thức này có cả trong MongoDB shell, Node.js driver lẫn model Mongoose. "removeDocuments" không phải phương thức nào của MongoDB. "deleteOne" có thật nhưng chỉ xoá document đầu tiên khớp bộ lọc, dù có nhiều document cùng thoả điều kiện. "removeMany" cũng không tồn tại; thứ có thật trước đây là remove(), đã bị đánh dấu lỗi thời và thay bằng cặp deleteOne, deleteMany.
Mở trang câu hỏi này →
AWildcard for simplicity
BWhitelist of trusted domains
CBoth are equally secure
DDisable CORS and use proxies
Lời giải
Đáp án đúng: "Whitelist of trusted domains". Danh sách trắng (whitelist) chỉ cho các origin được liệt kê, ví dụ cấu hình gói cors với tuỳ chọn origin là mảng tên miền frontend của mình, nên trang lạ không đọc được phản hồi API qua trình duyệt của người dùng; đây là nguyên tắc cấp quyền tối thiểu. "Wildcard for simplicity" đặt Access-Control-Allow-Origin bằng ký tự đại diện cho mọi trang đọc phản hồi, tiện cho API công khai nhưng mở rộng bề mặt tấn công, và không dùng được cùng credentials như cookie. "Both are equally secure" sai vì hai cách cho số lượng origin được phép khác hẳn nhau. "Disable CORS and use proxies" là đường vòng chứ không phải cấu hình CORS an toàn hơn: proxy cùng origin có thể dùng trong vài kiến trúc, nhưng câu hỏi so sánh hai cấu hình CORS và hạn chế origin mới trực tiếp tăng an toàn.
Mở trang câu hỏi này →
AEJS
BHandlebars
CPug
DMustache
Lời giải
Đáp án đúng: "EJS". EJS (Embedded JavaScript) nhúng JavaScript thẳng vào HTML bằng cặp thẻ phần trăm: thẻ trơn chạy mã như vòng lặp hay if, thẻ có dấu bằng in giá trị đã thoát ký tự HTML, thẻ có dấu gạch ngang in giá trị thô; trong Express bật bằng app.set('view engine', 'ejs') rồi gọi res.render(). "Handlebars" dùng cú pháp ngoặc nhọn kép cho biến và block helper như each, if. "Pug" không dùng thẻ đánh dấu mà dựa vào thụt lề và tên thẻ viết gọn, chẳng hạn một dòng h1= title. "Mustache" cũng dùng ngoặc nhọn kép, là ngôn ngữ template không logic mà Handlebars kế thừa và mở rộng.
Mở trang câu hỏi này →
Đề bài
const express = require('express');
const app = express();

app.all('/users', (req, res, next) => {
  console.log('Accessing the /users endpoint...');
  next(); // pass control to the next handler
});

app.delete('/users', (req, res) => {
  res.send('DELETE request to the /users endpoint');
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});
AThe response will be 'Accessing the /users endpoint...'
BThe response will be 'DELETE request to the /users endpoint'.
CThe response will be 'GET request to the /users endpoint'.
DThe response will be 'POST request to the /users endpoint'.
Lời giải
Đáp án đúng: "The response will be 'DELETE request to the /users endpoint'.". Lần theo mã: app.all('/users') khớp mọi phương thức HTTP tới /users nên chạy trước, in dòng Accessing the /users endpoint... ra console của server rồi gọi next() để chuyển tiếp; Express duyệt tiếp ngăn xếp route, gặp app.delete('/users') khớp cả đường dẫn lẫn phương thức DELETE, handler này gọi res.send nên client nhận chuỗi DELETE request to the /users endpoint. "The response will be 'Accessing the /users endpoint...'" sai vì console.log chỉ ghi log phía server, không gửi gì về client. "The response will be 'GET request to the /users endpoint'." sai vì mã không có route GET nào, và request ở đây là DELETE. "The response will be 'POST request to the /users endpoint'." sai vì cũng không có route POST, chuỗi đó không xuất hiện trong mã.
Mở trang câu hỏi này →
AHandle file uploads
BConnect to SQL databases
CProvide a web application framework
DCompile TypeScript files
Lời giải
Đáp án đúng: "Provide a web application framework". Express là web framework tối giản dựng trên module http của Node.js: nó cung cấp định tuyến (routing) theo phương thức và đường dẫn như app.get hay app.post, chuỗi middleware xử lý request theo thứ tự, các tiện ích trên req và res như res.json, res.status, res.render, cùng khả năng gắn view engine. Nhờ vậy viết API hay ứng dụng web gọn hơn nhiều so với dùng http trực tiếp. "Handle file uploads" không phải vai trò chính của Express; tải tệp lên được xử lý bằng middleware riêng như multer gắn vào Express. "Connect to SQL databases" là việc của driver hoặc ORM như pg, mysql2, Sequelize; Express không kèm sẵn kết nối cơ sở dữ liệu. "Compile TypeScript files" là việc của trình biên dịch tsc hoặc công cụ như ts-node, không liên quan tới Express.
Mở trang câu hỏi này →
Atoken
Bpassport
Ccookie
Dsession
Lời giải
Đáp án đúng: "token". Trong xác thực dựa trên token như JWT, sau khi đăng nhập thành công máy chủ ký và trả về một token; mỗi request sau đó client gửi lại token trong header, thường là Authorization: Bearer kèm chuỗi token, để middleware như passport-jwt hay jwt.verify kiểm tra chữ ký mà không cần lưu phiên ở máy chủ. "passport" là tên thư viện xác thực Passport chạy phía máy chủ, không phải dữ liệu client gửi đi. "cookie" được trình duyệt gửi tự động trong header Cookie, không cần client chủ động đính kèm, và gắn với xác thực dựa trên phiên chứ không phải luồng token được mô tả. "session" là dữ liệu lưu ở máy chủ; client chỉ giữ mã phiên trong cookie chứ không gửi cả phiên trong header.
Mở trang câu hỏi này →
AsetImmediate() schedules the callback function to run immediately after the current event loop iteration, regardless of the delay specified.
BsetTimeout() schedules the callback function to run after a specified delay, which can be zero (i.e., immediately) or a positive integer representing the number of milliseconds to wait.
CIf both setTimeout() and setImmediate() are called in the same loop iteration, setTimeout() will be executed before setImmediate ().
DNone of the above
Lời giải
Đáp án đúng: "If both setTimeout() and setImmediate() are called in the same loop iteration, setTimeout() will be executed before setImmediate ().". Câu hỏi tìm phát biểu sai. Thứ tự giữa setTimeout(fn, 0) và setImmediate() không được đảm bảo: gọi ở mã chính thì kết quả phụ thuộc hiệu năng tiến trình, có lần timer chạy trước, có lần ngược lại; còn gọi bên trong callback I/O như của fs.readFile thì setImmediate luôn chạy trước, vì sau pha poll event loop vào pha check trước khi quay lại pha timers. Khẳng định setTimeout luôn chạy trước vì thế là sai. "setImmediate() schedules the callback function to run immediately after the current event loop iteration" là phát biểu đúng về bản chất: callback được xếp vào pha check, chạy ngay sau pha poll của vòng hiện tại, và hàm này không nhận tham số độ trễ. "setTimeout() schedules the callback function to run after a specified delay" cũng đúng, độ trễ 0 thực tế được Node nâng lên 1 ms. "None of the above" sai vì có một phát biểu sai.
Mở trang câu hỏi này →