DrillLab
八股题库Question bank

105 道问答题,一道一卡105 questions, one card each

默认只显示问题 —— 先自己在心里答一遍,再展开对答案。答不上来就标「不会」,下次抽认卡会先抽它。Only the question shows by default. Answer it in your head first, then open the answer. Mark the ones you miss; the flashcard round puts those first.

0 / 105道自评过self-assessed
0Got it0模糊Shaky0不会No idea105还没做Not seen
正在读这台浏览器里的标记…Reading your marks from this browser…
标记不是打分,是给下一轮排队Marks are a queue, not a score

标「不会」的题会排到抽认卡最前面,标「会」的进低频池排最后。所以别客气 —— 觉得答得磕磕巴巴就标「模糊」。准备好了就去抽认卡“No idea” jumps to the front of the next round; “got it” drops to the back. So be honest — if it came out shaky, mark it shaky. Then go run a flashcard round.

题目Questions

筛出 6 道(共 105 道)。6 of 105 questions.
网络与安全Web & security#360

什么是 CORS,怎么解决 CORS 错误

What is CORS and how to solve the CORS error

看答案Show answer

一句话:浏览器的同源策略默认禁止页面读取 跨源响应;CORS 是服务器通过响应头「授权」某些跨源请求的机制。

三条最关键的认知(这才是区分度):

  • 是浏览器在拦,不是服务器拒绝。请求通常已经发出去了、 服务器也已经处理了—— 只是浏览器不让 JS 读响应。所以看到 CORS 错误不等于接口没执行(非幂等接口尤其要注意, 可能已经创建了数据)。
  • 所以前端改不了。必须服务端加响应头, 或者走代理。在前端加什么请求头都没用。
  • 同源 = 协议 + 域名 + 端口 三者全同。httphttps 不同源,30003001 不同源。

简单请求 vs 预检请求:GET / HEAD /POST 且只用安全头、Content-Type 限于三种 (form-urlencodedmultipart/form-datatext/plain)→ 直接发。
其他情况先发一个OPTIONS 预检注意 application/json就会触发预检—— 这就是为什么「明明是 POST 却多了一个 OPTIONS 请求」。

四种解法:

  1. 服务端加头(正解)——Access-Control-Allow-Origin, Express 里 app.use(cors())
  2. 开发时用 dev server 代理—— Vite 的 server.proxy, 让浏览器以为是同源。
  3. 生产用同源部署或网关—— 前端和 API 挂在同一个域下的不同路径。
  4. (历史方案)JSONP —— 只支持 GET,已淘汰。

会追问:「要带 cookie 怎么办?」—— 前端 credentials: "include", 服务端 Allow-Credentials: true而且此时Allow-Origin不能是 *,必须写具体域名。 这是最常见的「配了 cors 还是不行」的原因。
「预检能缓存吗?」——Access-Control-Max-Age, 避免每个请求都多一次往返。

In one line: the browser’s same-origin policy stops a page from reading a cross-origin response by default; CORS is the mechanism by which the server uses response headers to authorise some of those requests.

Three things to understand — this is what separates people:

  • The browser blocks it; the server did not refuse. The request usually went out and the server usually handled it — the browser just will not let your JS read the response. So a CORS error does not mean the endpoint did not run. Watch out with non-idempotent endpoints: the record may already exist.
  • Which is why the front end cannot fix it. The server has to send the headers, or you go through a proxy. No request header you add on the client will help.
  • Same origin means scheme, host and port all match. http and https are different origins; 3000 and 3001 are different origins.

Simple requests vs preflighted ones: GET / HEAD / POST with only safe headers and a Content-Type limited to three values (form-urlencoded, multipart/form-data, text/plain) go straight out.
Anything else sends an OPTIONS preflight first. Note that application/json triggers a preflight — that is why “it is a POST but I see an extra OPTIONS request”.

Four ways to fix it:

  1. Send the headers from the server (the real fix) — Access-Control-Allow-Origin, or app.use(cors()) in Express.
  2. Proxy through the dev server while developing — Vite’s server.proxy, so the browser thinks it is same-origin.
  3. In production, deploy same-origin or put a gateway in front — front end and API on the same domain, different paths.
  4. (Historical) JSONP — GET only, obsolete.

