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

Commit 38710a1

Browse files
committed
Request.form(Class) fix jooby-project#496
* req.body should not be use it for form post fix jooby-project#497 * POST with empty `Content-Type` generates an error fix jooby-project#444
1 parent 43ae304 commit 38710a1

File tree

8 files changed

+118
-52
lines changed

8 files changed

+118
-52
lines changed

coverage-report/src/test/java/org/jooby/BeanParserFeature.java

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

55
import java.util.Optional;
66

7-
import org.jooby.mvc.Body;
87
import org.jooby.mvc.Path;
98
import org.jooby.test.ServerFeature;
109
import org.junit.Test;
@@ -72,7 +71,7 @@ public String getibean(final org.jooby.Request req, final IBean bean) throws Exc
7271

7372
@org.jooby.mvc.POST
7473
@Path("/ibean")
75-
public String postibean(final org.jooby.Request req, final @Body IBean bean) throws Exception {
74+
public String postibean(final org.jooby.Request req, final IBean bean) throws Exception {
7675
assertEquals(req.param("name").value(), bean.name());
7776
assertEquals(req.param("valid").booleanValue(), bean.isValid());
7877
assertEquals(req.param("age").intValue(), bean.getAge());
@@ -131,7 +130,9 @@ public String invalidbean(final DefConstBean bean) throws Exception {
131130
});
132131

133132
post("/ibean", req -> {
134-
IBean bean = req.body().to(IBean.class);
133+
IBean bean = req.form(IBean.class);
134+
System.out.println(bean.name());
135+
135136
assertEquals(req.param("name").value(), bean.name());
136137
assertEquals(req.param("valid").booleanValue(), bean.isValid());
137138
assertEquals(req.param("age").intValue(), bean.getAge());
@@ -146,12 +147,12 @@ public String invalidbean(final DefConstBean bean) throws Exception {
146147
});
147148

148149
get("/defaultConstructor", req -> {
149-
req.params().to(DefConstBean.class);
150+
req.form(DefConstBean.class);
150151
return "OK";
151152
});
152153

153154
post("/beanwithargs", req -> {
154-
BeanWithArgs bean = req.body().to(BeanWithArgs.class);
155+
BeanWithArgs bean = req.form(BeanWithArgs.class);
155156
assertEquals(req.param("name").value(), bean.name);
156157
assertEquals(req.param("age").intValue(), (int) bean.age.get());
157158
return "OK";
@@ -165,7 +166,7 @@ public String invalidbean(final DefConstBean bean) throws Exception {
165166
});
166167

167168
post("/beannoarg", req -> {
168-
BeanNoArg bean = req.body().to(BeanNoArg.class);
169+
BeanNoArg bean = req.form(BeanNoArg.class);
169170
assertEquals(req.param("name").value(), bean.getName());
170171
assertEquals(req.param("age").intValue(), (int) bean.getAge().get());
171172
return "OK";
@@ -258,28 +259,6 @@ public void postibean() throws Exception {
258259

259260
}
260261

261-
@Test
262-
public void bean415() throws Exception {
263-
264-
request()
265-
.post("/ibean")
266-
.header("Content-Type", "application/xml")
267-
.multipart()
268-
.add("name", "edgar")
269-
.add("age", 34)
270-
.add("valid", false)
271-
.expect(415);
272-
273-
request()
274-
.post("/r/ibean")
275-
.header("Content-Type", "application/xml")
276-
.multipart()
277-
.add("name", "edgar")
278-
.add("age", 34)
279-
.add("valid", false)
280-
.expect(415);
281-
}
282-
283262
@Test
284263
public void postbeanwithargs() throws Exception {
285264
request()

coverage-report/src/test/java/org/jooby/issues/Issue433.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public static class Bean {
1616
});
1717

1818
post("/433", req -> {
19-
Bean bean = req.body(Bean.class);
19+
Bean bean = req.form(Bean.class);
2020
return bean.q;
2121
});
2222
}

coverage-report/src/test/java/org/jooby/issues/Issue444.java

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,13 @@ public class Issue444 extends ServerFeature {
2020
});
2121

2222
post("/444", req -> {
23-
ByteBuffer buffer = req.body().to(ByteBuffer.class);
23+
ByteBuffer buffer = req.body(ByteBuffer.class);
2424
return buffer.remaining();
2525
});
26+
27+
post("/444/raw", req -> {
28+
return req.body(String.class);
29+
});
2630
}
2731

2832
@Test
@@ -32,4 +36,60 @@ public void shouldAcceptPostWithoutContentType() throws URISyntaxException, Exce
3236
.body("abc", null)
3337
.expect("3");
3438
}
39+
40+
@Test
41+
public void shouldFavorCustomParserOnFormUrlEncoded() throws URISyntaxException, Exception {
42+
request()
43+
.post("/444")
44+
.body("abc", "application/x-www-form-urlencoded")
45+
.expect("3");
46+
}
47+
48+
@Test
49+
public void shouldFavorCustomParserOnMultipart() throws URISyntaxException, Exception {
50+
request()
51+
.post("/444")
52+
.body("abc", "multipart/form-data")
53+
.expect("3");
54+
}
55+
56+
@Test
57+
public void shouldGetRawBody() throws URISyntaxException, Exception {
58+
request()
59+
.post("/444/raw")
60+
.form()
61+
.add("foo", "bar")
62+
.add("bar", "foo")
63+
.expect("foo=bar&bar=foo");
64+
65+
request()
66+
.post("/444/raw")
67+
.body("--ylYSWaNWL2lXy3vBYw458nuB9UDehD5o6iHZuLK\n" +
68+
"Content-Disposition: form-data; name=\"foo\"\n" +
69+
"Content-Type: text/plain\n" +
70+
"Content-Transfer-Encoding: 8bit\n" +
71+
"\n" +
72+
"bar\n" +
73+
"--ylYSWaNWL2lXy3vBYw458nuB9UDehD5o6iHZuLK\n" +
74+
"Content-Disposition: form-data; name=\"bar\"; filename=\"foo.txt\"\n" +
75+
"Content-Type: text/plain\n" +
76+
"Content-Transfer-Encoding: binary\n" +
77+
"\n" +
78+
"foo\n" +
79+
"--ylYSWaNWL2lXy3vBYw458nuB9UDehD5o6iHZuLK--", "multipart/form-data")
80+
.expect("--ylYSWaNWL2lXy3vBYw458nuB9UDehD5o6iHZuLK\n" +
81+
"Content-Disposition: form-data; name=\"foo\"\n" +
82+
"Content-Type: text/plain\n" +
83+
"Content-Transfer-Encoding: 8bit\n" +
84+
"\n" +
85+
"bar\n" +
86+
"--ylYSWaNWL2lXy3vBYw458nuB9UDehD5o6iHZuLK\n" +
87+
"Content-Disposition: form-data; name=\"bar\"; filename=\"foo.txt\"\n" +
88+
"Content-Type: text/plain\n" +
89+
"Content-Transfer-Encoding: binary\n" +
90+
"\n" +
91+
"foo\n" +
92+
"--ylYSWaNWL2lXy3vBYw458nuB9UDehD5o6iHZuLK--");
93+
}
94+
3595
}

