{title:'Form Posts', updated:'9.0.0'}

HTTP form posts can be handled two ways:

  1. By parsing the entire HTTP body into a POJO using the registered {@link oaj.urlencoding.UrlEncodingParser}
  2. By access the form post entries as HTTP parts.

The following example shows the first approach of handling an application/x-www-form-urlencoded request of the form "aString=foo&aNumber=123&aDate=2001-07-04T15:30:45Z" and loading it into a simple bean.

| // A simple bean. | public static class FormInputBean { | public String aString; | public int aNumber; | @Swap(TemporalCalendarSwap.IsoLocalDateTime.class) | public Calendar aDate; | }

| @Rest(...) | public class MyRestResource extends BasicRestServlet { | | // Our form input endpoint. | @RestPost("/") | public Object doPost(@Content FormInputBean input) { | // Just mirror back the request | return input; | } | }

The next example shows handling it as individual parts:

| // Our form input endpoint. | @RestPost("/") | public Object doPost( | @FormData("aString") String aString, | @FormData("aNumber") int aNumber, | @FormData("aDate") Calendar aDate) { | ... | }

The advantage to the form input bean is that it can handle any of the parsable types (e.g. JSON, XML...) in addition to URL-Encoding while the latter approach only supports URL-Encoding.

If you're using form input beans, DO NOT use the @FormData attribute or {@link jakarta.servlet.http.HttpServletRequest#getParameter(String)} method since this will cause the underlying JEE servlet to parse the HTTP body as a form post.
Your input bean will end up being null since there won't be any content left after the servlet has parsed the body of the request.
This applies to WHENEVER you use @Content or {@link oajr.RestRequest#getContent()}
If you want to be able to consume url-encoded form post bodies as POJOs in Spring Boot, you'll need to add the following Spring Bean to your configuration to prevent Spring Boot from automatically consuming the body itself:

| @SpringBootApplication | @Controller | public class SpringBootAppConfig { | | @Bean | public FilterRegistrationBean<HiddenHttpMethodFilter> registration(HiddenHttpMethodFilter filter) { | FilterRegistrationBean<HiddenHttpMethodFilter> reg = new FilterRegistrationBean<>(filter); | reg.setEnabled(false); | return reg; | } | }

Multi-part Form Posts

The Juneau framework does not natively support multipart form posts. However, it can be done in conjunction with the Apache Commons File Upload library or through the Servlet 3.0 API directly.

The following is an example that uses the File Upload library to allow files to be uploaded as multipart form posts.

Example:

| @Rest( | path="/tempDir" | ) | public class TempDirResource extends DirectoryResource { | | @RestPost(path="/upload", matchers=TempDirResource.MultipartFormDataMatcher.class) | public Redirect uploadFile(RestRequest req) throws Exception { | ServletFileUpload upload = new ServletFileUpload(); | FileItemIterator iter = upload.getItemIterator(req); | while (iter.hasNext()) { | FileItemStream item = iter.next(); | if (item.getFieldName().equals("contents")) { | File file = new File(getRootDir(), item.getName()); | IOPipe.create(item.openStream(), new FileOutputStream(file)).closeOut().run(); | } | } | return new Redirect(); // Redirect to the servlet root. | } | | /** Causes a 404 if POST isn't multipart/form-data */ | public static class MultipartFormDataMatcher extends RestMatcher { | | @Override /* RestMatcher */ | public boolean matches(RestRequest req) { | String contentType = req.getContentType(); | return contentType != null && contentType.startsWith("multipart/form-data"); | } | }

The following shows using the HttpServletRequest.getParts() method to retrieve multi-part form posts when using Jetty. This example is pulled from the PetStore application.

| @RestPost | public SeeOtherRoot uploadFile(RestRequest req) throws Exception { | | // Required for Jetty. | MultipartConfigElement mce = new MultipartConfigElement((String)null); | req.setAttribute("org.eclipse.jetty.multipartConfig", mce); | | String id = UUID.randomUUID().toString(); | BufferedImage img = null; | for (Part part : req.getParts()) { | switch (part.getName()) { | case "id": | id = IOUtils.read(part.getInputStream()); | break; | case "file": | img = ImageIO.read(part.getInputStream()); | } | } | addPhoto(id, img); | return new SeeOtherRoot(); // Redirect to the servlet root. | }