What is the difference between redirect and navigation/forward and when to use what? - redirect

What is difference between a navigation in JSF
FacesContext context = FacesContext.getCurrentInstance();
context.getApplication().getNavigationHandler().handleNavigation(context, null, url);
and a redirect
HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
response.sendRedirect(url);
and how to decide when to use what?
The issue with navigation is that page URL does not change unless faces-redirect=true is added to the query string of the navigation URL. However, in my case appending faces-redirect=true throws error if I want to redirect to a non-JSF page like a plain HTML page.
And another option is as BalusC suggested at JSF 2.0 redirect error

First of all, the term "redirect" is in web development world the action of sending the client an empty HTTP response with just a Location header with therein the new URL on which the client has to send a brand new GET request. So basically:
Client sends a HTTP request to somepage.xhtml.
Server sends a HTTP response back with Location: newpage.xhtml header
Client sends a HTTP request to newpage.xhtml (this get reflected in browser address bar!)
Server sends a HTTP response back with content of newpage.xhtml.
You can track it with the webbrowser's builtin/addon developer toolset. Press F12 in Chrome/IE9/Firebug and check the "Network" section to see it.
The JSF navigationhandler doesn't send a redirect. Instead, it uses the content of the target page as HTTP response.
Client sends a HTTP request to somepage.xhtml.
Server sends a HTTP response back with content of newpage.xhtml.
However as the original HTTP request was to somepage.xhtml, the URL in browser address bar remains unchanged. If you are familiar with the basic Servlet API, then you should understand that this has the same effect as RequestDispatcher#forward().
As to whether pulling the HttpServletResponse from under the JSF hoods and calling sendRedirect() on it is the proper usage; no, that isn't the proper usage. Your server logs will get cluttered with IllegalStateExceptions because this way you aren't telling JSF that you've already taken over the control of the response handling and thus JSF shouldn't do its default response handling job. You should in fact be executing FacesContext#responseComplete() afterwards.
Also, everytime whenever you need to import something from javax.servlet.* package in a JSF artifact like a managed bean, you should absolutely stop writing code and think twice if you're really doing things the right way and ask yourself if there isn't already a "standard JSF way" for whatever you're trying to achieve and/or if the task really belongs in a JSF managed bean (there are namely some cases wherein a simple servlet filter would have been a better place).
The proper way of performing a redirect in JSF is using faces-redirect=true query string in the action outcome:
public String submit() {
// ...
return "/newpage.xhtml?faces-redirect=true";
}
Or using ExternalContext#redirect() when you're not inside an action method such as an ajax or prerender listener method:
public void listener() throws IOException {
// ...
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.redirect(ec.getRequestContextPath() + "/newpage.xhtml");
}
(yes, you do not need to put a try-catch around it on IOException, just let the exception go through throws, the servletcontainer will handle it)
Or using NavigationHandler#handleNavigation() in specific cases if you're using XML navigation cases and/or a custom navigation handler with some builtin listener:
public void listener() {
// ...
FacesContext fc = FacesContext.getCurrentInstance();
NavigationHandler nh = fc.getApplication().getNavigationHandler();
nh.handleNavigation(fc, null, "/newpage.xhtml?faces-redirect=true");
}
As to why the navigation handler fails for "plain HTML" files, that is simply because the navigation handler can process JSF views only, not other files. You should be using ExternalContext#redirect() then.
See also:
How to navigate in JSF? How to make URL reflect current page (and not previous one)
When should I use h:outputLink instead of h:commandLink?

Related

JAX-RS : Client side: Intercepting redirection before the redirections are followed

