You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by bu...@apache.org on 2022/01/09 21:20:56 UTC

svn commit: r1077937 - in /websites/production/tapestry/content: cache/main.pageCache rest-support-page-is-work-in-progress.html

Author: buildbot
Date: Sun Jan  9 21:20:56 2022
New Revision: 1077937

Log:
Production update by buildbot for tapestry

Modified:
    websites/production/tapestry/content/cache/main.pageCache
    websites/production/tapestry/content/rest-support-page-is-work-in-progress.html

Modified: websites/production/tapestry/content/cache/main.pageCache
==============================================================================
Binary files - no diff available.

Modified: websites/production/tapestry/content/rest-support-page-is-work-in-progress.html
==============================================================================
--- websites/production/tapestry/content/rest-support-page-is-work-in-progress.html (original)
+++ websites/production/tapestry/content/rest-support-page-is-work-in-progress.html Sun Jan  9 21:20:56 2022
@@ -106,7 +106,90 @@ Object save(@RequestBody User user) {
 Object save(Long id, @RequestBody User user) {
 	(...)
 }</code></pre>
-</div></div><p>The following types are supported out-of-the-box:</p><ul><li><code>String</code></li><li><code>Reader</code></li><li><code>InputStream</code></li><li>Primitive types and their wrapper types</li></ul><p>The actual conversion logic is implemented in the <code>HttpRequestBodyConverter</code> service, which is defined is an ordered configuration of <code>HttpRequestBodyConverter</code> instances. The service calls all contributed instances until one of them returns a non-null value. If none of them returns a non-null value, it falls back to trying to find a coercion, direct or indirect, from <code>HttpServletRequest</code> to the desired type.&#160;</p><p></p></div>
+</div></div><p>The following types are supported out-of-the-box:</p><ul><li><code>String</code></li><li><code>Reader</code></li><li><code>InputStream</code></li><li><code>JSONObject</code></li><li><code>JSONArray</code></li><li>Primitive types and their wrapper types</li></ul><p>The actual conversion logic is implemented in the <code>HttpRequestBodyConverter</code> service, which is defined is an ordered configuration of <code>HttpRequestBodyConverter</code> instances. The service calls all contributed instances until one of them returns a non-null value. If none of them returns a non-null value, it falls back to trying to find a coercion, direct or indirect, from <code>HttpServletRequest</code> to the desired type.&#160;</p><p>Here's one example of implementing an new <code>HttpRequestBodyConverter</code> then the code added to <code>AppModule</code> or any other Tapestry-IoC module class to have it used by <code>@RequestBody</code>:</p><div class="code panel pdl" style="border-wid
 th: 1px;"><div class="codeContent panelContent pdl">
+<pre><code class="language-java">/**
+ * Converts the body of HTTP requests to {@link User} instances. It delegates this task
+ * to {@link UserService#toObject(String)}.
+ */
+public class UserHttpRequestBodyConverter implements HttpRequestBodyConverter {
+    
+    private final UserService userService;
+
+    public UserHttpRequestBodyConverter(UserService userService) {
+        super();
+        this.userService = userService;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public &lt;T&gt; T convert(HttpServletRequest request, Class&lt;T&gt; type) {
+        T value = null;
+        // Check whether this converter handles the given type
+        if (User.class.equals(type)) {
+			// Actual conversion logic
+            try {
+                value = (T) userService.toObject(IOUtils.toString(request.getReader()));
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+ &#160; &#160; &#160; &#160;}
+        return value;
+    }
+
+}</code></pre>
+</div></div><div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>UserHttpRequestBodyConverter contribution</b></div><div class="codeContent panelContent pdl">
+<pre><code class="language-java">    public static void contributeHttpRequestBodyConverter(
+            OrderedConfiguration&lt;HttpRequestBodyConverter&gt; configuration) {
+        
+        configuration.addInstance("User", UserHttpRequestBodyConverter.class); // automatic instantiation and dependency injection
+		// or configuration.add("User", new UserHttpRequestBodyConverter(...));
+    }</code></pre>
+</div></div><h2 id="RESTSupport(pageisworkinprogress)-AnsweringRESTrequests">Answering REST requests</h2><p>Just like any other Tapestry event handler method, the returned value defines what gets to be sent to the user agent making the request. This logic is written in <code>ComponentEventResultProcessor</code> implementations, usually but not necessarily one per return type/class, which are contributed to the <code>ComponentEventResultProcessor</code> service. These implementations can also set additional HTTP headers and set the HTTP status code.</p><p>REST requests responses usually fall into 2 types: ones just returning HTTP status and and headers (for example, <code>HEAD</code> and <code>DELETE</code> requests) and ones returning that and also content (for example, <code>GET</code>, sometimes other methods too).</p><h3 id="RESTSupport(pageisworkinprogress)-Contentresponses">Content responses</h3><p>For content responses, Tapestry has out-of-the-box support for <code>StreamRespo
 nse</code> (mostly binary content),&#160; <code>TextStreamResponse</code> (simple text content), <code>JSONArray</code> (since Tapestry 5.8.0) and <code>JSONObject</code> (since 5.8.0).&#160; Here's one example for adding support for a class, <code>User</code>, converting it to the JSON format:</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
+<pre><code class="language-java">/**
+ * Handles {@link User} instances when they're returned by event handler methods.
+ * Heavily inspired by {@link JSONCollectionEventResultProcessor} from Tapestry itself.
+ */
+final public class UserComponentEventResultProcessor 
+        implements ComponentEventResultProcessor&lt;User&gt; {
+
+    private final Response response;
+
+    private final ContentType contentType;
+    
+    private final UserService userService;
+
+    public UserComponentEventResultProcessor(Response response,
+            @Symbol(TapestryHttpSymbolConstants.CHARSET) String outputEncoding,
+            UserService userService)    {
+        this.response = response;
+        this.userService = userService;
+        contentType = new ContentType(InternalConstants.JSON_MIME_TYPE).withCharset(outputEncoding);
+    }
+
+    public void processResultValue(User user) throws IOException
+    {
+        PrintWriter pw = response.getPrintWriter(contentType.toString());
+        pw.write(userService.toJsonString(user));
+        pw.close();
+		// You could also set extra HTTP headers or the status code here
+    }        
+}</code></pre>
+</div></div><div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>UserComponentEventResultProcessor contribution</b></div><div class="codeContent panelContent pdl">
+<pre><code class="language-java">    public void contributeComponentEventResultProcessor(
+            MappedConfiguration&lt;Class, ComponentEventResultProcessor&gt; configuration) {
+        configuration.addInstance(User.class, UserComponentEventResultProcessor.class);
+    }</code></pre>
+</div></div><h3 class="auto-cursor-target" id="RESTSupport(pageisworkinprogress)-Non-contentresponses">Non-content responses</h3><p>For responses without content, just HTTP status and headers, and also for simple String responses, Tapestry 5.8.0 introduced the <code>HttpStatus</code> class. You can create instances of it by either using its utility static methods that match HTTP status names like <code>ok()</code>, <code>created()</code>, <code>accepted()</code>,&#160;<code>notFound()</code> and <code>forbidden() </code>or using one of its constructors. In both cases, you can customize the response further by using a fluent interface with methods for header-specific methods like <code>withLocation(url)</code> and <code>withContentLocation(url)</code> or the generic <code>withHttpHeader(String name, String value)</code>. Check the <code>HttpStatus</code> JavaDoc for the full list of methods.</p><div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelConten
 t pdl">
+<pre><code class="language-java">    @OnEvent(EventConstants.HTTP_PUT) 
+    Object save(@RequestBody User user) {
+        userService.save(user);
+        return HttpStatus.created()
+                .withContentLocation("Some URL")
+                .withHttpHeader("X-Something", "X-Value");
+    }
+
+</code></pre>
+</div></div><p></p></div>
             </div>
             <!-- /// Content End -->
           </div>