AEM - URL does not change with RequestDispatcher forward - aem

When i do a Forward using RequestDispatcher.. the result page loads but the URL does not change.
The URL where we start and Submit data to the PostServlet: http://localhost:4502/content/en/postformtest.html
Final result URL should be: http://localhost:4502/content/en/postformtestresult.html
But is: http://localhost:4502/services/processFormData
What am i missing? Appreciate any thoughts.
Code snippets..
The HTML Form:
<form name="userRegistrationForm" method="post" action="/services/processFormData">
<input type="submit" title="Submit" class="btn submit btn-success" value="Submit" tabindex="25" name="bttnAction">
</form>
The POST Servlet
#SlingServlet(
label = "Common POST Servlet",
metatype = true,
methods = { "POST" },
name="com.commons.service.servlets.TPostServlet",
paths = { "/services/processFormData" }
)
public class TPostServlet extends SlingAllMethodsServlet{
#Override
protected void doPost(SlingHttpServletRequest request,SlingHttpServletResponse response) throws ServletException,IOException {
final SlingHttpServletRequest syntheticRequest = new SyntheticSlingHttpServletGetRequest(request);
final RequestDispatcherOptions options = new RequestDispatcherOptions();
options.setReplaceSelectors("");
options.setForceResourceType("cq/Page");
request.getRequestDispatcher("/content/en/postformtestresult.html", options).forward(syntheticRequest, response);
}
}
The Wrapper Servlet:
public class SyntheticSlingHttpServletGetRequest extends
SlingHttpServletRequestWrapper {
private static final String METHOD_GET = "GET";
public SyntheticSlingHttpServletGetRequest(final SlingHttpServletRequest request) {
super(request);
}
#Override
public String getMethod() {
return METHOD_GET;
}
}

As the javadocs for RequestDispatcher indicate, the RequestDispatcher and by association the forward method act as wrappers around the resource essentially allowing the delegation of further processing to the resource. This is done behind the scenes so to speak and as such the requested URL will not change - it is not a redirect.
Based on the content of your question I presume what you are trying to accomplish is a traditional form POST to a page. This is actually a rather cumbersome pattern to achieve in AEM and you would most likely be better served by submitting the form asynchronously and then redirecting based on the response.
If all you need is a simple redirect after form processing this can be achieved by calling the sendRedirect method of the response.
If, however, you do need to POST to a page which is then going to handle both the form processing and the page rendering, you could employ a method similar to the OOB form components. The OOB com.day.cq.wcm.foundation.forms.impl.FormsHandlingServlet is implemented as both a Servlet and a request level Filter. As a filter it catches to POST request to the page prior to processing, forwards it using the RequestDispatcher to its Servlet nature, and the Servlet in turn is able to process the request and then forward it, again using the RequestDispatcher, to the page after wrapping the request as a GET request similar to what you are doing above. A bit circuitous but, as noted, this is a cumbersome pattern to realize.

Have you looked at ACS Commons Forms? They do support PRG as a standard form handling process. Check it out at http://adobe-consulting-services.github.io/acs-aem-commons/features/forms.html
You may end up using the feature as is or will get some hint for implementation.
The git link for the same is https://github.com/Adobe-Consulting-Services/acs-aem-commons

Related

Keycloak Custom Form Provider: Form has wrong action url

I have a custom form added to RestCredentialFlow,
I can configure the new flow execution and I see the new Form. Looks all good.
The only problem is that the form action url points to registration and not reset-credentials,
<form id="kc-reset-password-form" class="sb-form-box" action="http://localhost:8080/auth/realms/soka/login-actions/registration?session_code=**&execution=478d7632-2821-42f1-9c34-aa013fea33eb&client_id=account&" method="post">
...
</form>
I can change it in the browser an everything works fine.
Can anybody help, why it points to registration and how to change it.
I don't see any interaction with the registration flow.
Thanks
Here the form is rendered, this already return the form with the wrong actionUrl.
public class ResetCredentialPage implements FormAuthenticator, FormAuthenticatorFactory {
private static final Logger log = Logger.getLogger(ResetCredentialPage.class);
public static final String PROVIDER_ID = "reset-credential-page-form";
#Override
public Response render(FormContext context, LoginFormsProvider form) {
return form.createPasswordReset();
}
...
}
Not sure if you've found the answer yet, I'm in the exact same position. Based on my reading of the documentation I strongly suspect the FormAction/FormAuthenticator classes are designed for the registration page only. So the action uri is not meant to be anything other than the registration uri - basically I suspect it's hard coded somewhere more or less.
If I'm wrong let me know.