I am trying to implement "redirect handler/filter" on JAX-RS(Jersey) client side. The expectation is that this handler would get invoked before actual redirection by Jersey takes place.
So far I could find is to disable follow redirect and implement own redirect filter, but this means I have to implement full redirect handling including handling loops or other edge cases, which I am trying to avoid and want to rely on Jersey implementation.
I only want to add/change header or other parameters inside the request before actual redirection takes place. Also there are only few redirect we want to manipulate while let other redirections work as is.
Disabling redirect:
ClientBuilder
.newBuilder()
.withConfig(new ClientConfig(clientConfig))
.property(ClientProperties.FOLLOW_REDIRECTS, false)
.build();
Sample redirect handler:
Response resp = requestContext.getClient().target(responseContext.getLocation()).request()
.method(requestContext.getMethod());
responseContext.setEntityStream((InputStream) resp.getEntity());
responseContext.setStatusInfo(resp.getStatusInfo());
responseContext.setStatus(resp.getStatus());
Are there any pointers or reference I can use to intercept redirect?

$response->withRedirect() method in slim not rendering global variable for twig created in middleware

<?php
namespace Auto\middleware;
class registrationMiddleware{
public $container;
public function __construct($container){
$this->container=$container;
// $container is the $app instance passed from the route
}
public function __invoke($request,$response,$next){
$this->container->getContainer()->view->getEnvironment()->addGlobal('error','error');
$response=$next($request,$response);
return $response->withRedirect($this->container->getContainer()->get('router')->pathFor('redirect'));
}
}
It does not output global variable (error) on rendering.
It's important to remember that your browser knows nothing about views, and templates, and global variables. All it sees is an HTTP response, which contains a status code, some headers, and optionally a body.
A normal response has a status code of 200, and a body containing the content the browser should display.
The Slim $response->withRedirect method creates an HTTP response with a status code telling the browser to redirect (301, 302, or 307). Although such a response can contain a body, it will generally just be a fallback for browsers that don't automatically redirect. A standard modern browser will never render this content, because as soon as it sees the status code, it will send a new request to the URL given in the response's Location header, and display the content from that to the user instead.
Once the user has been redirected, there is no context left over from before the redirect. The browser has requested a new URL, and your application will receive that new request and process it, with no way of seeing what happened previously. There will be no global variables, views, or anything else, left over from before the redirect. The only information retained will be things you've explicitly saved to cookies, or to the session (which is referenced by a cookie).
The end result of all this being that you cannot both display something with a view and issue an HTTP redirect in the same piece of code.

How to redirect the url from nested site in pencilblue?

I want to 301 redirect the URLs from previous site that are nested, as pencilblue doesn’t support them,
e.g. a/b to page/b
For this I have been experimenting in include/http/request_handler.js but facing some issues.
Call never comes inside RequestHandler.prototype.handleRequest or even RequestHandler.prototype.onSessionRetrieved (seems these methods are not being called from anywhere)
Therefore I placed the code in RequestHandler and after confirming that req is not for public resource or api, I create a new url and execute
return this.doRedirect(newUrl, 301)
This actually works but at the same time I receive
Can’t render headers after they are sent error
#1075 has not helped me much as I’m not sure which specific controller I should modify. I need to catch the req as early as possible and see if it’s a page then redirect to page prefixed url.
Thanks in advance.
There are couple of ways to do redirects. You can do them from a controller or from middleware. You are correct in that, some of the functions in the request handler are not called. These are deprecated despite the fact pencilblue team didn't mark them as such. They replaced a good deal of the request handler functionality with /include/http/router.js and include/http/middleware/index.js. Plugins can register their own middleware to hijack the request pipeline.
See Advanced Routing on wiki for more info about creating your own middleware.
Using the routing framework your plugin would be able to register middleware that would be able to inspect the request and then redirect based on your specific criteria. The Router will be accessible from req.router and from there you could call req.router.redirect (Source).
Reference: #1224

How to enable/disable HTTP methods for RESTful Web service?

