Skip to content
This repository was archived by the owner on Mar 3, 2026. It is now read-only.

Commit dc068d0

Browse files
committed
Implement Response.Body a DSL for MVC routes
1 parent edcfdc2 commit dc068d0

12 files changed

Lines changed: 509 additions & 87 deletions

File tree

jooby-core/src/main/java/jooby/BodyConverter.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
import javax.annotation.Nonnull;
66

7+
import jooby.Response.Body;
8+
79
import com.google.common.annotations.Beta;
810
import com.google.inject.TypeLiteral;
911

@@ -73,10 +75,10 @@ public interface BodyConverter {
7375
* in order to close resources.
7476
* </p>
7577
*
76-
* @param message The body message.
78+
* @param body The body message.
7779
* @param writer The writing context.
7880
* @throws Exception If the body can't be write it.
7981
*/
80-
void write(@Nonnull Object message, @Nonnull BodyWriter writer) throws Exception;
82+
void write(@Nonnull Body body, @Nonnull BodyWriter writer) throws Exception;
8183

8284
}

jooby-core/src/main/java/jooby/Response.java

Lines changed: 196 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,17 @@
88
import java.io.Reader;
99
import java.nio.charset.Charset;
1010
import java.util.Date;
11+
import java.util.LinkedHashMap;
1112
import java.util.Map;
1213
import java.util.Optional;
1314

1415
import javax.annotation.Nonnull;
1516

1617
import jooby.fn.ExSupplier;
18+
import jooby.internal.SetHeaderImpl;
1719

1820
import com.google.common.annotations.Beta;
21+
import com.google.common.collect.ImmutableMap;
1922

