07 May 2010

Enable Arabic (non-ASCII) characters in request parameteres of "GET" method

Al salamo Alykom,

If you have a JSP page that sends a request with non-ASCII data to a Servlet, here's the steps to enable the Servlet from interrupting the correct character.

First, for your Servlet (Server) to interrupt the character encoding correctly, It needs to know in what encoding the client will send the request.

1- suppose you are sending the following JSP Page:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

<form action="SomeServlet" method="get">
Enter your name (in arabic, or in any non-ASCII language)<input type="text" name="username" />
<input type="submit">
</form>

</body>
</html>


2- create the following Class:

package com.forat.web.util;


import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

/**
* Used to overrides the implementations of
* getParameterXXXXX methods in {@link HttpServletRequest} <br />
* It decoded the query string for the incoming requests Requests before calling the getParameterXXXX methods
*
* @author mhewedy
*/
public class EncodedHttpServletRequest extends HttpServletRequestWrapper {

private String encoding;
private HttpServletRequest request;
private Map paramMap;

/**
* This Constructor is the same as if you create an instance by calling
* EncodedHttpServletRequest(request, "GB2312")
* @param request
* @see #EncodedHttpServletRequest(HttpServletRequest, String)
*/
public EncodedHttpServletRequest(HttpServletRequest request) {
this(request, "UTF-8");
}

/**
* Create instance of this class, trying to set the character encoding - that will be used to decode the query
* string - by calling {@link HttpServletRequest#getCharacterEncoding()}, if its value is null,
* then the <em>encoding</em> parameter's value will be used instead
* @param request
* @param encoding encoding to be used to encode the request's query string
*/
public EncodedHttpServletRequest(HttpServletRequest request, String encoding) {
super(request);
this.request = request;
this.encoding = encoding;
}

public Map getParameterMap() {
if (paramMap == null) {
String queryString = request.getQueryString();
if (queryString == null) return Collections.unmodifiableMap(paramMap = new HashMap());
synchronized (this) {
if (paramMap == null) {
paramMap = new HashMap();
fillParameterMap(queryString);
}
}
}
return Collections.unmodifiableMap(paramMap);
}

private void fillParameterMap(String queryString) {
try {
String[] nvPair = queryString.split("&");
for (String pair : nvPair) {
String[] pairArr = pair.split("=");
String key = pairArr[0];
String value = URLDecoder.decode(pairArr[1], encoding) ;

if (paramMap.containsKey(key)) {
String[] valArr = (String[]) paramMap.get(key);
String[] newValArr = new String[valArr.length + 1];
System.arraycopy(valArr, 0, newValArr, 0, valArr.length);
newValArr[newValArr.length - 1] = value;
paramMap.put(key, newValArr);
}else {
paramMap.put(key, new String[] {value});
}
}
}catch(UnsupportedEncodingException ex) {
throw new RuntimeException(ex.getMessage(), ex);
}catch(Exception ex) {
throw new RuntimeException("Malformed query string: " + queryString, ex);
}
}

public String getParameter(String name) {
Object value = getParameterMap().get(name);
if (value == null)
return null;
return ((String[])getParameterMap().get(name))[0];
}

public String[] getParameterValues(String name) {
Object value = getParameterMap().get(name);
if (value == null)
return null;
return ((String[])getParameterMap().get(name));
}

public Enumeration getParameterNames() {
return Collections.enumeration(getParameterMap().keySet());
}
}


3- create that filter :

package com.forat.web.util;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

/**
* Set the encoding for the POST requests if one not specified by the incoming request
* @author mhewedy
*/
public class EncodingFilter implements Filter {

public void init(FilterConfig fConfig) throws ServletException {}

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

String HTTP_METHOD = ((HttpServletRequest) request).getMethod();
if ("GET".equalsIgnoreCase(HTTP_METHOD)) {
// for GET requests
request = new EncodedHttpServletRequest((HttpServletRequest) request, "UTF-8");
}else if ("POST".equalsIgnoreCase(HTTP_METHOD)) {
// for Posted requests
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
}

chain.doFilter(request, response);
}

public void destroy() {}
}



and here's its mapping:

<filter>
<display-name>EncodingFilter</display-name>
<filter-name>EncodingFilter</filter-name>
<filter-class>com.forat.web.util.EncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>


4- and here's the Servlet that accepts the User input:

package com.forat.web;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* Servlet implementation class SomeServlet
*/
public class SomeServlet extends HttpServlet {
private static final long serialVersionUID = 1L;

/**
* @see HttpServlet#HttpServlet()
*/
public SomeServlet() {
super();
// TODO Auto-generated constructor stub
}

/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println(request.getParameter("username"));
}

}



Good luck :)
(for more comprehensive tutorial, please see http://www.arabteam2000-forum.com/index.php?showtopic=220589 (In Arabic) )

3 comments:

Unknown said...

It is still showing ???? in console. Please clarify that the code you have written is correct. Or post a war file of your work.

mhewedy said...

Here are you bro a test app:

http://www.arabteam2000-forum.com/index.php?app=core&module=attach&section=attach&attach_id=103345

mhewedy said...

I am not sure if you need to register first or not.