coverage-report/src/test/java/org/jooby/issues/Issue453.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,14 @@ public static class Form {
1818
return req.header("text", "html").value();
1919
});
2020

21-
get("/453/escape-form", req -> {
21+
get("/453/escape-params", req -> {
2222
return req.params(Form.class, req.param("xss").value("html")).text;
2323
});
2424

25+
get("/453/escape-form", req -> {
26+
return req.form(Form.class, req.param("xss").value("html")).text;
27+
});
28+
2529
get("/453/to-escape-form", req -> {
2630
return req.params(req.param("xss").value("html")).to(Form.class).text;
2731
});
@@ -49,6 +53,10 @@ public void escapeForm() throws Exception {
4953
.get("/453/escape-form?text=%3Ch1%3EX%3C/h1%3E")
5054
.expect("<h1>X</h1>");
5155

56+
request()
57+
.get("/453/escape-params?text=%3Ch1%3EX%3C/h1%3E")
58+
.expect("<h1>X</h1>");
59+
5260
request()
5361
.get("/453/escape-form?text=%3Ch1%3EX%3C/h1%3E&xss=none")
5462
.expect("<h1>X</h1>");

jooby-undertow/src/main/java/org/jooby/internal/undertow/UndertowRequest.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@
5353
import io.undertow.util.AttachmentKey;
5454
import io.undertow.util.HeaderValues;
5555
import io.undertow.util.HttpString;
56+
import javaslang.Lazy;
57+
import javaslang.control.Try;
5658

5759
public class UndertowRequest implements NativeRequest {
5860

@@ -63,7 +65,7 @@ public class UndertowRequest implements NativeRequest {
6365

6466
private Config config;
6567

66-
private final FormData form;
68+
private final Lazy<FormData> form;
6769

6870
private final String path;
6971

@@ -74,8 +76,8 @@ public UndertowRequest(final HttpServerExchange exchange, final Config conf)
7476
this.exchange = exchange;
7577
this.blocking = Suppliers.memoize(() -> this.exchange.startBlocking());
7678
this.config = conf;
77-
this.form = parseForm(exchange, conf.getString("application.tmpdir"),
78-
conf.getString("application.charset"));
79+
this.form = Lazy.of(() -> Try.of(() -> parseForm(exchange, conf.getString("application.tmpdir"),
80+
conf.getString("application.charset"))).get());
7981
this.path = URLDecoder.decode(exchange.getRequestPath(), "UTF-8");
8082
}
8183

@@ -93,9 +95,10 @@ public String path() {
9395
public List<String> paramNames() {
9496
ImmutableList.Builder<String> builder = ImmutableList.<String> builder();
9597
builder.addAll(exchange.getQueryParameters().keySet());
96-
form.forEach(v -> {
98+
FormData formdata = this.form.get();
99+
formdata.forEach(v -> {
97100
// excludes upload from param names.
98-
if (!form.getFirst(v).isFile()) {
101+
if (!formdata.getFirst(v).isFile()) {
99102
builder.add(v);
100103
}
101104
});
@@ -111,7 +114,7 @@ public List<String> params(final String name) {
111114
query.stream().forEach(builder::add);
112115
}
113116
// form params
114-
Optional.ofNullable(form.get(name)).ifPresent(values -> {
117+
Optional.ofNullable(form.get().get(name)).ifPresent(values -> {
115118
values.stream().forEach(value -> {
116119
if (!value.isFile()) {
117120
builder.add(value.getValue());
@@ -150,7 +153,7 @@ public List<Cookie> cookies() {
150153
@Override
151154
public List<NativeUpload> files(final String name) {
152155
Builder<NativeUpload> builder = ImmutableList.builder();
153-
Deque<FormValue> values = form.get(name);
156+
Deque<FormValue> values = form.get().get(name);
154157
if (values != null) {
155158
values.forEach(value -> {
156159
if (value.isFile()) {

jooby-undertow/src/test/java/org/jooby/undertow/UndertowRequestTest.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,7 @@
2020
public class UndertowRequestTest {
2121

2222
private Block form = unit -> {
23-
Config conf = unit.get(Config.class);
24-
expect(conf.getString("application.tmpdir")).andReturn("target");
25-
expect(conf.getString("application.charset")).andReturn("UTF-8");
26-
27-
HeaderMap headers = unit.mock(HeaderMap.class);
28-
expect(headers.getFirst("Content-Type")).andReturn(null);
29-
3023
HttpServerExchange exchange = unit.get(HttpServerExchange.class);
31-
expect(exchange.getRequestHeaders()).andReturn(headers);
3224
expect(exchange.getRequestPath()).andReturn("/");
3325
};
3426

jooby/src/main/java/org/jooby/Request.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,17 @@ default <T> T params(final Class<T> type) {
648648
return params().to(type);
649649
}
650650

651+
/**
652+
* Short version of <code>params().to(type)</code>.
653+
*
654+
* @param type Object type.
655+
* @param <T> Value type.
656+
* @return Instance of object.
657+
*/
658+
default <T> T form(final Class<T> type) {
659+
return params().to(type);
660+
}
661+
651662
/**
652663
* Short version of <code>params(xss).to(type)</code>.
653664
*
@@ -660,6 +671,18 @@ default <T> T params(final Class<T> type, final String... xss) {
660671
return params(xss).to(type);
661672
}
662673

674+
/**
675+
* Short version of <code>params(xss).to(type)</code>.
676+
*
677+
* @param type Object type.
678+
* @param xss Xss filter to apply.
679+
* @param <T> Value type.
680+
* @return Instance of object.
681+
*/
682+
default <T> T form(final Class<T> type, final String... xss) {
683+
return params(xss).to(type);
684+
}
685+
663686
/**
664687
* Get a HTTP request parameter under the given name. A HTTP parameter can be provided in any of
665688
* these forms:
@@ -777,7 +800,8 @@ default List<Upload> files(final String name) {
777800
List<Cookie> cookies();
778801

779802
/**
780-
* HTTP body.
803+
* HTTP body. Please don't use this method for form submits. This method is used for getting
804+
* <code>raw</code> data or a data like json, xml, etc...
781805
*
782806
* @return The HTTP body.
783807
* @throws Exception If body can't be converted or there is no HTTP body.
@@ -787,6 +811,9 @@ default List<Upload> files(final String name) {
787811
/**
788812
* Short version of <code>body().to(type)</code>.
789813
*
814+
* HTTP body. Please don't use this method for form submits. This method is used for getting
815+
* <code>raw</code> or a parsed data like json, xml, etc...
816+
*
790817
* @param type Object type.
791818
* @param <T> Value type.
792819
* @return Instance of object.

jooby/src/main/java/org/jooby/internal/RequestImpl.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -278,9 +278,6 @@ public Mutant body() throws Exception {
278278
long length = length();
279279
if (length > 0) {
280280
MediaType type = type();
281-
if (!type.isAny() && (MediaType.form.matches(type) || MediaType.multipart.matches(type))) {
282-
return params();
283-
}
284281
Config conf = require(Config.class);
285282

286283
File fbody = new File(conf.getString("application.tmpdir"),
@@ -289,9 +286,9 @@ public Mutant body() throws Exception {
289286
int bufferSize = conf.getBytes("server.http.RequestBufferSize").intValue();
290287
Parser.BodyReference body = new BodyReferenceImpl(length, charset(), fbody, req.in(),
291288
bufferSize);
292-
return new MutantImpl(require(ParserExecutor.class), type(), body);
289+
return new MutantImpl(require(ParserExecutor.class), type, body);
293290
}
294-
return new MutantImpl(require(ParserExecutor.class), type(), new EmptyBodyReference());
291+
return new MutantImpl(require(ParserExecutor.class), type, new EmptyBodyReference());
295292
}
296293

297294
@Override

0 commit comments

Comments
 (0)