DrillLab
练习Practice

动手做Get your hands on it

练习跟着课文走 —— 每节课尾都有本课的练习。这一页是全部练习的总库,想集中刷题的时候来。 每个练习都写清了它来自哪一节,卡住了就回去看那一节。Practice follows the lessons — every lesson ends with the exercises for that lesson. This page is the whole library, for when you want to drill in one sitting. Each exercise names the lesson it came from, so you can go back when you stall.

0 / 148个做对过you got right

练习Exercises

筛出 47 个练习(共 148 个) · 第 4 / 4 页。Showing 47 of 148 · page 4 / 4.
来自From 六个端点:状态码就是这道题的全部Six endpoints: the status codes are the whole task · Federation 考试Federation exam
L2填空Fill the blanks补全三个关键端点的状态码与调用Fill in the status codes and calls of three key endpoints

五个空。第 2、4、5 个是这道题真正的得分点。

Five blanks. Numbers 2, 4 and 5 are where the credit in this question actually is.

JAVAOrderController.java5 个空5 blanks
1@GetMapping("/api/orders/{id}")
2public ResponseEntity<Order> getOrderById(@PathVariable Long id) {
3 // 找不到时 service 抛 EntityNotFoundException,交给全局处理器
4 return ResponseEntity.(orderService.getOrderById(id));
5}
6
7@PostMapping("/api/orders")
8public ResponseEntity<Order> createOrder(@Valid @RequestBody CreateOrderRequest request) {
9 Order created = orderService.createOrder(request);
10 return ResponseEntity.status(HttpStatus.).body(created);
11}
12
13@PatchMapping("/api/orders/{id}/status")
14public ResponseEntity<Order> updateOrderStatus(
15 @PathVariable Long id,
16 @RequestBody Map<String, String> statusUpdate) {
17 String raw = statusUpdate.get("status");
18 if (raw == null || raw.isBlank()) {
19 throw new ResponseStatusException(HttpStatus., "status is required");
20 }
21 OrderStatus status = OrderStatus.(raw.trim().toUpperCase());
22 return ResponseEntity.ok(orderService.updateOrderStatus(id, status));
23}
24
25@DeleteMapping("/api/orders/{id}")
26public ResponseEntity<Void> deleteOrder(@PathVariable Long id) {
27 orderService.deleteOrder(id);
28 return ResponseEntity.().build();
29}
把 5 个空都填上才能检查(还差 5 个)Fill all 5 blanks to check (5 to go)
来自From Debug Lab · Federation 十种典型故障Debug Lab · ten common Federation failures · Federation 考试Federation exam
L2Debug LabDebug Lab故障 2 · Cannot return null for non-nullable fieldFault 2 · Cannot return null for non-nullable field

查一个没有订单的用户,整个 data 变成了null,而且 errors 里有一条很长的消息。

You query a user who has no orders, the whole data turns into null, and errors carries one very long message.

第 1 步 · 先看现象和代码,别急着改Step 1 · read the symptom and the code before you touch anything
$ node verify-schema.mjs Query.orders: {"orders":null} errors: [{ "message": "Cannot return null for non-nullable field Query.orders.", "path": ["orders"], "extensions": { "code": "INTERNAL_SERVER_ERROR" } }] # 更严重的情况:如果查询是嵌套的,整个 data 会变成 null$ node verify-schema.mjs Query.orders: {"orders":null} errors: [{ "message": "Cannot return null for non-nullable field Query.orders.", "path": ["orders"], "extensions": { "code": "INTERNAL_SERVER_ERROR" } }] # Worse case: if the query is nested, the whole data object turns into null
JavaScriptsrc/resolvers/orderResolvers.js示意Illustrative
1async orders(_, { userId }, { dataSources, correlationId }) {
2 const orders = await dataSources.orderDataSource.getOrdersByUserId(userId);
3 return orders; // 数据源可能返回 undefined
4}
5
6// 参考 schema.graphql:
7// type Query {
8// orders(userId: ID!): [Order!]! ← 双重非空
9// }
1async orders(_, { userId }, { dataSources, correlationId }) {
2 const orders = await dataSources.orderDataSource.getOrdersByUserId(userId);
3 return orders; // the data source may return undefined
4}
5
6// For reference, schema.graphql says:
7// type Query {
8// orders(userId: ID!): [Order!]! ← non-null twice
9// }
第 2 步 · 这是什么类型的错误Step 2 · what kind of error is this
来自From Debug Lab · Federation 十种典型故障Debug Lab · ten common Federation failures · Federation 考试Federation exam
L2Debug LabDebug Lab故障 4 · PATCH 传了小写状态,返回 500Fault 4 · PATCH sends a lowercase status and gets a 500

Java 那边。mvn test 全过, 但客户端传小写的 shipped 时服务返回 500。