Follow-up: “What if I need to send cookies?” — credentials: "include" on the client, Allow-Credentials: true on the server, and at that point Allow-Origin cannot be * — it must name the origin. This is the most common reason for “I configured cors and it still does not work”.
“Can the preflight be cached?” — Access-Control-Max-Age, so you do not pay a round trip per request.

JavaScript两种最常用的解法示意Illustrative
1// 服务端(正解)
2app.use(cors({
3 origin: "https://app.example.com", // 带 cookie 时不能用 *
4 credentials: true,
5 maxAge: 86400, // 缓存预检结果
6}));
7
8// 开发时代理(vite.config.ts)
9server: {
10 proxy: { "/api": { target: "http://localhost:4000", changeOrigin: true } },
11}
12// 浏览器看到的是同源的 /api/...,不触发 CORS
1// On the server (the correct way)
2app.use(cors({
3 origin: "https://app.example.com", // with cookies you cannot use *
4 credentials: true,
5 maxAge: 86400, // cache the preflight result
6}));
7
8// A proxy during development (vite.config.ts)
9server: {
10 proxy: { "/api": { target: "http://localhost:4000", changeOrigin: true } },
11}
12// The browser sees /api/... on the same origin, so CORS is never triggered
网络与安全Web & security#358

HTTPS vs HTTP

HTTPS vs HTTP

看答案Show answer

一句话:HTTPS = HTTP + TLS 加密层。 同一套协议,只是传输过程被加密和验证了。

它提供三样东西(要说全):

  • 加密—— 中间人看不到内容
  • 身份验证—— 证书证明「你连的确实是这个域名的服务器」,这一条常被忽略, 但它才是防钓鱼的关键
  • 完整性—— 内容被篡改会被发现

握手大致过程:客户端打招呼 → 服务器发证书 → 客户端验证书链 →用非对称加密协商出一个对称密钥 → 之后用对称加密传数据。为什么要混用两种加密?非对称安全但慢, 对称快但要先安全地交换密钥 ——所以用非对称来交换对称密钥。 这一句是加分点。

端口:HTTP 80, HTTPS 443。

会追问:「HTTPS 慢吗?」—— 握手有额外开销, 但TLS 1.3 把握手压到一次往返, 而且HTTP/2 和 HTTP/3 只在 HTTPS 上可用—— 多路复用带来的收益通常超过加密的开销。 所以「用 HTTPS 会变慢」现在基本不成立。
「有了 HTTPS 就安全了吗?」——。它只保护传输过程。 XSS、SQL 注入、 弱口令、越权 一个都没解决。

In one line: HTTPS is HTTP plus a TLS layer. Same protocol; the transport is now encrypted and authenticated.

It gives you three things — name all three:

  • Encryption — someone in the middle cannot read the contents
  • Authentication — the certificate proves “you really are talking to the server for this domain”. People forget this one, and it is the part that stops phishing
  • Integrity — tampering is detected

Roughly how the handshake goes: client says hello → server sends its certificate → client verifies the chain → they use asymmetric crypto to agree on a symmetric key → everything after that is symmetric. Why mix the two? Asymmetric is secure but slow; symmetric is fast but needs the key exchanged safely first — so you use asymmetric to exchange the symmetric key. That sentence is the bonus point.

Ports: 80 for HTTP, 443 for HTTPS.

Follow-up: “Is HTTPS slow?” — the handshake costs something, but TLS 1.3 gets it down to one round trip, and HTTP/2 and HTTP/3 are only available over HTTPS — the multiplexing usually more than pays for the encryption. So “HTTPS makes it slower” no longer really holds.
“Does HTTPS make me secure?” — no. It protects the transport. XSS, SQL injection, weak passwords and broken authorisation are all still yours to solve.

网络与安全Web & security#359

什么是 JWT

What is JWT

看答案Show answer

一句话:JSON Web Token —— 一个自带签名的字符串, 服务器不用存它也能验证它没被篡改。

