Skip to content

Commit 8d0493f

Browse files
committed
MVC API: HTTP body provision
1 parent bcae584 commit 8d0493f

File tree

13 files changed

+349
-40
lines changed

13 files changed

+349
-40
lines changed

docs/docinfo-footer.html

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
<script src="https://cdnjs.cloudflare.com/ajax/libs/zepto/1.2.0/zepto.min.js"></script>
12
<script src="https://cdnjs.cloudflare.com/ajax/libs/tocbot/3.0.2/tocbot.min.js"></script>
23
<script>
34
var oldtoc = document.getElementById('toctitle').nextElementSibling;
@@ -11,7 +12,7 @@
1112
ignoreSelector: '.discrete',
1213
smoothScroll: false
1314
});
14-
var handleTocOnResize = function() {
15+
var handleTocOnResize = function () {
1516
var width = window.innerWidth
1617
|| document.documentElement.clientWidth
1718
|| document.body.clientWidth;
@@ -24,15 +25,16 @@
2425
throttleTimeout: 1000,
2526
smoothScroll: false
2627
});
27-
}
28-
else {
28+
} else {
2929
tocbot.refresh();
3030
}
3131
};
3232
window.addEventListener('resize', handleTocOnResize);
3333
handleTocOnResize();
34+
$(function () {
35+
window.location.hash = window.location.hash;
36+
});
3437
</script>
35-
<script src="https://cdnjs.cloudflare.com/ajax/libs/zepto/1.2.0/zepto.min.js"></script>
3638
<script>
3739
var primarySelector = '.primary';
3840
var secondarySelector = '.secondary';

docs/index.adoc

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ Jooby is a modular micro framework. At the core level:
8181

8282
Now, if you need more... Jooby don't leave you alone:
8383

