I have a J2EE application with a web service which goes like
http://servername/service?task=getFile&id=25
How can I convert these type of urls to
http://servername/service/getFile/25
http://servername/service/getFile/26
etc?
Please provide your suggestions.
You can use the UrlRewriteFilter in order to achieve this. You will just have to write the rules for rewriting, similar to mod_rewrite. For example:
<rule>
<from>^/products/([0-9]+)$</from>
<to>/products/index.jsp?product_id=$1</to>
</rule>
To the point, you thus want to forward the friendly URL to an unfriendly URL (so that you don't need to change existing request parameter collecting logic of the servlet) and to redirect the unfriendly URL to an friendly URL (so that the friendly URL get reflected in the browser address bar of the client).
The best place for this is a Filter. To access the HttpServletRequest, just downcast ServletRequest to HttpServletRequest. You can get the query string by getQueryString() and you can get the pathinfo by getRequestURI(). Here's a kickoff example:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException
{
HttpServletRequest httpreq = (HttpServletRequest) request;
String query = httpreq.getQueryString();
if (query != null) {
// Unfriendly URL invoked. Convert params to pathinfo and redirect.
StringBuffer newURL = httpreq.getRequestURL();
for (String param : query.split("&")) {
newURL.append('/').append(param.substring(param.indexOf('=') + 1));
}
((HttpServletResponse) response).sendRedirect(newURL.toString());
} else {
// Friendly URL invoked. Convert pathinfo to params and forward.
String[] parts = httpreq.getRequestURI().replace(httpreq.getContextPath(), "").split("/");
String newURL = String.format("%s?task=%s&id=%s", parts[1], parts[2], parts[3]);
httpreq.getRequestDispatcher(newURL).forward(request, response);
}
}
You can of course also grab the aforementioned UrlRewriteFilter.
Related
I have a method which processes that URL:
http://IP:PORT/auth/myapp?Username=username
and accessible from remote. However I can not change external system which uses my app and it sends username within HTTP Header. I mean they access that URL:
http://IP:PORT/auth/myapp
I think that I can get related HTTP Header as follows:
Enumeration headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()) {
String headerName = (String)headerNames.nextElement();
if (headerName.equals("UNAME")) {
String username = request.getHeader(headerName);
}
}
I can not modify whole part of my app and I have to add that info as path parameter into existing request. I mean change that request to:
http://IP:PORT/auth/myapp?Username=username
How can I do that?
PS:
Can this piece of code solve the problem that I've described?
if (request.getParameter("Username") == null) {
Enumeration headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = (String) headerNames.nextElement();
if (headerName.equals("UNAME")) {
String username = request.getHeader(headerName);
response.sendRedirect(request.getRequestURI() + "&Username="+username);
}
}
}
If you are accessing 'username' query parameter using HttpServletRequest.getParameter('username') in other parts of your app, you can try using HttpServletRequestWrapper
class MyRequestWrapper extends HttpServletRequestWrapper{
public String getParameter(String name){
// if name equals username, call super.getHeader('username')
//else super.getParameter(name);
}
}
You can extend this class and override getParameter() method. In your implementation, you get the value from header if parameter name is username else call the super method.
It's round-a-bout, but if you also have the HttpServletResponse object, you can send a redirect request to the http://IP:PORT/auth/myapp?Username=username URL
I think there is two solutions:
Use a filter on your application mapped on each request. The filter will check header and add a parameter if the UNAME header is there
Use Apache Rewrite Module in front of your web application. Rewrite module will add the parameter to the url (check "Module Apache mod_rewrite")
UPDATE: As mohit said used a HttpRequestWrapper combined with a filter
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
//create the MyCustomWrapperRequest object to wrap the HttpServletRequest
MyCustomWrapperRequest request = new MyCustomWrapperRequest((HttpServletRequest)servletRequest);
//continue on in the filter chain
filterChain.doFilter(request, servletResponse);
}
And in MyCustomWrapperRequest override getParameter() function
I have a controller mapping, where I pass 2 request params instead of 1. But when done like that Spring is not throwing any exception rather it is ignoring the additional request params.
For eg:
#RequestMapping(value="/test", method = RequestMethod.GET)
public ModelAndView eGiftActivation(#RequestParam("value") String value)
When I hit my app using /test.do?value=abcd it is working fine. But when I pass additional params like /test.do?value=abcd&extra=unwanted also it's working fine.
In this case I want Spring to restrict the second URL where additional params are passed.
How can I achieve this?
You could check it manually, like this:
#RequestMapping("/test")
public ModelAndView eGiftActivation(HttpServletRequest request) {
Map<String, String[]> params = request.getParameterMap();
if (params.size() != 1 || !params.containsKey("value")) {
throw new RuntimeException("Extra parameters are present"); // or do redirect
}
...
}
I don't think it's possible (For Spring to prevent the request to flow to any controller's method). The reason being that:
Your controller handles request based on the URI path like, /app/hello/{name} rather than the request parameters
Request parameters are there to give extra set of meta-info for the request rather than endpoint specification of request.
But, if you wanted to restrict the URI path as such, you can use regex and you can avoid. I'm afraid it's not feasible and even the requirement for that never arose.
Programmatical Way:
Having said that, you can take HttpServletRequest for parameters and loop through the parameters to check for extra ones:
#RequestMapping(value="/test", method = RequestMethod.GET)
public Object eGiftActivation(#RequestParam("value") String value, HttpServletRequest request){
//check the request.getParameterMap() and throw custom exception if you need and handle using Exception handler or throw invalid request
return new ResponseEntity<String>(HttpStatus.SC_BAD_REQUEST);
}
I prefer handling these kind of validations (if required, what ever may be the reason) inside the Filter generically so that the requests will not even reach the Controller methods.
Please find the required code to handle inside the Filter as below (logic is almost similar to Slava).
#Component
public class InvalidParamsRequestFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
Map<String, String[]> params = request.getParameterMap();
if (request.getRequestURI().contains("/test") && (params.size() != 1 || !params.containsKey("value"))) {
//Here, Send back the Error Response OR Redirect to Error Page
} else {
filterChain.doFilter(request, response);
}
}
}
I am using JSF2. I have implemented a custom faces servlet like so:
public class MyFacesServletWrapper extends MyFacesServlet {
// ...
}
wherein I'm doing some authorization checks and sending a redirect when the user is not logged in:
public void service(ServletRequest request, ServletResponse response) {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
if (...) {
String loginURL = req.getContextPath() + "/LoginPage.faces";
res.sendRedirect(loginURL);
}
}
This works when the user tries to navigate to another page. However, this does not work when a JSF form is submitted by a JSF command link/button. The line sendRedirect() line is hit and executed, no exception is been thrown, but the user stays at the same page. Basically, there's no visual change at all.
Why does this work on page navigation, but not on form submit?
Your concrete problem is most likely caused because your JSF command link/button is actually sending an ajax request which in turn expects a special XML response. If you're sending a redirect as response to an ajax request, then it would just re-send the ajax request to that URL. This in turn fails without feedback because the redirect URL returns a whole HTML page instead of a special XML response. You should actually be returning a special XML response wherein the JSF ajax engine is been instructed to change the current window.location.
But you've actually bigger problems: using the wrong tool for the job. You should use a servlet filter for the job, not a homegrown servlet and for sure not one which supplants the FacesServlet who is the responsible for all the JSF works.
Assuming that you're performing the login in a request/view scoped JSF backing bean as follows (if you're using container managed authentication, see also 2nd example of Performing user authentication in Java EE / JSF using j_security_check):
externalContext.getSessionMap().put("user", user);
Then this kickoff example of a filter should do:
#WebFilter("/*") // Or #WebFilter(servletNames={"facesServlet"})
public class AuthorizationFilter implements Filter {
private static final String AJAX_REDIRECT_XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<partial-response><redirect url=\"%s\"></redirect></partial-response>";
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession session = request.getSession(false);
String loginURL = request.getContextPath() + "/login.xhtml";
boolean loggedIn = (session != null) && (session.getAttribute("user") != null);
boolean loginRequest = request.getRequestURI().equals(loginURL);
boolean resourceRequest = request.getRequestURI().startsWith(request.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER + "/");
boolean ajaxRequest = "partial/ajax".equals(request.getHeader("Faces-Request"));
if (loggedIn || loginRequest || resourceRequest)) {
if (!resourceRequest) { // Prevent browser from caching restricted resources. See also https://stackoverflow.com/q/4194207/157882
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
response.setHeader("Pragma", "no-cache"); // HTTP 1.0.
response.setDateHeader("Expires", 0); // Proxies.
}
chain.doFilter(request, response); // So, just continue request.
}
else if (ajaxRequest) {
response.setContentType("text/xml");
response.setCharacterEncoding("UTF-8");
response.getWriter().printf(AJAX_REDIRECT_XML, loginURL); // So, return special XML response instructing JSF ajax to send a redirect.
}
else {
response.sendRedirect(loginURL); // So, just perform standard synchronous redirect.
}
}
// ...
}
See also:
Using JSF 2.0 / Facelets, is there a way to attach a global listener to all AJAX calls?
FullAjaxExceptionHandler does not show session expired error page on ajax button
FacesContext.getCurrentInstance().getExternalContext().redirect("newpage.xhtml"); try this.... in place of res.sendredirect(cpath).
I've a method that uses HttpServletRequest, HttpServletResponse and ServletContext.
Here's my code:
public String processarModelo(MyObject ou, Map<String, Object> attrs,
Map<String, Object> params) throws Exception {
ServletContext sc = com.opensymphony.webwork.ServletActionContext.getServletContext();
HttpServletResponse r = com.opensymphony.webwork.ServletActionContext.getResponse();
MyHttpRequest rw = new MyHttpRequest(com.opensymphony.webwork.ServletActionContext.getRequest());
rw.clearAttributes();
for (String s : attrs.keySet()) {
rw.setAttribute(s, attrs.get(s));
}
Map<String, String> p = rw.getParameterMap();
p.clear();
for (String s : params.keySet()) {
p.put(s, (String) params.get(s));
}
ByteArrayOutputStream bout = new ByteArrayOutputStream();
Writer w = new OutputStreamWriter(bout);
SwallowingHttpServletResponse r2 = new SwallowingHttpServletResponse(r, w, "iso-8859-1");
javax.servlet.RequestDispatcher dispatcher = sc.getRequestDispatcher("/paginas/expediente/processa_modelo.jsp");
dispatcher.include(rw, r2);
w.flush();
String s = bout.toString();
return s;
}
It's working when I call it from my browser.
This week I've built a webservice that must call the same method. Inside the webservice method I have however no ServletContext, HttpServletResponse and HttpServletRequest at hands.
How can I emulate them to render my JSP and get the generated HTML?
Well, searching around about #Context annotation, i found this one for webservice:
#Resource
private WebServiceContext context;
With this, i can get everything i need with this:
HttpServletRequest request = (HttpServletRequest)context.getMessageContext().get(MessageContext.SERVLET_REQUEST);
ServletContext servletContext = (ServletContext) context.getMessageContext().get(MessageContext.SERVLET_CONTEXT);
HttpServletResponse response = (HttpServletResponse) context.getMessageContext().get(MessageContext.SERVLET_RESPONSE);
Thanks everybody for helping!!
Are you sure you want your web service to call this? It will get redirected to some other page....
It looks like a design issue to me. You should extract the actual functionality from here and place it in some common code. Then call it in such a way that the web service request and servlet request will have enough information to call the functionality.
Also one letter variable names are discouraged ;)
If you want to make a call to the web page to store the HTML, you should make an HTTP request in the webservice method itself and store the data. You can use something like HTTPClient (http://hc.apache.org/httpcomponents-client-ga/) to do this.
Trying to build the HTTPRequest yourself isnt a good way to go.
I am implementing a filter to set
httpServletResponse.setHeader("X-XSS-Protection", "1; mode=block");
I have written the filter. I want to check if its working perfect or not.
I thought to read the header from response object. But I don't know how to do that.
Can any one tell how to do it.
Or if there is abetter way of doing it, let me know.
Edit
Updating the code
public void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain filterChain)
throws IOException, ServletException
{
final HttpServletResponse response = (HttpServletResponse) res;
final HttpServletRequest request = (HttpServletRequest) req;
//set X-XSS-protection in http header, other http headers can be added in same way
String value = enable ? "1" : "0";
if(block)
{
value += "; mode=block";
}
PrintWriter out = response.getWriter();
out.println("ready to set xss");
response.setHeader("X-XSS-protection", value);
out.println("<br/><br/>Xss has been set");
filterChain.doFilter(req, res);
out.println("<br/><br/>XSS"+request.getHeader("X-XSS-protection"));
out.println("<br/><br/>job done");
}
I am getting "XSSnull"
Please help me how do I correct it.
Thanks in advance.
You can get and read the response reader like this
request.getHeader("name of the header");
I am guessing in your case you are aiming for something like this
request.getHeader("X-XSS-Protection");
EDIT
for more clarification, you can think of a little analogy of Request and Response.
Request - What you are sending.
Response - What you are receiving
for more information about Request and Response please refer to this guide, if will containt all the information you need about Request and Response headers. Go specifically to 'Handling Http Response Headers'