Is there a simple way to implement routing in JSF?

2019-05-03 10:21发布

I am trying to implement a "generic" view where (part of) the content displayed depends on the URL. E.g.

If /somepath/somepage.xhtml points to a non existing file, instead of going straight to a 404 error I want to try to retrieve /somepath/somepage.xhtml's content from the database using a generic view, /genericview.xhtml, where I have something like:

<h:outputText value="#{genericViewBean.content_lg}"
                escape="false" />

which, if found by the backing bean, would output the content of the database entry from a tgenericcontent table, depending on the originally requested viewId:

 webpath                              | content
 /somepath/somepage.xhtml             | <p>This is a test</p>
 /someotherpath/someotherpage.xhtml   | <p>A different test</p>

If the view content is not found in that table then the standard 404 error would be returned.

The closest I got wast to clone /genericview.xhtml changing only the file path (for example, to /somepath/somepage.xhtml). But that gets me one exact copy of the file per view, it is quite messy, and it doesn't allow me to create a new url just by adding an entry to my database.

How can I get the same result without cloning /genericview.xhtml?

(P.S: I have read about prettyfaces, but isn't there a simpler solution?)

1条回答
SAY GOODBYE
2楼-- · 2019-05-03 10:36

For that, normally a servlet filter is being used. PrettyFaces, UrlRewriteFilter and FacesViews also do it that way.

You can get the request URI by HttpServletRequest#getRequestURI(). You can check the existence of a web resource by ServletContext#getResource() which will return null on non-existent resources. If the resource exists, just continue the request by FilterChain#doFilter(), else forward the request to the generic view by RequestDispatcher#forward().

All in all, this is how the filter could look like:

@WebFilter("/*")
public class GenericViewFilter implements Filter {

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        String relativeRequestURI = request.getRequestURI().substring(request.getContextPath().length());

        boolean resourceExists = request.getServletContext().getResource(relativeRequestURI) != null;
        boolean facesResourceRequest = request.getRequestURI().startsWith(request.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER));

        if (resourceExists || facesResourceRequest) {
            chain.doFilter(request, response);
        }
        else {
            request.getRequestDispatcher("/genericview.xhtml").forward(request, response);
        }
    }

    // ...
}

In the /genericview.xhtml, the original request URI is available as request attribute keyed with RequestDispatcher#FORWARD_REQUEST_URI. You could use it in @PostConstruct of backing bean associated with the view in order to pull the right content from the DB.

String originalRequestURI = (String) externalContext.getRequestMap().get(RequestDispatcher.FORWARD_REQUEST_URI);
// ...
查看更多
登录 后发表回答