84-
- MVC programming model (subset of https://github.com/jax-rs[JAX-RS])
84+
- MVC programming model (similar to JAX-RS)
8585
- Module ecosytem
8686

8787
=== Script API
@@ -168,9 +168,7 @@ fun main(args: Array<String>) {
168168
[#introduction-mvc-api]
169169
=== MVC API
170170

171-
On the other hand, the MVC API (a.k.a mvc routes) uses annotation and reflection to define/execute routes.
172-
Jooby implements a subset of https://github.com/jax-rs[JAX-RS] but not intended to be a fully
173-
https://github.com/jax-rs[JAX-RS] compliant implementation.
171+
On the other hand, the MVC API (a.k.a mvc routes) uses annotation to define routes and byte code generation to execute routes.
174172

175173
.MVC API:
176174
[source,java,role="primary"]
@@ -186,6 +184,8 @@ public class App extends Jooby {
186184
}
187185
}
188186
187+
import io.jooby.annotations.*;
188+
189189
public class MyController {
190190
191191
@GET
@@ -208,6 +208,8 @@ fun main(args: Array<String>) {
208208
run(::App, args)
209209
}
210210
211+
import io.jooby.annotations.*;
212+
211213
class MyController {
212214
213215
@GET

docs/mvc-api.adoc

Lines changed: 206 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,208 @@
11
== MVC API
22

3-
[IMPORTANT]
4-
====
5-
MVC API is not implemented yet.
6-
====
3+
MVC API is an alternative way to define routes in Jooby. It uses annotations and byte code generation
4+
to define and execute routes.
5+
6+
The package `io.jooby.annotations` contains the annotations available for MVC routes.
7+
8+
.MVC API:
9+
[source,java,role="primary"]
10+
----
11+
import io.jooby.annotations.*;
12+
13+
@Path("/mvc") // <1>
14+
public class MyController {
15+
16+
@GET // <2>
17+
public String sayHi() {
18+
return "Hello Mvc!";
19+
}
20+
}
21+
22+
App.java
23+
24+
{
25+
use(new MyController()); // <3>
26+
}
27+
----
28+
29+
.Kotlin
30+
[source,kotlin,role="secondary"]
31+
----
32+
33+
import io.jooby.annotations.*;
34+
35+
@Path("/mvc") // <1>
36+
class MyController {
37+
38+
@GET // <2>
39+
fun sayHi() : String {
40+
return "Hello Mvc!"
41+
}
42+
}
43+
44+
45+
fun main(args: Array<String>) {
46+
run(args) {
47+
use(MyController()) // <3>
48+
}
49+
}
50+
51+
----
52+
53+
<1> Set a path pattern. The `@Path` annotation is enable at class or method level
54+
<2> Add a HTTP method
55+
<3> Register/install the controller in the main application
56+
57+
[id=mvc-parameters]
58+
=== Parameters
59+
60+
HTTP parameter provision is available via `*Param` annotations.
61+
62+
[id=mvc-header]
63+
==== Header
64+
65+
Provision of headers is available via javadoc:annotations.HeaderParam[] annotation:
66+
67+
.Headers
68+
[source, java, role = "primary"]
69+
----
70+
public class MyController {
71+
72+
@GET
73+
public Object provisioning(@HeaderParam String token) { // <1>
74+
...
75+
}
76+
}
77+
----
78+
79+
.Kotlin
80+
[source, kotlin, role = "secondary"]
81+
----
82+
class MyController {
83+
84+
@GET
85+
fun provisioning(@HeaderParam token: String) : Any { // <1>
86+
...
87+
}
88+
}
89+
----
90+
91+
<1> Access to HTTP header named `token`
92+
93+
Compared to JAX-RS the parameter name on `@*Param` annotation is completely optional, but required for
94+
non valid Java names:
95+
96+
97+
.Non valid Java name
98+
[source, java, role = "primary"]
99+
----
100+
public class MyController {
101+
102+
@GET
103+
public Object provisioning(@HeaderParam("Last-Modified-Since") long lastModifiedSince) {
104+
...
105+
}
106+
}
107+
----
108+
109+
.Kotlin
110+
[source, kotlin, role = "secondary"]
111+
----
112+
class MyController {
113+
114+
@GET
115+
fun provisioning(@HeaderParam("Last-Modified-Since") lastModifiedSince: Long) : Any {
116+
...
117+
}
118+
}
119+
----
120+
121+
[id=mvc-path]
122+
==== Path
123+
124+
For path parameters the javadoc:annotations.PathParam[] annotation is required:
125+
126+
.PathParam
127+
[source, java, role = "primary"]
128+
----
129+
public class MyController {
130+
131+
@Path("/{id}")
132+
public Object provisioning(@PathParam String id) {
133+
...
134+
}
135+
}
136+
----
137+
138+
.Kotlin
139+
[source, kotlin, role = "secondary"]
140+
----
141+
class MyController {
142+
143+
@Path("/{id}")
144+
fun provisioning(@PathParam id: String) : Any {
145+
...
146+
}
147+
}
148+
----
149+
150+
[id=mvc-query]
151+
==== Query
152+
153+
For query parameters the javadoc:annotations.QueryParam[] annotation is required:
154+
155+
.QueryParam
156+
[source, java, role = "primary"]
157+
----
158+
public class MyController {
159+
160+
@Path("/")
161+
public Object provisioning(@QueryParam String q) {
162+
...
163+
}
164+
}
165+
----
166+
167+
.Kotlin
168+
[source, kotlin, role = "secondary"]
169+
----
170+
class MyController {
171+
172+
@Path("/")
173+
fun provisioning(@QueryParam q: String) : Any {
174+
...
175+
}
176+
}
177+
----
178+
179+
[id=mvc-form]
180+
==== Formdata/Multipart
181+
182+
For formdata/multipart parameters the javadoc:annotations.FormParam[] annotation is required:
183+
184+
.QueryParam
185+
[source, java, role = "primary"]
186+
----
187+
public class MyController {
188+
189+
@Path("/")
190+
@POST
191+
public Object provisioning(@FormParam String username) {
192+
...
193+
}
194+
}
195+
----
196+
197+
.Kotlin
198+
[source, kotlin, role = "secondary"]
199+
----
200+
class MyController {
201+
202+
@Path("/")
203+
@POST
204+
fun provisioning(@FormParam username: String) : Any {
205+
...
206+
}
207+
}
208+
----

jooby/src/main/java/io/jooby/Context.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -279,20 +279,26 @@ default long contentLength() {
279279
@Nonnull Body body();
280280

281281
default @Nonnull <T> T body(@Nonnull Reified<T> type) {
282-
return body(type.getType());
282+
MediaType contentType = MediaType.valueOf(header("Content-Type")
283+
.value("text/plain"));
284+
return body(type, contentType);
283285
}
284286

285287
default @Nonnull <T> T body(@Nonnull Reified<T> type, @Nonnull MediaType contentType) {
286-
return body(type.getType(), contentType);
288+
try {
289+
return parser(contentType).parse(this, type.getType());
290+
} catch (Exception x) {
291+
throw Throwing.sneakyThrow(x);
292+
}
287293
}
288294

289-
default @Nonnull <T> T body(@Nonnull Type type) {
295+
default @Nonnull <T> T body(@Nonnull Class type) {
290296
MediaType contentType = MediaType.valueOf(header("Content-Type")
291297
.value("text/plain"));
292298
return body(type, contentType);
293299
}
294300

295-
default @Nonnull <T> T body(@Nonnull Type type, @Nonnull MediaType contentType) {
301+
default @Nonnull <T> T body(@Nonnull Class type, @Nonnull MediaType contentType) {
296302
try {
297303
return parser(contentType).parse(this, type);
298304
} catch (Exception x) {

jooby/src/main/java/io/jooby/MediaType.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ private MediaType(@Nonnull String value, Charset charset) {
104104
}
105105

106106
@Override public int hashCode() {
107-
return raw.hashCode();
107+
return value.hashCode();
108108
}
109109

110110
public @Nullable String param(@Nonnull String name) {

jooby/src/main/java/io/jooby/Parser.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ public interface Parser {
2525
throw new Err(StatusCode.UNSUPPORTED_MEDIA_TYPE);
2626
}
2727
};
28+
Parser RAW = new Parser() {
29+
@Override public <T> T parse(Context ctx, Type type) {
30+
return ctx.body().to(type);
31+
}
32+
};
2833

2934
@Nonnull <T> T parse(@Nonnull Context ctx, @Nonnull Type type) throws Exception;
3035
}

0 commit comments

Comments
 (0)