• 0
daziplqa

الطريق الوردية في دعم اللغه العربيه

سؤال

بسم الله الرحمن الرحيم

السلام عليكم,,

لعل كثر منا يواجه مشاكل في دعم اللغه العربيه عند البرمجه ب JSP/Servlet و باقي منصات الويب في الجافا (مثل struts و JSF ,, إلخ)

و لعل الكثر من أساتذتنا قد كتبوا في هذا الموضوع, ولعل هذا الموضوع يكون نقطة في بحر, و لكن بصدري علم أريد أن أخرجه لكم.

أولا, الويب في الجافا (و أي ويب) مبني علي إرسال طلب Http أي (Http Request ) للخادم, و إستقبال رد منه (Http Response).

أنواع الطلب في ال Http:

1- GET

2- POST

3- PUT

4- DELETE

5- و أخرون...

بالنسبه لمظم تطبيقاتنا, فنحن لانستخدم إلا أول نوعين GET و POST.

فعند إرسال طلب من نوع GET يكون شكل الطلب كالتالي:

http://some_server:8080/SomeServlet?name1=value1&name2=value2

وبالتالي البيانات التي يقوم المستخدم بإرسالها, ترسل في رأس الطلب (request header)

أما عند إرسال طلب من نوع POST, فإن البيانات التي يقوم المستخدم بإرسالها تكون في بطن الطلب (request body)

و بالتالي فنحن لا نراها في مربع الطلب الخاص بالمتصفح.

إرسال أحرف عربيه

فإن إرسال أحرف عربيه سواء ك GET أو ك POST يتطلب منك عملا زائدا, و نحن بصدد التحدث عنه في هذا الموضوع.

بالنسبه ل POST فالموضوع في الجافا ليس صعبا, لأن الداله setCharacterEncoding داخل الفئة HttpServletRequest , تقوم بتصحيح ال encoding الخاص ببطن الطلب (request body ) .

من وثائق الجافا:

Overrides the name of the character encoding used in the body of this request.

و بالتالي, كما قلنا, فإنها تصحح (set) ال encoding لبطن الطلب,,

و إستخدام الداله

 request.getParameter("name"); 

كاف جداً.

إذن ماذا لو كان الطلب من نوع GET ؟

قصة كبيييره جدا,

دعونا نبدأها,

عندما يقوم المتصفح (أو أي عميل HTTP) بإرسال الطلب للخادم, و إن كان العميل يستخدم الطريقه GET, فإن العميل لايرسل إلا الأحرف اللاتنينيه كما هي, أما باقي الأحرف فإنها يقوم بتحويلها قبل إرسالها (و هذه العمليه هي التي يطلع عيها URL-Encoding )

فمثلا كلنا يعرف أن الرمز %20 يمثل مسافه (space), و يختلف هذا التمثيل طبقا لنوع ال encoding المستخدم لعمل ال URL-Encoding ,

فمثلا حرف "أ" عند عمل URL-Encoding له ب ال UTF-8 , فإنه يعطي شئ, و عند عمل URL-Encoding له ب encoding أخر مثل ISO-8859-6, فإنه يعطي رمز أخر.

لرؤية ذلك, قم بتنفيذ هذا المثال:

package org.daz;

import java.net.URLEncoder;

public class Test {
public static void main(String[] args) throws Exception{
String ch = "أ";
System.out.println(URLEncoder.encode(ch, "UTF-8"));
System.out.println(URLEncoder.encode(ch, "ISO-8859-6"));
}
}

ولاحظ الناتج :

%D8%A3
%C3

إذن, كما قلنا , فالمتصفح قبل أن يرسل أحرف non-ASCII (كما هو الحال في حالة اللغه العربيه), فإنه سوف يقوم بعمل url-encoding لها.

لكن السؤال هنا, ما هو نوع ال encoding الذي سوف يتستخدمه المتصفح ؟؟

أنت من يخبر المتصفح بإستخدام encoding معين , عن طريق أن تضع الكود التالي فصفحة ال JSP الخاصه بك:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- your page goes here -->
</body>
</html>

و في حالة الصفحات من نوع HTML فقط, يكفي السطر التالي:

<

meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

أنت هنا, تطلب من المتصفح أن يعمل url-encoding للبيانات التي سوف ترسلها للخادم ب UTF-8 (و هي كاف جدا للغه العربيه)

لكن لاحظ, يجب من الخادم أن يعرف مسبقا ما هو نوع ال encoding الذي تم به عمل url-encoding للبيانات التي سوف تأتي إليه,, (و حيث أنك ستكن المطور للصفحات و الخادم معا, فستتحكم فكلا الطرفين )