How to access suffixes using HTL/Sightly?

TLDR: Create a new AEM page called "mypage.html". Supply suffixes in the URL. Pass this suffixes to an Sling servlet. The suffixes act as URL parameters.
sample desired URL: http://localhost:4502/mypage.html/john/smith
So I created a servlet (used this guide: http://www.aemcq5tutorials.com/tutorials/sling-servlet-in-aem/) that can read a suffix.
#SuppressWarnings("serial")
#SlingServlet(paths="geometrixx/components/hompepage", selectors="name", extensions="html",methods="GET", metatype=true)
public class StaffProfileServlet extends SlingAllMethodsServlet {
private static final Logger log = LoggerFactory.getLogger(CourseBookmarkServlet.class);
#Override
protected void doGet(final SlingHttpServletRequest request,
final SlingHttpServletResponse response) throws ServletException, IOException {
RequestPathInfo rpi = request.getRequestPathInfo();
String[] suffixes = rpi.getSuffix().split("/");
and it working fine if I access it via http://localhost:4502/content/geometrixx/en.name.html/first/last
What I want to do next is create a new page called "mypage.html" and supply first and last as suffixes.
mypage will display information relevant to the person in a properly formatted page. With code above, all I get is JSON response.
Some assumptions/changes which I think is needed to achieve my goal:
I will be using paths and using request parameters (i.e. using request.getParameter("myparameter") on servlet code
I will be using AJAX to access the servlet
If my assumptions are correct, how do I access suffixes from HTL/Sightly? I understand I can get the URI via ${request.requestURI} or even Javascript. And using this value, I can then use this in my AJAX call.
But is this the AEM/Sling way of doing it? Or perhaps there is a better way to do what I want?
Thanks a lot!
You can use RequestPathInfo interface from HTL to access suffix's. ${request.requestPathInfo.suffix}
Global objects accessible through HTL -> here.
Methods accessible through request object -> here.

How to Pass object to REST Get Method

I am using Jersey Rest implementation. There are one Rest Services Called HelloWorld. See the below code.
Please consider this code as reference not as compiled code.
#Path("helloWorld")
public class HelloWorld{
#Path("test")
#Produces(...)
#Consum(...)
#GET
public Response test(Person person){
System.out.println(person);
}
}
I am using Jersey client to sent the request.
Here My question is apart from POST method is there any way to send the object to GET method directly. Instead of QueryString.
Please let me if there is any way to do so.
Thanks
So the problem shouldn't be with the server. I did a few tests on different servers (not weblogic as I don't use it) and all of them seem to have no problems accepting a body in the GET request. The problem seem to be with the client. To test I used the following code
ClientBuilder.newClient()
.target("http://localhost:8080/api/get-body")
.property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true)
.request()
.method(HttpMethod.GET, Entity.text("Hello World"));
The SUPPRESS_HTTP_COMPLIANCE_VALIDATION allows us to pass a body to the request. If we didn't use this, then we would get an error.
The problem with this code, is that even though we set this override property, the client completely overrides the GET method and automatically makes it a POST method, so I would get back a 405 Method Not Allowed.
The solution I came up with is to just allow the client to set a header, e.g. X-GET-BODY-OVERRIDE, and then use a #PreMatching filter on the server side to check for this header. If the header is present, then just change the method to a GET
#Provider
#PreMatching
public class GetWithBodyFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext request) throws IOException {
String getOverride = request.getHeaderString("X-GET-BODY-OVERRIDE");
if (getOverride != null && "true".equalsIgnoreCase(getOverride)) {
request.setMethod(HttpMethod.GET);
}
}
}
Then just register the filter with the server side. On the client, you would simply need to add the header
ClientBuilder.newClient()
.target("http://localhost:8080/api/get-body")
.property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true)
.request()
.header("X-GET-BODY-OVERRIDE", "True")
.method(HttpMethod.GET, Entity.text("Hello World"));
This solution is good because it takes into account more than just the Jersey client, in regards with being able to send a body in the GET request.