I'm writing a RESTful Web service.
Technologies that I use:
Eclipse EE Kepler IDE
GlassFish 3 (based on Java 6)
Jersey
JDK v7
When I annotate a Java method with, for example, the #DELETE annotation
I get the following HTTP error (invoked via URI):
HTTP Status 405 - Method Not Allowed
I would like to know how to enable/disable (so that to enable/disable the above HTTP error) those methods (PUT, HEAD, etc.) and at which level it can be done (Glassfish, Web.xml, etc). As well, can you invoke all of those resource methods (annotated with HTTP method type) from either Web browser's URI, within the <form>, or stand-alone client application (non-browser)?
For example, whether or not the following config line on deployment descriptor is present, it makes no difference:
<security-constraint>
<web-resource-collection>
<web-resource-name>RESTfulServiceDrill</web-resource-name>
<url-pattern>/drill/rest/resource/*</url-pattern>
<http-method>DELETE</http-method>
</web-resource-collection>
Of course, one's can disable a specific resource method by throwing an exception from it (and map it to an HTTP error) as the indication of it. That would indicate that the implementation is not available, for example.
So far, only #GET and #POST (on the <form>) resource methods work out, the other annotated methods, such as #POST (via URI), #PUT, #DELETE, #OPTIONS returns the above HTTP error. And this is where my question needs solutions. Why does the mentioned resource methods cause HTTP error when the former two don't?
An example of a resource method:
#DELETE
#Consumes(MediaType.TEXT_PLAIN)
#Produces(MediaType.TEXT_PLAIN)
#Path("/getDelete/{value}/{cat}")
public String getDelete(#PathParam("value") String value, #PathParam("cat") String cat){
return value+" : "+cat;
}
Invoking URL:
getDelete
The deployment descriptor is empty, except for the above lines of XML code. So far, I made the app to work by using annotations, no Web.xml (only contains some default values, such as index.jsp files).
Any ideas out there?
To my understanding, You have your REST APIs exposed and you are trying to access it from HTML <form>.Now you are able to access the GET and POST methods(REST APIs) from HTML <form> but not PUT, DELETE and other HTTP methods.
The reason why you get Method Not Allowed exception when you try to access DELETE or PUT or other HTTP methods is, HTML <form> does not support methods other than GET and POST.
Even if you try
<form method="delete"> or <form method="put">
HTML will not understand these methods and consider this as simply <form> (i.e) default form method is GET.
So even you have mentioned method as DELETE or PUT. It is a GET request.
And when the call is made, the jersey container tries to find the requestpath(here"/getDelete/{value}/{cat}") with the specified method(here GET).
Though this path exists,you have mentioned DELETE as acceptable method in your resource(#DELETE annotation says so). But Jersey is looking for GET now.Since it cant find #GET, it returns Method not allowed Exception.
So, how to solve it?
In HTML <form> you cant use HTTP methods other than GET and POST. It is better to have a wrapper in between the REST layer and HTML. So that you can make a POST call from your HTML, then the wrapper handles that call and which in-turns calls the DELETE of REST layer.
And, why POST method is not working from browser is, By default Browser makes a GET call. Have a look at Postman to make REST calls with different Http methods.

GateIn - redirect within portlet

How can you redirect a user by URL (within backing bean) to some other page within portlet? We are using GateIn 3.1 on JBoss 5.1.0
Usually, FacesContext.getCurrentInstance().getExternalContext().redirect("url") is enough, but here it doesn't work, it doesn't redirect user.
context.getApplication().getNavigationHandler().handleNavigation(context, null, page) doesn't work either.
We want to avoid making navigation rules for every possible page we can redirect to.
EDIT:
It appears a4j:commandButton was causing some problems, after we replaced it with h:commandButton we are being redirected but not only within portlet but within portal.
To have sendRedirect available, you have to cast object response to HttpServletResponse :
HttpServletResponse objHttpServletResponse = (HttpServletResponse)
FacesContext.getCurrentInstance()
.getExternalContext()
.getResponse();
objHttpServletResponse.sendRedirect(url);
This is doing a 302 redirect, managed by browser.
The only other alternative (to having many navigation cases in faces-config.xml) I've found that works is using FacesContext.getCurrentInstance().getViewRoot().setViewId(page) to redirect, where page is String page = FacesContext.getCurrentInstance().getViewRoot().getViewId().