حسنا, بالنسبه للخادم ( ال Servlet الخاص بك),

بالنسبه لل POST كما ذكرنا فإن إستخدام :

 request.getParameter

بعد أن تقوم بمناداة

 request.setCharacterEncoding("UTF-8")

كاف جدا,,

إذا, ماذا عن GET؟؟ لا الأمر مختلف, بالنسب ل GET فلو إستخدمت request.getParameter مباشرة, فإنك سوف تحصل على بيانات مخطئة (corrupted data)

إذا, الحل هو إستخدام الدالة request.getQueryString() و هي تقوم بإسترجاع ال query string كاملا لك,

و ال query string هو ما يكون في نهاية الطلب و يكون بالشكل :

name1=value1&name2=value2&name3=value3

بعد الحصول على ال query string, نقوم بعمل URL-Decode له (لأن المتصفح قام بعمل url-encoding له), و عند عمل url-decoding نستخدم نفس ال encoding الذي إستخدم المتصفح:

لاحظ المثال التالي:

package org.daz;

import java.net.URLDecoder;
import java.net.URLEncoder;

public class Test {
public static void main(String[] args) throws Exception{
String ch = "أ";
System.out.println("original String: " + ch);
String encoding_data = URLEncoder.encode(ch, "UTF-8");
System.out.println("encoded string: "+ encoding_data);
System.out.println("original string again: "+ URLDecoder.decode(encoding_data, "UTF-8"));
}
}

لاحظ الناتج:


original String: أ
encoded string: %D8%A3
original string again: أ

إذن أنت تحتاج أن تعمل URL-decoding بنفس ال encoding الخاص بالصفحة التي قام المتصفح بإرسالها لك,,

المثال التالي به كود يقوم بمعالجة مشكلة اللغة العربيه لكلا الطريقتين POST و GET:

أولا, صفحة ال JSP:

<%@ 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>

و هذه هي الفئة التي تقوم بتصحيح ال ENCODING في حالة GET:

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());
}
}

و هذه Filter يصحح ال encoding في حالة POST و يستخدم الفئة السابقة لتصحيح ال encoding في حالة GET:

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() {}
}

و هذا ال 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>

و هذا ال Servlet للإختبار :

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"));
}

}

لقد قمت بإرفاق هذه المثال لكي تعم الفائده,

أرجو من الأخوه المشرفين النظر في تثبيت هذا الموضوع نظرا لاهميته (بالنسبالي, أنا موافق , ههههههههه)

المراجع:

http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol

http://en.wikipedia.org/wiki/POST_%28HTTP%29

http://java.sun.com/developer/technicalArticles/Intl/HTTPCharset/

http://balusc.blogspot.com/2009/05/unicode-how-to-get-characters-right.html

test.zip

تم تعديل بواسطه {هويدي}
6

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

4 إجابة على هذا السؤال .

  • 0

لا أعرف يا هويدي لكني لم أواجه أي مشاكل مع get بالنسبة لـ post فيكفيني في العادة استخدام الدالة setCharacterEncoding

أين تظهر هذه المشاكل بالضبط؟

تحياتي

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

صحيح, بالنسبه لل POST, فإستخدام setCharacterEncoding كافية جدا,

هل جربت GET ؟

جرب هذا المثال:

<%@ 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="ControllerServlet" method="get">
Enter your name (in arabic) <input type="text" name="username"/>
<input type="submit"/>
</form>
</body>
</html>

package com.forat;

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

public class ControllerServlet extends HttpServlet {
private static final long serialVersionUID = 1L;

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String corruptedValue = request.getParameter("username");
System.out.println(corruptedValue);
}

}

و هذا هو الناتج (أعمل على Ubuntu Linux, JVM 1.6):

أحمد

1

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

:blink: :blink:

صحيح الكلام مئة بالمئة

واستغرب أن هذه المشكلة لم تحصل معي من قبل أبداً

يبدو أني لم أستخدم get أبداً في النماذج

على كل شكراً أخ هويدي على الموضوع

تحياتي

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

:blink: :blink:

صحيح الكلام مئة بالمئة

واستغرب أن هذه المشكلة لم تحصل معي من قبل أبداً

يبدو أني لم أستخدم get أبداً في النماذج

على كل شكراً أخ هويدي على الموضوع

تحياتي

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

من فضلك سجل دخول لتتمكن من التعليق

ستتمكن من اضافه تعليقات بعد التسجيل



سجل دخولك الان

  • يستعرض القسم حالياً   0 members

    لا يوجد أعضاء مسجلين يشاهدون هذه الصفحة .