issue capturing the hashed URI parameters in Coldfusion [duplicate]

I have such url - http://www.coolsite.com/daily-plan/#id=1
What the easiest way to parse that string and read a hash value (the value after #id=)?
Thank you
On client side (i.e. from JavaScript) you can check window.location.hash to get hash. On server side, general answer is 'it is impossible' since hash is not sent in request to server.
Upd: I maybe misunderstood the question. My answer is about how to get hash part of url either in browser or in server side code during request processing, not about string processing.
Upd2: Answer to comment here because it doesn't fit in comment.
How does it work when user clicks on your navigational links?
I assume hash is changed and corresponding content is downloaded via AJAX request from web service or REST.
For example if your user has URL www.example.com in his browser and this page shows a list of product categories. User clicks one category and URL changes to www.example.com/#id=5 and products from that category(with ID=5) are downloaded via AJAX and shown on the page. No postback, only partial page refresh.
Is this close to your scenario?
Now you want user to paste/enter www.example.com/#id=5 directly in the browser address bar and go directly to list of products in that category.
But /#id=5 is not sent to server with request by the browser, so there is no way to get that value on server side, and you can do nothing about it since it is the browser decided not to send this data and you don't have it on server side.
In our project we use solution when server returns only common page code/html, i.e. header, footer, without main/center part of the page. Then there is a JavaScript code which executes right after this common HTML loaded. It takes window.location.hash and sends it to web service via AJAX and web service returns content (HTML) for the main part of the page.
new URI("http://.../abc#xyz").getFragment();
See the Javadocs for URI
Here is how to capture anchor links. Works across all web frameworks.
I'll use an example scenario to illustrate: let's say we need to capture a deep URL http://server.com/#/xyz requested by an unauthenticated user so that they can be redirected to that deep URL post-login.
The unauthenticated user requests http://server.com/#/xyz (everything from the '#' onwards is not sent to the server).
All the server knows is that the user wants http://server.com/ and that they are unauthenticated. Server redirects the user to a login form.
Here's the clever bit: the client is still waiting on their original request so if the server includes a hidden element in the login form with some JS that references window.location.href, it can capture the full URL of the original request complete with the anchor portion:
<form action="/login" method="post">
<div>
<label>Username:</label>
<input type="text" name="username"/><br/>
</div>
<div>
<label>Password:</label>
<input type="password" name="password"/>
</div>
<!-- XXXXXXXXX CLEVER BIT XXXXXXXXXX-->
<script>
document.write('<input type="hidden" name="from" value="'+document.location.href+'"/>');
</script>
<!-- XXXXXXXXXX-->
<div>
<input class="submit-button" type="submit" value="Submit"/>
</div>
</form>
The user authenticates themself and the original URL is sent with the POST. The server can then relay the user to the original deep URL.
String url = " http://www.coolsite.com/daily-plan/#id=1";
int sharpPos = url.indexOf('#');
String q = null;
if (sharpPos >= 0) {
q = url.substring(sharpPos);
}
Surely you can use various methods of string manipulation including regular expressions.
But actually your example is strange. Typically parameters of URL are passed after question mark. In this case you can just use standard class URL:
String q = new URL(" http://www.coolsite.com/daily-plan?id=1").getQuery();
what you are using to do this ?
If you are using jsp or servlet following will be useful to you
if (request.getParameter("#id") == null) {
out.println("Please enter your name.");
} else {
out.println("Hello <b>"+request.getParameter(i)+"</b>!");
}
If you are using javascript for it following function will be useful to you
function getURLParameters()
{
var sURL = window.document.URL.toString();
if (sURL.indexOf("?") > 0)
{
var arrParams = sURL.split("?");
var arrURLParams = arrParams[1].split("&");
var arrParamNames = new Array(arrURLParams.length);
var arrParamValues = new Array(arrURLParams.length);
var i = 0;
for (i=0;i<arrURLParams.length;i++)
{
var sParam = arrURLParams[i].split("=");
arrParamNames[i] = sParam[0];
if (sParam[1] != "")
arrParamValues[i] = unescape(sParam[1]);
else
arrParamValues[i] = "No Value";
}
for (i=0;i<arrURLParams.length;i++)
{
alert(arrParamNames[i]+" = "+ arrParamValues[i]);
}
}
else
{
alert("No parameters.");
}
}
REPLACE the '#' with '?' when parsing the url. Check the code below
String url = "http://www.coolsite.com/daily-plan/#id=1";
String urlNew = url.replace("#", "?");
String id = Uri.parse(urlNew).getQueryParameter("id");
If you URL will the same as you write and doesn't contains anythins else then whis code on Java will help you
String val = "http://www.coolsite.com/daily-plan/#id=1";
System.out.println(val.split("#id")[1]);
Don't forget check to null value.
P.S. If you use servlet you can get this parameter from request.getAttribute("id").
With best regards,
Psycho
if your url get from OAuth callback,then you can't!
because the full url won't send to service because of hash(#)

How to produce rendered output from a Sling POST in AEM?

It seems like Sling expects every form POST to modify the JCR. So the expected/standard behavior would be a POST-redirect-GET, which is fine for most things. However, I need to be able to POST to AEM and then use the data in that POST to create a rendered result. Our use of AEM is stateless and so I don't want to carry the POST'd data in Session in order to utilize it in a subsequent GET.
Some have recommended putting the POST'd data in Browser sessionStorage, but that doesn't have broad enough support to be sufficient.
As far as I can tell there is no way for Sling in AEM to take a POST and produce a rendered result.
Here is a screenshot of what a POST produces in the page/resourceType component and in any Sling included jsp's that happen to be involved in the rendering.
I have tried things like using the "nop" operation.
<input type="hidden" name=":operation" value="nop" />
But either way all servlets think a POST is happening and don't render properly.
There is the option of creating a custom servlet, to handle the POST, but then how do you render the templated output and change the request so that all the components think they are serving a GET?
UPDATED:
Here is a screenshot of the "nop" POST.jsp result.
What you can do is create a POST.jsp file in the appropiate resourceType.
If your POST request go to /content/yourapp/something, which has a resourceType: your/app/example. Then you can create a file /apps/your/app/example/POST.jsp with whatever render you wish. You can even include your default rendering script in the POST.jsp file if you need it to be rendered the same as the GET requests.
The other option is to use a servlet registered for POST requests and internally use the SlingRequestProcessor service. That service allow you to programmatically process a request through Sling. You can use a SlingRequestWrapper to wrap your request and override getMethod() to return "GET". That should process the request as if it was a GET request.
This sounds like a somewhat funky use case, IIUC you are using a large request parameter P to drive the rendering?
Using a custom POST servlet should work, if you use something like
slingRequest.getRequestDispatcher(resource).forward(request, response) where request is a wrapper around the actual request, where request.getMethod() returns GET. You can then store your P data in request attributes.
The SlingHttpServletRequestWrapper class can be used to create such wrappers.
Creating a custom servlet to handle a post could be an idea. After successfull write you could redirect to the modified resource - simple 302.
The other solution that comes to my mind is a custom Filter that would do the same. However, since AEM expects to get 200 instead of 302, it would be good to tell by atrribute or parameter that this POST needs to be redirected. Otherwise some of the AEM UI functionalities could brake. This is a quick example of an idea. You would probably need to write something more sophisticated.
#Component(immediate = true)
#Service
#Properties({
#Property(name = Constants.SERVICE_DESCRIPTION, value = "Desc"),
#Property(name = Constants.SERVICE_VENDOR, value = "Company name"),
#Property(name = Constants.SERVICE_RANKING, intValue = RedirectFilter.RANKING),
#Property(name = "filter.scope", value = "request") })
public class RedirectFilter implements Filter {
public static final int RANKING = -1000; // low ranking
#Override
public void init(final FilterConfig filterConfig) throws ServletException {
}
#Override
public void destroy() {
}
#Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
throws IOException, ServletException {
if (request.getParameter("redirect").equals("true")) {
((SlingHttpServletResponse) response).sendRedirect(((SlingHttpServletRequest)request).getRequestURI());
}
}
}