Skip to content

Commit a84aa16

Browse files
committed
docs(notes): add Spring request context options guide
1 parent f3fe589 commit a84aa16

File tree

3 files changed

+146
-2
lines changed

3 files changed

+146
-2
lines changed

_posts/en/2025-01-11-notes-en.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ generated: false
77
translated: false
88
---
99

10-
These 2131 notes are primarily generated by AI chatbots. I used them to summarize key points and will walk through them to enhance my understanding.
10+
These 2132 notes are primarily generated by AI chatbots. I used them to summarize key points and will walk through them to enhance my understanding.
1111

12+
* [Spring Request Context Options](/notes/2025-09-04-spring-request-context-en)
1213
* [AI Model Context Window Capacities](/notes/2025-09-03-ai-model-context-window-en)
1314
* [Alphabet Hits $186.66B in H1 2025](/notes/2025-09-03-alphabet-h1-2025-revenue-en)
1415
* [Building Trust Takes Time and Consistency](/notes/2025-09-03-building-trust-consistency-en)
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
---
2+
title: Spring Request Context Options
3+
lang: en
4+
layout: post
5+
audio: false
6+
translated: false
7+
generated: true
8+
image: false
9+
---
10+
11+
Great use-case for a tiny “request context” in Spring. You’ve got a few clean options—pick one based on your stack:
12+
13+
### 1) Request-scoped bean (simple & safe)
14+
15+
Store the test user id in a request-scoped bean that your `TestUserFilter` sets, and any component can inject/read.
16+
17+
```java
18+
// holds the value for the lifetime of a single HTTP request
19+
@Component
20+
@RequestScope
21+
public class TestUserContext {
22+
private String userId;
23+
public String getUserId() { return userId; }
24+
public void setUserId(String userId) { this.userId = userId; }
25+
}
26+
```
27+
28+
```java
29+
@Component
30+
public class TestUserFilter extends OncePerRequestFilter {
31+
private final TestUserContext ctx;
32+
public TestUserFilter(TestUserContext ctx) { this.ctx = ctx; }
33+
34+
@Override
35+
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
36+
throws ServletException, IOException {
37+
String id = req.getHeader("X-Test-User-Id");
38+
if (id == null) id = req.getParameter("testUserId");
39+
if (id != null && !id.isBlank()) ctx.setUserId(id);
40+
chain.doFilter(req, res); // request-scope bean is auto-cleaned after request ends
41+
}
42+
}
43+
```
44+
45+
Use anywhere:
46+
47+
```java
48+
@Service
49+
public class SomeService {
50+
private final TestUserContext ctx;
51+
public SomeService(TestUserContext ctx) { this.ctx = ctx; }
52+
public void doWork() {
53+
String testUserId = ctx.getUserId(); // null if not provided
54+
}
55+
}
56+
```
57+
58+
**Why:** No manual cleanup; works across controllers/services/DAOs within the same request.
59+
60+
---
61+
62+
### 2) Spring Security context (recommended if you already use Spring Security)
63+
64+
Let the filter set an `Authentication` so all components can read it via `SecurityContextHolder` or `@AuthenticationPrincipal`.
65+
66+
```java
67+
@Component
68+
public class TestUserFilter extends OncePerRequestFilter {
69+
@Override
70+
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
71+
throws IOException, ServletException {
72+
String id = req.getHeader("X-Test-User-Id");
73+
if (id == null) id = req.getParameter("testUserId");
74+
75+
if (id != null && !id.isBlank()) {
76+
var auth = new UsernamePasswordAuthenticationToken(
77+
id, "N/A", List.of(new SimpleGrantedAuthority("ROLE_TEST")));
78+
SecurityContextHolder.getContext().setAuthentication(auth);
79+
}
80+
try {
81+
chain.doFilter(req, res);
82+
} finally {
83+
SecurityContextHolder.clearContext();
84+
}
85+
}
86+
}
87+
```
88+
89+
Use anywhere:
90+
91+
```java
92+
String testUserId = (String) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
93+
```
94+
95+
or inject `Principal`/`Authentication` in controllers.
96+
97+
**Why:** Plays nicely with method security, auditing, logs, etc.
98+
99+
---
100+
101+
### 3) ThreadLocal holder (works without web/security deps; remember to clear)
102+
103+
If you’re not using request scope or Security:
104+
105+
```java
106+
public final class TestUserHolder {
107+
private static final ThreadLocal<String> USER_ID = new InheritableThreadLocal<>();
108+
public static void set(String id) { USER_ID.set(id); }
109+
public static String get() { return USER_ID.get(); }
110+
public static void clear() { USER_ID.remove(); }
111+
}
112+
```
113+
114+
```java
115+
@Component
116+
public class TestUserFilter extends OncePerRequestFilter {
117+
@Override
118+
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
119+
throws IOException, ServletException {
120+
String id = req.getHeader("X-Test-User-Id");
121+
if (id == null) id = req.getParameter("testUserId");
122+
if (id != null && !id.isBlank()) TestUserHolder.set(id);
123+
try {
124+
chain.doFilter(req, res);
125+
} finally {
126+
TestUserHolder.clear(); // CRITICAL for pooled threads
127+
}
128+
}
129+
}
130+
```
131+
132+
If you run async tasks and need propagation, add a `TaskDecorator` to copy the ThreadLocal.
133+
134+
---
135+
136+
### Quick tips
137+
138+
* Put the id in a header like `X-Test-User-Id` (easy to pass from Postman/MockMvc) or a query param for local testing.
139+
* For unit tests with MockMvc: `.header("X-Test-User-Id", "u123")`.
140+
* Prefer **Option 1** if you want a simple “bag” visible to any bean in the same HTTP request.
141+
* Prefer **Option 2** if you already use Spring Security or want authorization/auditing integration.
142+
* Use **Option 3** only when you don’t want Spring scopes/security; be meticulous about clearing to avoid leaks.

original/2025-01-11-notes-en.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ generated: false
77
translated: false
88
---
99

10-
These 2131 notes are primarily generated by AI chatbots. I used them to summarize key points and will walk through them to enhance my understanding.
10+
These 2132 notes are primarily generated by AI chatbots. I used them to summarize key points and will walk through them to enhance my understanding.
1111

12+
* [Spring Request Context Options](/notes/2025-09-04-spring-request-context-en)
1213
* [AI Model Context Window Capacities](/notes/2025-09-03-ai-model-context-window-en)
1314
* [Alphabet Hits $186.66B in H1 2025](/notes/2025-09-03-alphabet-h1-2025-revenue-en)
1415
* [Building Trust Takes Time and Consistency](/notes/2025-09-03-building-trust-consistency-en)

0 commit comments

Comments
 (0)