三段结构,用点分隔:header.payload.signature

  • header—— 用什么算法签的
  • payload—— 用户 id、过期时间等业务数据
  • signature—— 对前两段用密钥算出的签名

最重要的一条(必答):前两段只是Base64URL 编码,不是加密——任何人都能解出来看。 所以payload 里绝不能放密码、 身份证号这类敏感信息。 签名保证的是「没被改过」,不是「看不到」

优点:无状态—— 服务器不用存 session, 天然适合多实例和微服务 (任何一台都能独立验证)。

缺点(这半边是重点):

  • 没法主动失效。签出去就有效到过期。 用户改密码、 管理员封号,旧 token 照样能用
  • 体积比 session id 大, 每个请求都要带。
  • 放哪都有风险——localStorage 怕 XSS, cookie 怕 CSRF。

会追问:「怎么让 JWT 提前失效?」—— 这是这题的分水岭:

  • 短过期 + refresh token—— access token 只活 15 分钟, 用一个可撤销的 refresh token 换新的。这是标准做法。
  • 黑名单—— 把要作废的 token id 存 Redis。但这就重新变成有状态了, 等于放弃了 JWT 的主要优点。

安全上还有一个经典坑:验证时必须指定期望的算法, 不能信 header 里写的 —— 否则攻击者把 alg改成 none 就绕过签名了。

In one line: a JSON Web Token is a string that carries its own signature, so the server can verify it has not been tampered with without storing it.

Three parts, separated by dots: header.payload.signature

  • header — which algorithm signed it
  • payload — the data: user id, expiry and so on
  • signature — the first two parts signed with your secret

The one thing you must say: the first two parts are Base64URL encoded, not encrypted anyone can decode and read them. So never put a password or a national id number in the payload. The signature guarantees “unchanged”, not “unreadable”.

The upside: it is stateless — the server stores no session, which suits many instances and microservices, since any one of them can verify it alone.

The downsides — this half is the real question:

  • You cannot revoke it. Once issued it is valid until it expires. User changes their password, an admin bans the account — the old token still works.
  • It is bigger than a session id and rides along on every request.
  • Every place you store it has a risk localStorage is exposed to XSS, a cookie is exposed to CSRF.

Follow-up: “How do you expire a JWT early?” — this is where the question separates people:

  • Short expiry plus a refresh token — the access token lives 15 minutes and you trade a revocable refresh token for a new one. This is the standard answer.
  • A blocklist — keep revoked token ids in Redis. But now you are stateful again, which gives up the main reason you chose JWT.

One classic security trap: when you verify, you must pin the algorithm you expect rather than trust the one in the header — otherwise an attacker sets alg to none and walks past the signature entirely.

网络与安全Web & security#361

session vs cookie

sessions vs cookies

看答案Show answer

先纠正一个常见混淆:它们不是同级的东西。cookie 是「浏览器存小数据的机制」session 是「服务器记住用户状态的方案」—— 而 session 通常靠 cookie 来传那个 id

CookieSession
存在哪浏览器服务器(内存 / Redis / 数据库)
存什么小字符串(≤ 4 KB)任意大小的用户数据
安全性用户能看能改用户只拿到一个 id
能否主动失效要等过期或被覆盖能,删掉服务端记录就行

典型流程:登录成功 → 服务器建 session、 生成 session id → 通过 Set-Cookie 发给浏览器 → 之后每个请求浏览器自动带上 → 服务器用 id 查出用户。

Cookie 的四个安全属性必须会:

  • HttpOnly—— JS 读不到,防 XSS 偷 cookie
  • Secure—— 只在 HTTPS 下发送
  • SameSite——Strict / Lax /None防 CSRF的主要手段
  • Max-Age / Domain /Path—— 作用范围

会追问:「session vs JWT 怎么选?」——

  • 要能立刻踢人下线(后台管理、支付类)→ session
  • 多服务、跨域、 移动端 + Web 共用JWT(配 refresh token)

还会问:「session 在多实例部署下怎么办?」—— 存内存会导致「刷新一下就掉登录」 (请求打到别的实例)。 解法是把 session 存 Redis, 或者用粘性会话(不推荐)。