This one is on the Java side. mvn test passes everything, but the service returns 500 when the client sends the lowercase shipped.

第 1 步 · 先看现象和代码,别急着改Step 1 · read the symptom and the code before you touch anything
$ curl -i -X PATCH localhost:8080/api/orders/1/status \ -H 'Content-Type: application/json' -d '{"status":"shipped"}' HTTP/1.1 500 {"timestamp":"...","status":500,"error":"Internal Server Error"} # 服务端日志: java.lang.IllegalArgumentException: No enum constant com.techflow.orders.model.OrderStatus.shipped at java.base/java.lang.Enum.valueOf(Enum.java:293) at com.techflow.orders.model.OrderStatus.valueOf(OrderStatus.java:3) at c.t.orders.controller.OrderController.updateOrderStatus(OrderController.java:71) # mvn test:Tests run: 5, Failures: 0 ← 测试全过$ curl -i -X PATCH localhost:8080/api/orders/1/status \ -H 'Content-Type: application/json' -d '{"status":"shipped"}' HTTP/1.1 500 {"timestamp":"...","status":500,"error":"Internal Server Error"} # Server log: java.lang.IllegalArgumentException: No enum constant com.techflow.orders.model.OrderStatus.shipped at java.base/java.lang.Enum.valueOf(Enum.java:293) at com.techflow.orders.model.OrderStatus.valueOf(OrderStatus.java:3) at c.t.orders.controller.OrderController.updateOrderStatus(OrderController.java:71) # mvn test: Tests run: 5, Failures: 0 ← every test passes
JavaOrderController.java示意Illustrative
1@PatchMapping("/api/orders/{id}/status")
2public ResponseEntity<Order> updateOrderStatus(
3 @PathVariable Long id,
4 @RequestBody Map<String, String> statusUpdate) {
5 OrderStatus status = OrderStatus.valueOf(statusUpdate.get("status"));
6 return ResponseEntity.ok(orderService.updateOrderStatus(id, status));
7}
第 2 步 · 这是什么类型的错误Step 2 · what kind of error is this
来自From 缺口一 · Dropdown、Tabs、星级评分Gap 1 · dropdown, tabs and star rating · 面试八股Interview questions
L2填空Fill the blanks补全「点外面关掉」Fill in "click outside closes it"DrillLab 自出Written by DrillLab

四个空。第 2 个用错会导致「点自己内部也关掉」, 第 4 个漏了会泄漏监听器。

Four blanks. Get the 2nd one wrong and a click inside the dropdown closes it too; miss the 4th one and you leak a listener.

TSXsrc/components/Dropdown/index.tsx4 个空4 blanks
1const boxRef = <HTMLDivElement>(null);
2
3useEffect(() => {
4 if (!open) return;
5
6 const onDocClick = (e: MouseEvent) => {
7 if (boxRef.current && !boxRef.current.(e.target as Node)) {
8 setOpen(false);
9 }
10 };
11
12 document.addEventListener("", onDocClick);
13 return () => document.("", onDocClick);
14}, [open]);
把 4 个空都填上才能检查(还差 4 个)Fill all 4 blanks to check (4 to go)
来自From 缺口三 · 同一个 Todo 换成 Redux ToolkitGap 3 · the same Todo app, moved to Redux Toolkit · 面试八股Interview questions
L2填空Fill the blanks补全 createSliceFill in createSliceDrillLab 自出Written by DrillLab

四个空。第 3 个空是这道题的加分点, 第 4 个空写错会让 reducer 不纯。

Four blanks. The 3rd one is the bonus point of this question; get the 4th one wrong and the reducer is no longer pure.

