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)
2. org.apache.sling.servlets.post.impl.SlingPostServlet
3. org.apache.sling.jcr.webdav.impl.servlets.SlingWebDavServlet
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…
Comments