First, clear up the usual confusion: these are not two options at the same level. A cookie is a browser mechanism for storing a small value; a session is a server-side way of remembering who the user is — and a session normally uses a cookie to carry its id.

CookieSession
Lives whereThe browserThe server (memory / Redis / database)
Holds whatA small string, 4 KB or lessUser data of any size
SecurityThe user can read it and change itThe user only ever holds an id
Can you revoke it?Only by expiry or overwriteYes — delete the server-side record

The typical flow: login succeeds → the server creates a session and a session id → it goes out via Set-Cookie → the browser attaches it to every subsequent request → the server looks the user up by that id.

You have to know the four cookie security attributes:

  • HttpOnly — JS cannot read it, which stops XSS from stealing the cookie
  • Secure — only sent over HTTPS
  • SameSite Strict / Lax / None, the main defence against CSRF
  • Max-Age / Domain / Path — its scope

Follow-up: “Session or JWT?” —

  • You need to kick someone out right now (admin panels, anything touching payments) → session
  • Many services, cross-domain, one API for both mobile and web JWT, with a refresh token

They will also ask: “What happens to sessions across several instances?” — keeping them in memory means “refresh the page and I am logged out” when the request lands on a different instance. The fix is to put sessions in Redis, or sticky sessions (not recommended).

网络与安全Web & security#362

常见的 HTTP 状态码

Give some HTTP response status codes

看答案Show answer

先说五个类别,再举例 —— 这样显得有体系:1xx 信息、2xx 成功3xx 重定向4xx 客户端错5xx 服务端错

含义什么时候用
200 OK成功GET / PUT / PATCH 成功
201 Created已创建POST 成功,建议带 Location 头
204 No Content成功但没内容DELETE 成功
301 / 302永久 / 临时重定向301 会被浏览器缓存,改错了很难收回
304 Not Modified没变,用缓存配 ETag / Last-Modified
400 Bad Request请求有问题参数缺失、格式错、校验失败
401 Unauthorized没登录 / token 无效「你是谁?」
403 Forbidden登录了但没权限「知道你是谁,但你不能干这个」
404 Not Found资源不存在
409 Conflict冲突重复注册、并发修改
422语义错误格式对但业务上不合法
429请求太多限流
500服务端异常未捕获的错误
502 / 503 / 504网关错 / 不可用 / 超时上游挂了、在维护、上游太慢

401 vs 403 是最常问的一对401 是「没认证」,403 是「认证了但没授权」
实践里有个细节:为了不泄露资源是否存在, 有些接口会把「没权限」也返回 404。

会追问:「业务错误该用 4xx 还是 200 带错误码?」——REST 风格用 4xx(让 HTTP 语义承载错误), 但要注意有些老网关会吞掉 4xx 的响应体GraphQL 则一律返 200, 错误放在 errors 字段里 —— 因为一个请求可能部分成功, 没法用单个状态码表达。这个对比答出来很加分, Federation 那门课里 extensions.code就是干这个的。

Name the five classes first, then give examples — it reads as organised: 1xx informational, 2xx success, 3xx redirect, 4xx client error, 5xx server error.

CodeMeansWhen
200 OKSuccessA successful GET / PUT / PATCH
201 CreatedCreatedA successful POST — send a Location header
204 No ContentSuccess, nothing to returnA successful DELETE
301 / 302Permanent / temporary redirectBrowsers cache 301, so a wrong one is hard to take back
304 Not ModifiedUnchanged, use your cachePaired with ETag or Last-Modified
400 Bad RequestThe request is wrongMissing parameter, bad format, failed validation
401 UnauthorizedNot logged in, or the token is invalid“Who are you?”
403 ForbiddenLogged in but not allowed“I know who you are, and you cannot do this”
404 Not FoundNo such resource
409 ConflictConflictDuplicate signup, concurrent edit
422Semantically wrongWell-formed but invalid for the business rules
429Too many requestsRate limiting
500Server failedAn uncaught error
502 / 503 / 504Bad gateway / unavailable / timeoutUpstream is down, in maintenance, or too slow

