Repository navigation
Expand file tree
/
Copy pathExpContentNegotiation.java
More file actions
174 lines (133 loc) · 7.03 KB
/
Copy pathExpContentNegotiation.java
File metadata and controls
174 lines (133 loc) · 7.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
import java.util.ArrayList;
import java.util.List;
// Simula la negociación de contenido de Spring MVC via Accept header.
// MessageConverter serializa el mismo objeto Java a JSON o XML según
// lo que el cliente declare en Accept: application/json / application/xml.
// ── Modelo ────────────────────────────────────────────────────────────────────
// @Entity / @JsonSerializable — POJO plano
class User {
private final Long id;
private final String nombre;
private final String email;
private final boolean activo;
User(Long id, String nombre, String email, boolean activo) {
this.id = id;
this.nombre = nombre;
this.email = email;
this.activo = activo;
}
public Long getId() { return id; }
public String getNombre() { return nombre; }
public String getEmail() { return email; }
public boolean isActivo() { return activo; }
}
// ── Interfaz MessageConverter ─────────────────────────────────────────────────
// Equivale a org.springframework.http.converter.HttpMessageConverter
interface MessageConverter {
// Retorna true si este converter puede escribir el mediaType solicitado
boolean canWrite(String mediaType);
// Serializa el objeto al formato correspondiente
String write(Object object);
// Media type que produce (para el header Content-Type de la respuesta)
String getMediaType();
}
// ── Converter JSON ────────────────────────────────────────────────────────────
// Equivale a MappingJackson2HttpMessageConverter
class JsonMessageConverter implements MessageConverter {
@Override
public boolean canWrite(String mediaType) {
return mediaType.contains("application/json") || mediaType.equals("*/*");
}
@Override
public String write(Object obj) {
if (obj instanceof User u) {
return "{\n"
+ " \"id\": " + u.getId() + ",\n"
+ " \"nombre\": \"" + u.getNombre() + "\",\n"
+ " \"email\": \"" + u.getEmail() + "\",\n"
+ " \"activo\": " + u.isActivo() + "\n"
+ "}";
}
return "{}";
}
@Override
public String getMediaType() { return "application/json"; }
}
// ── Converter XML ─────────────────────────────────────────────────────────────
// Equivale a Jaxb2RootElementHttpMessageConverter
class XmlMessageConverter implements MessageConverter {
@Override
public boolean canWrite(String mediaType) {
return mediaType.contains("application/xml") || mediaType.contains("text/xml");
}
@Override
public String write(Object obj) {
if (obj instanceof User u) {
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<user>\n"
+ " <id>" + u.getId() + "</id>\n"
+ " <nombre>" + u.getNombre() + "</nombre>\n"
+ " <email>" + u.getEmail() + "</email>\n"
+ " <activo>" + u.isActivo() + "</activo>\n"
+ "</user>";
}
return "<user/>";
}
@Override
public String getMediaType() { return "application/xml"; }
}
// ── ContentNegotiationHandler ─────────────────────────────────────────────────
// Equivale al DispatcherServlet seleccionando el HttpMessageConverter correcto
class ContentNegotiationHandler {
private final List<MessageConverter> converters = new ArrayList<>();
ContentNegotiationHandler() {
// Spring registra converters en orden; primero JSON, luego XML
converters.add(new JsonMessageConverter());
converters.add(new XmlMessageConverter());
}
// Simula el endpoint: GET /api/users/{id}
// @GetMapping(value="/api/users/{id}", produces={"application/json","application/xml"})
void handle(Long id, String acceptHeader) {
System.out.println(" Petición: GET /api/users/" + id + " Accept: " + acceptHeader);
// En Spring MVC este paso lo hace ContentNegotiationManager
MessageConverter selected = converters.stream()
.filter(c -> c.canWrite(acceptHeader))
.findFirst()
.orElse(null);
if (selected == null) {
System.out.println(" → 406 Not Acceptable — ningún converter soporta: " + acceptHeader);
return;
}
// Simula la "lógica de negocio" del controller
User user = new User(id, "Jorge Martínez", "jorge@example.com", true);
System.out.println(" Converter seleccionado: " + selected.getClass().getSimpleName());
System.out.println(" Content-Type respuesta: " + selected.getMediaType());
System.out.println(" Body:\n");
System.out.println(selected.write(user));
}
}
// ── Punto de entrada ──────────────────────────────────────────────────────────
public class ExpContentNegotiation {
public static void main(String[] args) {
ContentNegotiationHandler handler = new ContentNegotiationHandler();
System.out.println("=== Simulación Content Negotiation (Accept header) ===\n");
// ─── Mismo endpoint, dos clientes distintos ───────────────────────────
System.out.println("--- Cliente 1: espera JSON ---");
handler.handle(1L, "application/json");
System.out.println("\n" + "─".repeat(55) + "\n");
System.out.println("--- Cliente 2: espera XML ---");
handler.handle(1L, "application/xml");
System.out.println("\n" + "─".repeat(55) + "\n");
System.out.println("--- Cliente 3: acepta cualquier cosa (*/*) → JSON gana (primer converter) ---");
handler.handle(1L, "*/*");
System.out.println("\n" + "─".repeat(55) + "\n");
System.out.println("--- Cliente 4: Accept no soportado → 406 ---");
handler.handle(1L, "application/pdf");
System.out.println("\n" + "─".repeat(55));
System.out.println("\n[ Notas ]");
System.out.println(" @RequestMapping(produces=...) restringe qué converters aplican al endpoint.");
System.out.println(" Si el cliente envía Accept: application/xml pero el método solo produce JSON");
System.out.println(" → Spring devuelve 406 Not Acceptable antes de invocar el handler.");
System.out.println(" El orden de registro de converters en la lista importa: */* siempre cae en el primero.");
}
}