AEM, FORM Submission & Handling POST requests
In this article I’ll be specifically talking about how to handle POST request in AEM and what options we have.
Please note that Adobe has added more security around handling POST request from AEM 6.x onwards to prevent CSRF attack. If you don’t configure AEM correctly, POST requests will not work even after using any of methods explained in this article. You can read more about security configurations and restriction at:
It is very common use cases to have external application POST some data to an AEM page (e.g. /content/en/home.html) or even submit a form (POST) to same URL. But, as you know that in AEM POST works differently and any POST call to AEM is intercepted by Sling’s POST servlet (org.apache.sling.servlets.post.impl.SlingPostServlet).
In some cases org.apache.sling.servlets.post.impl.SlingPostServlet is very useful when you actually want to perform CURD (create, update, read and delete) operation on JCR nodes. If you are interested in manipulating nodes in AEM and don’t want to deal with low level JCR session and access right etc. then look at some simple but, very powerful examples at below URL:
Let’s go back to our original topic and define a use case for this article. Let’s say we have a page /content/en/home.html and following are acceptance criteria:
1. GET request should render the original page as HTML (/content/en/home.html).
2. POST request to same page should also render the same page and should persist the original request object (request parameters etc.)
3. External application should be able to POST data to /content/en/home.html and we should be able to access it via request.getParameter(“WHATEVER_PARAM_NAME”)
Out of box first use case just works without any customizations. But, if you try to POST some thing to same URL you’ll not get expected result because Sling’s POST servlet will intercept the request and will think we are trying to perform some CURD operation but that is not what we want in this case.
Sling provides a very useful utility to check which script/servlet will be invoked and if there are multiple eligible servlets that can handle the request then what will be the order. On your author instance navigate to below URL and enter any URL and choose request method (GET, POST etc.) and click resolve. The result will show a list of servlets and their order.
By default the order of POST request processing is as follows:
1. com.adobe.granite.rest.impl.servlet.DefaultServlet (OptingServlet)
So, what options we have?
1. Create a Sling servlet
2. Add a POST.jsp at template level and update form action so that it points to jcr:content e.g. (/content/en/home/jcr:content.html). We need to add jcr:content in URL because this is the node where we have sling:resourceType defined which will tells sling’s resolver where our POST.jsp is sitting that is capable of handling POST request. If we don’t include jcr:content in path then Sling’s POST servlet will handle the request.
3. Just include “.external” (e.g. (/content/en/home.external.html) selector to your POST request URL. Does this sound simple? Yes it is.
I’ll not explain first 2 options as you can find ample of examples for those 2 options on internet. I am going to talk about power of 3rd option in this article. So how does this works?
In AEM every page (.html) has a primary type jcr:primaryType = cq:Page and there are set of scripts that are mapped with this primary type which intercepts request and renders a page differently based on factors like selector, type of request method (by default only GET is handled) etc. You can find these mapping scripts for cq:Page under the folder /libs/foundation/components/primary/cq/Page. One of script under this folder is external.POST.jsp and this is the script which is responsible for handling a POST request with “.external” selector. If you look at the implementation of external.POST.jsp you’ll notice that implementation is very simple, it just creates a wrapper around existing POST request and overrides getMethod() method of SlingHttpServletRequestWrapper class so that it returns GET and forward control page URL (by removing selector) with this wrapped request and response object.
Very simple but powerful technique when you want handle both GET and POST request coming to a page.
I hope this will help you!!! Feel free to comment, suggest correction or anything else that you like…