401 vs 403 is the pair they ask about most: 401 means not authenticated, 403 means authenticated but not authorised.
One detail from practice: to avoid leaking whether a resource exists, some endpoints return 404 for “not allowed” as well.

Follow-up: “Should a business error be a 4xx or a 200 with an error code?” — REST says 4xx, so the HTTP semantics carry the error, but watch out: some older gateways swallow the body of a 4xx. GraphQL always returns 200 and puts errors in the errors field, because one request can be partially successful and no single status code says that. Drawing that contrast earns you points — in the Federation course, extensions.code is exactly this.

网络与安全Web & security#357

测试有哪几种

What are the different kinds of tests

看答案Show answer

一句话:按范围从小到大 ——单元 → 集成 → 端到端, 这就是「测试金字塔」:越往上越慢越脆,所以数量越少

  • 单元测试(unit)—— 测一个函数或一个组件, 依赖全部 mock。快、多、定位准。例:一个纯函数、 一个 React 组件的渲染。
  • 集成测试(integration)—— 测几个模块协作是否正确, 可能真的连数据库或起一个测试服务器。 例:调一个 API 端点, 断言它真的写进了库。
  • 端到端(E2E)——用真实浏览器走完整用户流程。 Playwright / Cypress。 最接近真实,也最慢最容易随机失败。

还会提到的几种:回归测试(防止改坏老功能)、 快照测试(比对渲染输出,容易变成「随手更新快照」的橡皮章)、 性能 / 压力测试、 可访问性测试、 冒烟测试(上线后快速验证主流程)。

Testing Library 的核心理念值得说:「像用户一样测试」—— 按可见文本和 role 查元素, 而不是按 class 名或组件内部结构。 这样重构内部实现测试不会碎。

会追问:「测试覆盖率要多少?」——不要给一个死数字。 正确回答是:覆盖率只说明「代码被执行过」, 不说明「断言是对的」
这一点我可以给一个实测例子: Federation 那门课的源项目里, 六个端点全部 return null也能通过 3 个测试, node-subgraph 的 4 个「通过」里3 个是「空实现恰好满足断言」所以「测试通过 ≠ 做对了」—— 比覆盖率数字更该关心的是断言够不够强。

In one line: smallest scope to largest — unit → integration → end-to-end. That is the testing pyramid: higher means slower and flakier, so you write fewer of them.

  • Unit — one function or one component, everything else mocked. Fast, numerous, and precise about where the problem is. A pure function; a React component rendering.
  • Integration — do a few modules work together, possibly against a real database or a test server. Call an API endpoint and assert the row really landed.
  • End-to-end a real browser walking a whole user journey. Playwright or Cypress. Closest to reality, and also the slowest and the most prone to random failure.

Others worth mentioning: regression tests (so old behaviour does not break), snapshot tests (comparing rendered output — which easily degrades into rubber-stamping “update snapshot”), performance and load tests, accessibility tests, and smoke tests for a quick check of the main flow after a deploy.

Testing Library’s core idea is worth stating: “test it the way a user uses it” — find elements by visible text and role, not by class name or internal component structure. Then refactoring the internals does not shatter the tests.

Follow-up: “What coverage number should you aim for?” — do not give a number. The right answer is that coverage tells you code was executed, not that the assertions are any good.
Here is a measured example: in the source project behind the Federation course, six endpoints that all just return null still passed 3 tests, and of the 4 passes in node-subgraph, 3 were an empty implementation happening to satisfy the assertion. So a green test does not mean you got it right — the strength of the assertions matters more than the coverage percentage.

这些题从哪来Where these come from

99 道来自面试题库 #269–#387;TypeScript 深度那 6 道是 DrillLab 自出的(senior 补强,卡片上有标注)。答案都是 DrillLab 写的,所以讲解里的代码块一律标「示意」。每道题都能点回它出处的那一节课。99 questions come from the interview bank (#269–#387); the 6 TypeScript deep-dive ones are DrillLab-made (marked on the card). All answers are written by DrillLab, so every code block here is labelled “demo”. Each card links back to the lesson it came from.