2023
/**
2124
* Give you access to the actual HTTP response. You can read/write headers and write HTTP body.
@@ -24,7 +27,137 @@
2427
* @since 0.1.0
2528
*/
2629
@Beta
27-
public interface Response {
30+
public interface Response extends SetHeader {
31+
32+
public class Body implements SetHeader {
33+
34+
private Map<String, String> headers = new LinkedHashMap<>();
35+
36+
private SetHeaderImpl setHeader = new SetHeaderImpl((name, value) -> headers.put(name, value));
37+
38+
private Object content;
39+
40+
private HttpStatus status;
41+
42+
private MediaType type;
43+
44+
public Body(final Object content) {
45+
content(content);
46+
}
47+
48+
public Body() {
49+
}
50+
51+
public static Body ok() {
52+
return new Body().status(HttpStatus.OK);
53+
}
54+
55+
public static Body ok(final Object message) {
56+
return new Body(message).status(HttpStatus.OK);
57+
}
58+
59+
public static Body accepted() {
60+
return new Body().status(HttpStatus.ACCEPTED);
61+
}
62+
63+
public static Body accepted(final Object message) {
64+
return new Body(message).status(HttpStatus.ACCEPTED);
65+
}
66+
67+
public static Body noContent() {
68+
return new Body().status(HttpStatus.NO_CONTENT);
69+
}
70+
71+
public Body status(final HttpStatus status) {
72+
this.status = status;
73+
return this;
74+
}
75+
76+
public Body type(final MediaType type) {
77+
this.type = type;
78+
return this;
79+
}
80+
81+
public Body content(final Object content) {
82+
this.content = requireNonNull(content, "Content is required.");
83+
return this;
84+
}
85+
86+
public Map<String, String> headers() {
87+
return ImmutableMap.copyOf(headers);
88+
}
89+
90+
public Optional<HttpStatus> status() {
91+
return Optional.ofNullable(status);
92+
}
93+
94+
public Optional<MediaType> type() {
95+
return Optional.ofNullable(type);
96+
}
97+
98+
public Optional<Object> content() {
99+
return Optional.ofNullable(content);
100+
}
101+
102+
@Override
103+
public Body header(final String name, final char value) {
104+
setHeader.header(name, value);
105+
return this;
106+
}
107+
108+
@Override
109+
public Body header(final String name, final byte value) {
110+
setHeader.header(name, value);
111+
return this;
112+
}
113+
114+
@Override
115+
public Body header(final String name, final short value) {
116+
setHeader.header(name, value);
117+
return this;
118+
}
119+
120+
@Override
121+
public Body header(final String name, final int value) {
122+
setHeader.header(name, value);
123+
return this;
124+
}
125+
126+
@Override
127+
public Body header(final String name, final long value) {
128+
setHeader.header(name, value);
129+
return this;
130+
}
131+
132+
@Override
133+
public Body header(final String name, final float value) {
134+
setHeader.header(name, value);
135+
return this;
136+
}
137+
138+
@Override
139+
public Body header(final String name, final double value) {
140+
setHeader.header(name, value);
141+
return this;
142+
}
143+
144+
@Override
145+
public Body header(final String name, final CharSequence value) {
146+
setHeader.header(name, value);
147+
return this;
148+
}
149+
150+
@Override
151+
public Body header(final String name, final Date value) {
152+
setHeader.header(name, value);
153+
return this;
154+
}
155+
156+
@Override
157+
public String toString() {
158+
return content == null ? "" : content.toString();
159+
}
160+
}
28161

29162
public class Forwarding implements Response {
30163

@@ -115,7 +248,7 @@ public Response header(final String name, final short value) {
115248
}
116249

117250
@Override
118-
public Response header(final String name, final String value) {
251+
public Response header(final String name, final CharSequence value) {
119252
return response.header(name, value);
120253
}
121254

@@ -169,11 +302,21 @@ public void send(final Object body) throws Exception {
169302
response.send(body);
170303
}
171304

305+
@Override
306+
public void send(final Body body) throws Exception {
307+
response.send(body);
308+
}
309+
172310
@Override
173311
public void send(final Object body, final BodyConverter converter) throws Exception {
174312
response.send(body, converter);
175313
}
176314

315+
@Override
316+
public void send(final Body body, final BodyConverter converter) throws Exception {
317+
response.send(body, converter);
318+
}
319+
177320
@Override
178321
public Formatter format() {
179322
return response.format();
@@ -300,22 +443,31 @@ default Response cookie(final String name, final String value) {
300443
@Nonnull
301444
Variant header(@Nonnull String name);
302445

446+
@Override
303447
Response header(@Nonnull String name, char value);
304448

449+
@Override
305450
Response header(@Nonnull String name, byte value);
306451

452+
@Override
307453
Response header(@Nonnull String name, short value);
308454

455+
@Override
309456
Response header(@Nonnull String name, int value);
310457

458+
@Override
311459
Response header(@Nonnull String name, long value);
312460

461+
@Override
313462
Response header(@Nonnull String name, float value);
314463

464+
@Override
315465
Response header(@Nonnull String name, double value);
316466

317-
Response header(@Nonnull String name, String value);
467+
@Override
468+
Response header(@Nonnull String name, CharSequence value);
318469

470+
@Override
319471
Response header(@Nonnull String name, Date value);
320472

321473
/**
@@ -361,7 +513,22 @@ default Response type(@Nonnull final String type) {
361513
* @throws Exception If the response write fails.
362514
* @see BodyConverter
363515
*/
364-
void send(@Nonnull Object body) throws Exception;
516+
default void send(@Nonnull final Object body) throws Exception {
517+
requireNonNull(body, "A response message is required.");
518+
if (body instanceof Body) {
519+
send((Body) body);
520+
} else {
521+
Body b = new Body(body);
522+
HttpStatus status = status();
523+
if (status != null) {
524+
b.status(status);
525+
}
526+
type().ifPresent(t -> b.type(t));
527+
send(b);
528+
}
529+
}
530+
531+
void send(@Nonnull Body body) throws Exception;
365532

366533
/**
367534
* Responsible of writing the given body into the HTTP response.
@@ -371,9 +538,26 @@ default Response type(@Nonnull final String type) {
371538
* @throws Exception If the response write fails.
372539
* @see BodyConverter
373540
*/
374-
void send(@Nonnull Object body, @Nonnull BodyConverter converter) throws Exception;
541+
default void send(@Nonnull final Object body, @Nonnull final BodyConverter converter)
542+
throws Exception {
543+
requireNonNull(body, "A response message is required.");
544+
if (body instanceof Body) {
545+
send((Body) body, converter);
546+
} else {
547+
Body b = new Body(body);
548+
HttpStatus status = status();
549+
if (status != null) {
550+
b.status(status);
551+
}
552+
type().ifPresent(t -> b.type(t));
553+
send(b, converter);
554+
}
555+
}
375556

376-
@Nonnull Formatter format();
557+
void send(@Nonnull Body body, @Nonnull BodyConverter converter) throws Exception;
558+
559+
@Nonnull
560+
Formatter format();
377561

378562
default void redirect(final String location) throws Exception {
379563
redirect(HttpStatus.FOUND, location);
@@ -384,17 +568,20 @@ default void redirect(final String location) throws Exception {
384568
/**
385569
* @return A HTTP status.
386570
*/
387-
@Nonnull HttpStatus status();
571+
@Nonnull
572+
HttpStatus status();
388573

389574
/**
390575
* Set the HTTP status.
391576
*
392577
* @param status A HTTP status.
393578
* @return This response.
394579
*/
395-
@Nonnull Response status(@Nonnull HttpStatus status);
580+
@Nonnull
581+
Response status(@Nonnull HttpStatus status);
396582

397-
@Nonnull default Response status(final int status) {
583+
@Nonnull
584+
default Response status(final int status) {
398585
return status(HttpStatus.valueOf(status));
399586
}
400587

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package jooby;
2+
3+
import java.util.Date;
4+
5+
import javax.annotation.Nonnull;
6+
7+
public interface SetHeader {
8+
9+
SetHeader header(@Nonnull String name, char value);
10+
11+
SetHeader header(@Nonnull String name, byte value);
12+
13+
SetHeader header(@Nonnull String name, short value);
14+
15+
SetHeader header(@Nonnull String name, int value);
16+
17+
SetHeader header(@Nonnull String name, long value);
18+
19+
SetHeader header(@Nonnull String name, float value);
20+
21+
SetHeader header(@Nonnull String name, double value);
22+
23+
SetHeader header(@Nonnull String name, CharSequence value);
24+
25+
SetHeader header(@Nonnull String name, Date value);
26+
27+
}

jooby-core/src/main/java/jooby/TemplateProcessor.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import java.util.List;
44

5+
import jooby.Response.Body;
6+
57
import com.google.common.annotations.Beta;
68
import com.google.common.collect.ImmutableList;
79
import com.google.inject.TypeLiteral;
@@ -46,8 +48,8 @@ public final <T> T read(final TypeLiteral<T> type, final BodyReader reader) thro
4648
}
4749

4850
@Override
49-
public void write(final Object message, final BodyWriter writer) throws Exception {
50-
final Viewable viewable = (Viewable) message;
51+
public void write(final Body body, final BodyWriter writer) throws Exception {
52+
final Viewable viewable = (Viewable) body.content().get();
5153
render(viewable, writer);
5254
}
5355

jooby-core/src/main/java/jooby/internal/AssetBodyConverter.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import jooby.BodyReader;
1313
import jooby.BodyWriter;
1414
import jooby.MediaType;
15+
import jooby.Response.Body;
1516

1617
import com.google.common.collect.ImmutableList;
1718
import com.google.common.io.ByteStreams;
@@ -47,8 +48,8 @@ public <T> T read(final TypeLiteral<T> type, final BodyReader reader) throws Exc
4748
}
4849

4950
@Override
50-
public void write(final Object message, final BodyWriter writer) throws Exception {
51-
Asset asset = (Asset) message;
51+
public void write(final Body body, final BodyWriter writer) throws Exception {
52+
Asset asset = (Asset) body.content().get();
5253
boolean text = mediaType.equals(MediaType.javascript) || mediaType.equals(MediaType.css)
5354
|| mediaType.equals(MediaType.json) || MediaType.text.matches(mediaType);
5455
if (text) {

0 commit comments

Comments
 (0)