TSsrc/store/todosSlice.ts4 个空4 blanks
1const todosSlice = ({
2 name: "todos",
3 initialState,
4 reducers: {
5 added: {
6 reducer(state, action: PayloadAction<Todo>) {
7 state.items.(action.payload);
8 },
9 (text: string) {
10 return { payload: { id: (), text: text.trim(), done: false } };
11 },
12 },
13 toggled(state, action: PayloadAction<string>) {
14 const t = state.items.find((x) => x.id === action.payload);
15 if (t) t.done = !t.done;
16 },
17 },
18});
把 4 个空都填上才能检查(还差 4 个)Fill all 4 blanks to check (4 to go)
来自From 先读四个测试:它们到底要什么Read the four tests first: what exactly they ask for · Cab BookingCab Booking
L2填空Fill the blanks补齐 RideHistory 的两个 testid 和互斥逻辑Fill in the two testids of RideHistory and the either-or logic
空的时候只能出现 no-ride-title, 有记录的时候只能出现 history-cabs。把三个空填上。When it is empty only no-ride-title may appear; when there are records only history-cabs may appear. Fill in the three blanks.
JSXsrc/components/Home/RideHistory.jsx3 个空3 blanks
1const RideHistory = () => {
2 const { rideHistory } = useCabContext();
3 const latestRides = rideHistory.slice(-3).reverse();
4
5 return (
6 <section className="history-container">
7 <h3>Ride History</h3>
8
9 {latestRides.length > 0 ? (
10 <ul className="history-list">
11 {latestRides.map((ride, index) => (
12 <li key={`${ride.id}-${index}`} data-testid="">
13 <span>{ride.name}</span>
14 <strong>${ride.price}</strong>
15 </li>
16 ))}
17 </ul>
18 ) : (
19 <p data-testid="" className="empty-state">
20
21 </p>
22 )}
23 </section>
24 );
25};
把 3 个空都填上才能检查(还差 3 个)Fill all 3 blanks to check (3 to go)
来自From Context 放在哪一层 —— 这道题最容易死的地方Which level the Context goes on — the most common way to fail this task · Cab BookingCab Booking
L2填空Fill the blanks补齐 Context 三件套Fill in the three parts of the Context
四个空。第 4 个空是这道题的守卫,写错了就等于没有守卫。Four blanks. The fourth one is the guard, and getting it wrong is the same as having no guard at all.
JSXsrc/context/CabContext.jsx4 个空4 blanks
1import { createContext, useContext, useState } from "react";
2
3const CabContext = ();
4
5const CabProvider = ({ children }) => {
6 const [bookedCabDetails, setBookedCabDetails] = useState(null);
7 const [rideHistory, setRideHistory] = useState([]);
8
9 const updateBookedCabDetails = (details) => {
10 setBookedCabDetails(details);
11 setRideHistory();
12 };
13
14 return (
15 <CabContext.
16 value={{ bookedCabDetails, updateBookedCabDetails, rideHistory }}
17 >
18 {children}
19 </CabContext.>
20 );
21};
22
23const useCabContext = () => {
24 const context = useContext(CabContext);
25
26 if () {
27 throw new Error("useCabContext must be used within a CabProvider");
28 }
29
30 return context;
31};
32
33export { CabProvider, useCabContext };
把 4 个空都填上才能检查(还差 4 个)Fill all 4 blanks to check (4 to go)
来自From 用一个 state 管四个页面Controlling four pages with one piece of state · Cab BookingCab Booking
L2填空Fill the blanks补齐 App 的状态机Fill in the state machine of App
五个空。注意第 4 个空是这道题最容易写反的地方。Five blanks. The fourth one is the easiest place in this exercise to get backwards.
JSXsrc/App.jsx5 个空5 blanks
1const App = () => {
2 const [currentPage, setCurrentPage] = useState();
3 const { } = useCabContext();
4
5 const handleSelectCab = (cab) => {
6 (cab);
7 setCurrentPage("loading");
8 };
9
10 return (
11 <div className="App">
12 <AppHeader title={title} />
13
14 {currentPage === "home" && (
15 <Home onBookClick={() => setCurrentPage("cab-options")} />
16 )}
17
18 {currentPage === "cab-options" && (
19 <CabOptions onSelectCab={} />
20 )}
21
22 {currentPage === "loading" && (
23 <Loading onComplete={() => setCurrentPage("cab-confirmation")} />
24 )}
25
26 {currentPage === "cab-confirmation" && (
27 <CabConfirmation onConfirm={() => } />
28 )}
29 </div>
30 );
31};
把 5 个空都填上才能检查(还差 5 个)Fill all 5 blanks to check (5 to go)
来自From Loading:一秒之后自己跳走Loading: it moves to the next page by itself after one second · Cab BookingCab Booking
L2填空Fill the blanks补齐 Loading 的四个空Fill in the four blanks of Loading
第 3 个空是这道题的送分点,第 4 个空是这道题的良心。Blank 3 is the one that earns the marks. Blank 4 is the one that is simply the right thing to do.
JSXsrc/components/Loading/Loading.jsx4 个空4 blanks
1import { useEffect } from "react";
2
3const Loading = ({ onComplete }) => {
4 (() => {
5 const timer = (() => {
6 if (onComplete) onComplete();
7 }, );
8
9 return () => ;
10 }, [onComplete]);
11
12 return (
13 <main data-testid="loading" className="loading-container">
14 <div className="spinner" aria-hidden="true" />
15 <h1>Loading...</h1>
16 <p>We are working on your cab booking. Thanks for your patience.</p>
17 </main>
18 );
19};
把 4 个空都填上才能检查(还差 4 个)Fill all 4 blanks to check (4 to go)
来自From Loading:一秒之后自己跳走Loading: it moves to the next page by itself after one second · Cab BookingCab Booking
L2Debug LabDebug LabDebug Lab:定时器永远不到期Debug Lab: the timer never fires
测试 3 报「找不到 confirm-message」, 而 DOM 快照显示页面还停在 loading。 先读报错,再看下面那个 Loading 组件 ——它和源项目差一个东西Test 3 reports that it cannot find confirm-message, and the DOM snapshot shows the page still sitting on loading. Read the error first, then look at the Loading component below — one thing in it differs from the source project.
第 1 步 · 先看现象和代码,别急着改Step 1 · read the symptom and the code before you touch anything
FAIL src/test/App.test.jsx > React: Cab Booking > completes a booking and adds it to ride history TestingLibraryElementError: Unable to find an element by: [data-testid="confirm-message"] Ignored nodes: comments, script, style <body> <div> <div class="App"> <header …>…</header> <main class="loading-container" data-testid="loading"> <div class="spinner" aria-hidden="true" /> <h1>Loading...</h1> </main> </div> </div> </body> ❯ src/test/App.test.jsx:52:17
JSXsrc/components/Loading/Loading.jsx(有问题的版本)src/components/Loading/Loading.jsx (the broken version)示意Illustrative
1const Loading = ({ onComplete }) => {
2 useEffect(() => {
3 const timer = setTimeout(() => {
4 if (onComplete) onComplete();
5 }, 1000);
6
7 return () => clearTimeout(timer);
8 }); // ← 依赖数组呢?
9
10 return (
11 <main data-testid="loading" className="loading-container">
12 <div className="spinner" aria-hidden="true" />
13 <h1>Loading...</h1>
14 </main>
15 );
16};
1const Loading = ({ onComplete }) => {
2 useEffect(() => {
3 const timer = setTimeout(() => {
4 if (onComplete) onComplete();
5 }, 1000);
6
7 return () => clearTimeout(timer);
8 }); // ← where is the dependency array?
9
10 return (
11 <main data-testid="loading" className="loading-container">
12 <div className="spinner" aria-hidden="true" />
13 <h1>Loading...</h1>
14 </main>
15 );
16};
第 2 步 · 这是什么类型的错误Step 2 · what kind of error is this
来自From 完整答案跑不起来 —— 一个扩展名的事The complete answer does not run — the cause is one file extension · Cab BookingCab Booking
L2Debug LabDebug LabDebug Lab:0 个测试跑起来Debug Lab: zero tests run
README 说「先运行完整答案熟悉流程」。npm install 成功,npx vitest run 却是下面这个输出。注意最后一行的「no tests」。The README says to run the finished answer first to get used to the flow. npm install succeeds, and npx vitest run gives the output below. Look at the “no tests” on the last line.
第 1 步 · 先看现象和代码,别急着改Step 1 · read the symptom and the code before you touch anything
RUN v2.1.8 /Users/you/cab-booking-context ❯ src/test/App.test.jsx (0 test) ⎯⎯⎯⎯⎯⎯ Failed Suites 1 ⎯⎯⎯⎯⎯⎯⎯ FAIL src/test/App.test.jsx [ src/test/App.test.jsx ] Error: Failed to parse source for import analysis because the content contains invalid JS syntax. If you are using JSX, make sure to name the file with the .jsx or .tsx extension. Plugin: vite:import-analysis File: /Users/you/cab-booking-context/src/context/CabContext.js:19:27 17 | > 18 | {children} 19 | </CabContext.Provider> | ^ 20 | ); 21 | }; ❯ TransformPluginContext._formatError node_modules/vite/dist/node/chunks/dep-CB_7IfJ-.js:49255:41 ❯ TransformPluginContext.error node_modules/vite/dist/node/chunks/dep-CB_7IfJ-.js:49250:16 Test Files 1 failed (1) Tests no tests
JSXsrc/context/CabContext.js ← 注意这个扩展名src/context/CabContext.js ← look at that extension源项目From source
1import { createContext, useContext, useState } from "react";
2
3const CabContext = createContext();
4
5const CabProvider = ({ children }) => {
6 const [bookedCabDetails, setBookedCabDetails] = useState(null);
7 const [rideHistory, setRideHistory] = useState([]);
8
9 const updateBookedCabDetails = (details) => {
10 setBookedCabDetails(details);
11 setRideHistory([...rideHistory, details]);
12 };
13
14 return (
15 <CabContext.Provider
16 value={{ bookedCabDetails, updateBookedCabDetails, rideHistory }}
17 >
18 {children}
19 </CabContext.Provider>
20 );
21};
22
23const useCabContext = () => {
24 const context = useContext(CabContext);
25
26 if (!context) {
27 throw new Error("useCabContext must be used within a CabProvider");
28 }
29
30 return context;
31};
32
33export { CabProvider, useCabContext };
Source: cab-booking-context/src/context/CabContext.js
第 2 步 · 这是什么类型的错误Step 2 · what kind of error is this