IT_Programming/JSP · Servlet

JavaScript에 있어서의 URLencode의 처리

JJun ™ 2006. 11. 4. 11:54

JavaScript에 있어서의URLencode의 처리

주식회사 쿠레스

 

이 메모는 ,JavaScript에 쿠키를 처리하는 경우의 포인트를 가리켜 ,URLencode에 관련되는 트러블을 회피해 주시는 것을 목적으로 하고 있습니다. 급한 것(분)편은3장과4아키라를 파견해 읽어 받아 상관하지 않습니다. 또한 이 메모는 가능한 한IE에 봐 주세요.

 

목차

1.  1 쿠키와URL인코딩

2.  2 URL인코딩과는

3.  3 JavaScript에 있어서의escape()unescape()함수

4.  4 JavaScript에 있어서의encodeURI,decodeURI,encodeURIComponent,decodeURIComponent

5.  5 그러면 도대체 어떻게 하면 좋은가?

6.  6 UTF-8URLencode·디코드 함수의 예:

프로그램예

7.  7 JSPJavaScript간의 쿠키에 의한 데이터 교환예

 

HTML텍스트 이외에 웹·서버가 브라우저에 정보를 건네주는 수단은 ,HTTP응답 메세지의 헤더 부분에 그 정보를 세트 하는 것입니다 (보디 부분에는 통상HTML텍스트가 들어갑니다 ). 서버측으로부터는 쿠키 설정의 헤더행이나 스스로 만든 특별한 헤더행에 정보를 세트 할 수 있습니다. 이것들은 (이름 , 값) 의 페어의 형식을 취합니다.

 

HTML텍스트외에서 건네받는 정보는 , 브라우저 화면에 표시되지 않기 때문에 , 표시의 목적 이외의 세션 관리 등에 사용됩니다. 서브 렛에 있어서의 쿠키에 의한 세션 유지의 메카니즘은 , 바야흐로 이 특성을 이용한 것입니다. 세션 이외에도 클라이언트와의 각종 관리 정보(브라우저의 타입이나 버젼 , 유저 정보 등 ) , 암호화를 위한 정보(열쇠나 방식등 ) 의 교신이라고 하는 응용도 생각할 수 있습니다. 그리고 보낸 쿠키는 클라이언트에 축적되므로 , 반복 표시나 화면에 공통의 정보를 최초로 쿠키로 보내 버리는 일도 가능합니다. 그리고 , 폼에 표시하지 않고 필요한 정보를 서버에 돌려줄 수도 있습니다.

 

예를 들어Tomcat등의 서브 렛·엔진이 세션 유지에 사용하고 있는(“jsessionid”, ID)의 종류의 메카니즘 이외에 , 좀 더 상세하게 그 세션의 정보(유저의 이름등 ) 를 클라이언트로 유지해 , 필요하게 응하고 이것을 표시하는 등이 가능하게 됩니다.

 

JavaScript에 있어서는 ,HTTP응답 메세지의 헤더행을 직접 꺼내는 기능은 유감스럽지만 없습니다. 그렇지만Window.document.cookie오브젝트를 사용해 , 쿠키를 개입시킨 정보의 교환이 가능합니다. 그렇지만 , 후술과 같이HTTP메세지의 헤더행은URLencode 되지 않습니다. ASCII캐릭터 세트만으로 끝나는 구미와 달리 우리와 같이2아르바이트의 캐릭터 세트를 표준적으로 사용하는 경우는 ,URLencode에 주의하지 않으면 안됩니다. 여러분이 고민해 그리고 문제를 일으키기 쉬운 것은 이 점이지요.

 

이 메모는 ,JavaScript그리고 쿠키를 처리하는 경우의 포인트를 가리켜 ,URLencode에 관련되는 트러블을 회피해 주시는 것을 목적으로 하고 있습니다.

 


 

1.        쿠키와 URL인코딩

 

쿠키와URL인코딩의 기본적인 지식이 필요하게 되기 때문에

, 최초로 그 포인트를 실례로 가리키겠습니다.

 

통상cookie은 어플리케이션·서버가HTTP응답의 패킷의 헤더 부분에 세트 해 브라우저에 건네줍니다. 예를 들면IBM의 서브 렛·엔진은 다음과 같은HTTP의 헤더행을HTTP응답에 붙여 세션(서비스와 클라이언트와의 대응의 식별) 의 유지를 이라고 깔때기 하고 있습니다. 이 예() 에서는sessionid이라고 하는 「이름」의 변수와LV··되는 「값」의 조를 건네주고 있습니다.

Set-Cookie: sessionid=LV140HYAAAABZQ....;Path=/

 

이와 같이cookieHTTP패킷의 헤더행에 의해 전달되므로 ,2아르바이트 문자나”;”이나”=”등이 위험한 (프로토콜 상의미를 가진다 ) 문자를 포함한 「이름」이나 「값」을 가지는cookie을 클라이언트에 건네줄 때는 , 위험한 아르바이트 문자를 포함하지 않게URLencode 해 , 아르바이트열로서 전달하지 않으면 되지 않습니다.

 

멀티 아르바이트 문자를 서브 렛·엔진은 과연 이것을 인식해 자동적으로URLencode 해 주는 것일까

실험해 봅시다.

 

다음의 서브 렛은 넷상(http://ash.jp/java/hellocookie.htm)에서 공개되고 있던 프로그램에 일부 손본 것입니다. 이 프로그램은 서브 렛에 있어서의 쿠키 처리에 관한 힌트가 들어가 있기 때문에 , 대충 이해해 주세요.

import java.io.*;

import java.net.*;

import javax.servlet.*;

import javax.servlet.http.*;

/** 쿠키 읽고 쓰기 서브 렛 **/

public class HelloCookie0 extends HttpServlet {

  public void doGet (HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

    PrintWriter out;

    Cookie[] cookies;

    Cookie cookie;

    // Cookie의 취득

    cookies = req.getCookies();

    cookie = null;

    if (cookies != null){

      for(int i=0; i < cookies.length; i++) {

        cookie = cookies[i];

        if (cookie.getName().equals("HelloCookie")) { break; }

      }

    }

    res.setContentType("text/html; charset=Shift_JIS");

    out = res.getWriter();

    HttpSession session = req.getSession();  // Session의 취득과 기입해

    session.setMaxInactiveInterval(600);       // 10분간 유효

    if (session.isNew()) {     // Cookie의 기록

      cookie = new Cookie("HelloCookie", "Hello World!");

      cookie.setMaxAge(60);   // 유효기간은1분 에 끊어지는

      res.addCookie(cookie);

      out.println("<html><body>");

      out.println("<h1>Write Cookie</h1>");

      out.println("<p>리로드 해 주세요. </p>");

      out.println("</body></html>");

    } else {      // Cookie의 표시

      out.println("<html><body>");

      out.println("<h1>");

      for(int i=0; i < cookies.length; i++) {

        cookie = cookies[i];

        out.println(cookie.getName()+" : "+ new String(cookie.getValue().getBytes("8859_1"), "Shift_JIS"));

        out.println("<BR>");

      }

      out.println("</h1>");

      out.println("<p>Cookie의 샘플(HelloCookie0.java)</p>");

      out.println("</body></html>");

    }

  }

}

 

이 프로그램으로cookie = new Cookie("HelloCookie", "Hello World!");이라고 하는 행의 「값」의 캐릭터 라인에 한자나 위험한 문자를 설정하면(자) 서브 렛·엔진은 어떠한 응답 패킷을 송신할까 시험해 봅시다.

 

a)       ”Hello 일본!”과 바꾸어telnet의 소프트웨어로 액세스 하면(자) 다음과 같은 결과를 얻을 수 있습니다. 최초의2행(공백행도 포함한다 ) 은telnet이 보낸HTTP요구 메세지입니다. 그 이후가 서브 렛·엔진(여기에서는Tomcat) 이 돌려준HTTP응답 메세지입니다. 전반이 헤더 부분 , 마지막4행이 보디 부분입니다. 그런데HTTP응답의 헤더 부분을 보면(자) ,Set-Cookie되는 헤더행이2행 존재하는 것이 분 빌리고지요. 처음은 서브 렛이 작성한 것으로 , 후 의 것은 서브 렛·엔진이 세션 유지를 위해 작성한 것입니다. 따라서 , 브라우저로 해 보면 ,2개의 쿠키가 건네받았다고 하는 것이 됩니다. 브라우저측에서 이러한 쿠키에 굳이 변경을 더하지 않으면 , 서브 미트·버튼등으로 이URL을 재차 액세스 했을 때에는 , 이러한 쿠키는 유효기간이 끊어지지 않으면 그대로 서버에 돌려주어집니다.

 

GET /examples/servlet/HelloCookie0 HTTP/1.0

 

HTTP/1.1 200 OK

Content-Type: text/html; charset=Shift_JIS

Connection: close

Date: Wed, 30 Oct 2002 03:45:24 GMT

Server: Apache Tomcat/4.0.4-b3 (HTTP/1.1 Connector)

Set-Cookie: HelloCookie=Hello 일본!;Expires=Wed, 30-Oct-2002 03:50:25 GMT

Set-Cookie: JSESSIONID=BAFB93DD6C7848751C369747B316DB6C;Path=/examples

 

<html><body>

<h1>Write Cookie</h1>

<p>리로드 해 주세요. </p>

</body></html>

 

b)       그런데 이HTTP응답 메세지를 보면(자) ,telnet의 대응문자 세트를Shift_JIS으로 했으므로 , “Hello 일본!”의 부분은 문자가 변하고를 일으키지 말고 올바르게 읽어내지고 있습니다. 그리고 ,IE(v6)http://localhost:8080/examples/servlet/HelloCookie0과 액세스 해 , 여러분의PCC:\WINDOWS\Cookies을 조사하면(자) , 이 쿠키가 문자가 깨지지 않고 수리되고 있는 것이 확인할 수 있겠지요. 그렇지만 , 실은 이것은 , 우연히 잘되었다고 할 뿐(만큼)인 것입니다. 일본이라고 하는 문자는Shift_JIS으로서 헤더에 들어가 있습니다만 , 이 아르바이트열의 어느 아르바이트도ASCII의 「위험한 문자」에 떨어지지 않기 때문입니다. 더욱 , 이 실험에서는 로컬의 호스트를 사용하고 있어 네트워크를 개입시키고는 있지 않습니다. 네트워크의 노드에 따라서는 (낡은 시스템입니다만 )7비트 밖에 전송되지 않고 , 최상정도의1비트(MSB)은 잘못해 검출이나 동기등의 목적으로 사용되고 있는 것도 있습니다. 그러한 노드를 이HTTP메세지가 통과하면(자) , 당연히 문자가 변하고를 일으켜 버립니다.

 

c)        위험한 문자를 포함한 캐릭터 라인이 쿠키의 「값」의 장소에 세트 되면 어떻게 될까요? 쿠키의 「이름」에 위험한1아르바이트 문자가 포함되어 있으면(자) 서브 렛·엔진은 예외를 일으키도록(듯이) 규정되고 있습니다만 , 「값」에는 그러한 제약이 어떤 (뜻)이유일까 규정되고 있지 않습니다. 시험삼아” AAA ;B%BB”과 스페이스와 세미콜론을 포함한 문자를 출력해 보자. telnet에 이 서브 렛을 호출해 보면(자) 다음과 같은HTTP응답 패킷을 관찰할 수가 있습니다.

 

HTTP/1.1 200 OK

Content-Type: text/html; charset=Shift_JIS

Connection: close

Date: Tue, 22 Oct 2002 04:39:25 GMT

Server: Apache Tomcat/4.0.4-b3 (HTTP/1.1 Connector)

Set-Cookie: HelloCookie=Hello AAA;B%BB;Expires=Tue, 22-Oct-2002 04:44:26 GMT

이하 생략

 

이것을 브라우저(IE6)은 어떻게 수중에 넣었는지를Explorer에 보면(자)C:\WINDOWS\Cookies의 디렉토리에 기록되고 있는 파일은 다음과 같은 텍스트가 되어 있습니다. 즉 스페이스는 받아들였지만 세미콜론 이후는 쿠키로서는 받아들이지는 않습니다. 세미콜론은 인터넷의 세계에서는 「단락 문자」인 것입니다.

 

HelloCookie

AAA

localhost/examples/servlet/

1024

1088345728

29522332

2383813024

29522331

*

 

다음의 장으로 자세하게 설명합니다만 ,URL인코딩은MSB1의 아르바이트나 , 인터넷상에서 특별한 의미를 가지는7비트ASCII문자를 ,7비트ASCII문자를 사용해 혼란시키지 않고 보내기 위한 구조인 것입니다.

 

지금까지의 실험으로부터 분 덧없는 세상 게 ,쿠키의 송신에 해당해 이름 , 값과도URLencode 해 보내는 것이 추천 됩니다. 이것은NetScape사의 해설서에서도 추천 되고 있는 것입니다. 또한 서브 렛·엔진이 세션 유지를 위해cookie을 세트 할 때는URLencode 하고 있지는 않은 , 이라고 할까URLencode의 필요가 없는 문자 밖에 사용하고 있지 않습니다 (URL변환해도 어떤 변화도 생기지 않습니다 ). telnet등으로 검사하기 쉬운 (아르바이트인 채에서도 「이름」이 그대로 읽을 수 있다 ) 듯 ,「이름」은URLencode에 걸리지 않는 영수의 캐릭터 라인으로 하는 것이 트러블 방지가 되겠지요.

 

그런데 , 다음과 같이 이름과 값 쌍방을JavaURLEncoder에 encode 해 만든 쿠키는 어떻게 브라우저가 처리할까 조사해 봅시다.

 

      String namestring = "내각총리대신";

      String valuestring = "코이즈미 쥰이치로";

      namestring = URLEncoder.encode(namestring, "Shift_JIS");

      valuestring = URLEncoder.encode(valuestring, "Shift_JIS");

      cookie = new Cookie(namestring, valuestring);

 

그렇다면 ,c:\windows\cookies의 파일을 조사해 보면 다음과 같이 브라우저는URLencode 된 캐릭터 라인을 그대로 받고 있을 뿐이라고 말하는 것을 알 수 있습니다. 이것을 바탕으로 되돌리는 것은 프로그래머의 책임이라고 하는 것입니다.

 

%93%E0%8A%74%91%8D%97%9D%91%E5%90%62

%8F%AC%90%F2%8F%83%88%EA%98%59

localhost/examples/servlet/

1024

3287822464

29522316

294622464

29522316

*

 

Netscape의 생각은 ,「쿠키는1아르바이트 문자로 , 한편 안전한 문자로 구성된 캐릭터 라인이 이름과 값의 페어로서 존재하고 있는 것을 전제로 하고 있다. 그렇지 않은 캐릭터 라인을 그러한 제약에 따라 변환해 사용하는 것은 프로그래머의 책임이다」이라는 것이지요.

 

선두로 돌아오는


 

 

2.        URL인코딩과는

 

어떠한 아르바이트열에서도7비트만의ASCII문자를 사용해 인터넷의 그물을 통과시키기 위한 구조로서의URL인코딩 , 혹은 그 역의URL디코딩에 대해 좀 더 상세하게 이해하기로 하겠습니다.

 

메일(SMTP)이나HTTP등의 패킷은 , 헤더부에 행선지나 그 외 메세지의 제어에 관련되는 정보가 실립니다. 이 헤더는 도중 의 (헤더가 해석된다 ) 게이트 웨이를 몇이나 중계되어 구석에서 구석의 노드에 전달되므로 , 이러한 노드에 이해할 수 있는 코드와 문자로 표현되지 않으면 안됩니다. 헤더부에 일본어와 같은2아르바이트의 문자가 들어가면(자) , 노드는 이것을1아르바이트씩 해석하려고 합니다. 그 때에 그 아르바이트의 어떤 것인가가 노드에 있어 특별한 의미를 가지는 아르바이트이면(자) , 올바른 결과 를 얻을 수 없게 되어 버립니다. 더욱 네트워크에 따라서는 각 아르바이트의 맨 위의 비트(MSB)을 결핍 시키는 전송 노드가 존재합니다. 따라서 어떠한 문자여도 그러한 제약 속에서 안전하게 한편 투과적으로 전송되는 것이 필요하게 됩니다. 구체적으로는7비트로 한편 「안전한」ASCII(American Standard Code for Information Interchange)캐릭터 세트로부터 되는 캐릭터 라인으로 변환해 인터넷을 통한다고 하는 것입니다. 그러한 구조로서URLencode가 생각되었습니다. URLencode라고 하는 것은 , 원래 헤더부의URL부분에2아르바이트 문자나 제어 문자와 혼동하기 쉬운 문자가 들어가는 것을 방지하기 위해서 생각되었기 때문에 그렇게 불리고 있습니다. 그러나 보내지는 정보를 모두 「보인다」캐릭터 라인으로 변환하는 것은 형편이 좋은 일이 많아 , 메세지의 보디 부분의 전달에도 사용됩니다. 보디 부분의 변환에는 또 하나MIME(Multi-Purpose Internet Mail Extensions)의 인코딩이 있습니다. 이것은2아르바이트의 바이너리·데이터를3아르바이트의7비트ASCII문자로 변환하는 것으로 , 멀티미디어 정보의 전송에 사용됩니다.

 

URLencode의 순서는 이하 같습니다.

 

       일본어와 같이2아르바이트의 문자는1아르바이트마다 꺼내 ASCII문자로 간주해 이하의 변환을 실시한다.

       이름과 값에 있는 「안전하지 않다」문자는"%xx"이라고 하는 이스케이프 캐릭터 라인으로 변환한다. "xx"은 그 문자의ASCII치를16진 표시한 것이다. 「안전하지 않다」문자에는=, &, %, +이나 프린트 할 수 없는 문자,MSB(최상정도 비트) (이)가1의 문자를 포함한다.

       모든ASCII의 스페이스 문자를+으로 변환한다.

       이름과 값을=&에 이어 하나의 캐릭터 라인으로 한다. 예를 들면name1=value1&name2=value2&name3=value3

 

이 캐릭터 라인이POST요구 메세지의 보디 부분 , 혹은GET요구의 쿠에리 캐릭터 라인 , 혹은 쿠키의 헤더행으로서는 째 붐비어집니다.

 

선두로 돌아오는

 


 

 

3.        JavaScript에 있어서의escape()unescape()함수

 

드디어 클라이언트(브라우저) 쪽 에 이야기를 옮깁시다. JavaScript으로는 당초는escape()unescape()의 글로벌 함수가 그러한 목적으로 사용되는 일이 있었습니다. 그러나 이러한 함수는 완전한URLencode 대응이 아닌 데다가 , 그 함수의 정의가 도중에 바뀌어 버려 , 추천할 수 없습니다.

 

그렇다면JavaScript에 쿠키를 읽어낼 때 ,URLencode 된 쿠키를 어느 캐릭터 세트라고 이해해 디코드하는 것일까요? Java에 있어도URLEncoderURLDecoder의 두 개의 클래스에 있어 캐릭터 세트(게다가W3C이 권고하고 있다고 해도UTF-8을 추천!) 를 지정하게 되었던 것이 최근의 일 (j2sdk1.4부터 ) 인 것입니다.

 

JavaScript의 언어 사양(escape/unescape)에는 그러한 기능이 존재하지 않는 것이 혼란의 토대가 되고 있는 것 같습니다. 예를 들면escape()메소드는NN(Netscape Navigator)Shift_JIS의 코드를URLencode나무로 ,IE(Internet Explorer)은 Unicode 표기(이스케이프·순서이며UTF은 아니다! ) (을)를 돌려주어 버립니다. 역조작의escape()메소드도 이것에 대응합니다. 그런데 문제를 더욱 복잡하게 하고 있는 것이 ,IEescape()함수는 통상의UTF-16URLencode(예를 들면j2sdk1.4URLEncoder.encode(str, “UTF-16”);) 와는 완전히 다른 단순한 Unicode 표기의 캐릭터 라인을 만들어 낸다 , 라고 하는 것입니다. 예를 들면 다음과 같은htm파일을IE에 열리면(자) ,

<HTML>

<HEAD>

<TITLE>JavaScript escape()/unescape() functionarity test</TITLE>

<META http-equiv="content-type" CONTENT="text/html; charset=SHIFT_JIS">

</TITLE>

<BODY>

<PRE>

JavaScript에 있어서의escape함수의 브라우저에 의한 상위를 체크하는

<SCRIPT LANGUAGE="JavaScript">

  s="내각총리대신=코이즈미 쥰이치로";

  document.writeln("original string  : "+s);

  s=escape(s);

  document.writeln("escaped string   : "+s);

  s=unescape(s);

  document.writeln("unescaped string : "+s);

</SCRIPT>

</PRE>

</BODY>

</HTML>

 

다음과 같은 화면이 표시됩니다.

 

JavaScript에 있어서의escape함수의 브라우저에 의한 상위를 체크하는

original string  : 내각총리대신=코이즈미 쥰이치로

escaped string   : %u5185%u95A3%u7DCF%u7406%u5927%u81E3%3D%u5C0F%u6CC9%u7D14%u4E00%u90CE

unescaped string : 내각총리대신=코이즈미 쥰이치로

 

%uⅴⅴ되는 Unicode의16진 표기를 돌려주고 있는 것이 분 빌리고지요.

 

다음과 같이 ,IEunescape()함수를 사용해 읽어낼 수 없을까

URLEncoder.encode()메소드를 사용해 서버측에서 ,cookie을 세트 했다고 합시다.

 

import java.io.*;

import java.net.*;

import javax.servlet.*;

import javax.servlet.http.*;

/** 쿠키 읽고 쓰기 서브 렛 **/

public class HelloCookie extends HttpServlet {

  public void doGet (HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

    PrintWriter out;

    Cookie[] cookies;

    Cookie cookie;

    String urlencoding = "UTF-16"; //필요하게 응하고"Shift_JIS"이나”UTF-8”등 을 시험하는

    // Cookie의 취득

    cookies = req.getCookies();

    cookie = null;

    if (cookies != null){

      for(int i=0; i < cookies.length; i++) {

        cookie = cookies[i];

        if (cookie.getName().equals("HelloCookie")) { break; }

      }

    }

    res.setContentType("text/html; charset=Shift_JIS");

    out = res.getWriter();

    if (cookie == null) {      // Cookie의 기록

      String namestring = "내각총리대신";

      String valuestring = "코이즈미 쥰이치로";

      namestring = URLEncoder.encode(namestring, urlencoding);

      valuestring = URLEncoder.encode(valuestring, urlencoding);

      cookie = new Cookie(namestring, valuestring);

      cookie.setMaxAge(300);   // 유효기간은5분 에 끊어지는

      res.addCookie(cookie);

      out.println("<html><body>");

      out.println("<h1>Write Cookie</h1>");

      out.println("<P>");

      out.println("<SCRIPT LANGUAGE=\"JavaScript\">");

      out.println("s=unescape(document.cookie);");

      out.println("document.write(\"unescaped cookie : \"+s);");

      out.println("</SCRIPT>");

      out.println("</P>");

      out.println("<p>리로드 해 주세요. </p>");

      out.println("</body></html>");

    } else {      // Cookie의 표시

      out.println("<html><body>");

      out.println("<h1>");

      out.println(URLDecoder.decode(cookie.getValue(), urlencoding));

      out.println("</h1>");

      out.println("<p>Cookie의 샘플(HelloCookie.java)</p>");

      out.println("</body></html>");

    }

  }

}

 

이 때telnet에 액세스 해Tomcat이 송신하는HTTP응답 패킷의Set-Cookie행을 조사하면(자) 다음과 같이 되어 있습니다.

 

Set-Cookie: %FE%FF%51%85%95%A3%7D%CF%74%06%59%27%81%E3=%FE%FF%5C%0F%6C%C9%7D

%14%4E%00%90%CE;Expires=Wed, 23-Oct-2002 04:33:23 GMT

 

즉 「내각총리대신」의 캐릭터 라인은%FE%FF%51%85%95%A3%7D%CF%74%06%59%27%81%E3에 , 「코이즈미 쥰이치로」의 캐릭터 라인은%FE%FF%5C%0F%6C%C9%7D%14%4E%00%90%CE에 변환되고 있습니다. FEFF되는 캐릭터 라인은 이것이BOM(아르바이트순서 마크) 으로 , 빅 endian인 것을 의미합니다. 이것이 정식의UTF-16코드 표시인 것입니다. 「내각총리대신=코이즈미 쥰이치로」라고 하는 캐릭터 라인의 경우는 다음과 같이 변환됩니다.

 

%FE%FF%51%85%95%A3%7D%CF%74%06%59%27%81%E3%00%3D%5C%0F%6C%C9%7D%14%4E%00%90%CE

 

당연한 일이면서 이 서브 렛의 출력을IE unescape()함수는 올바르게 받아들여 주지 않습니다.

 

IE이 만들어 내는 「내각총리대신=코이즈미 쥰이치로」의 인코딩은 조금 전 가리킨 것처럼

단순한 Unicode 표기의

 

%u5185%u95A3%u7DCF%u7406%u5927%u81E3%3D%u5C0F%u6CC9%u7D14%u4E00%u90CE

 

이며 , 이것과는 완전히 차이가 납니다. 게다가”=”이라고 하는 문자는 Unicode 표시되지 말고 단순한%3D이라고 하는ASCII문자로서 처리되어 버리고 있습니다. 어째서%u003D으로 하지 않는 것일까요. ECMA-262에 준거했지만듯입니다만 , 곤란한 것입니다. 따라서 「IE의 경우는 수상한escape()unescape()함수는 사용하지 않는 편이 좋다」라고 하는 것이 결론입니다.

 

덧붙여서 방금전의 서브 렛으로 String urlencoding = "Shift_JIS";이라고 변경해 , 이것을NN에 호출하면(자) 다음과 같이 정상적으로 표시됩니다.


NN으로는 Shift_JISURLencode를 사용하고 있으면 문제가 없는가 하면 유감스럽지만

그렇지는 않습니다. 예를 들면 「내각총리대신 코이즈미 쥰이치로」라고 사이에 반각 스페이스 문자가 들어가 있으면(자)NN은 「내각총리대신+코이즈미 쥰이치로」라고 플러스 기호로 바꾸어 있습니다. 이것은URLencode의 해석의 상위에 의하는 것으로 ,j2sdk1.4으로는 스페이스는”+”문자로 변환하는데 ,NNescape()함수의 경우는 스페이스를%20으로 변환합니다. 세세한 일입니다만j2sdk1.4으로는 총이라고 통일해%nn의 형식에서 변환하는데NNescape()함수의 경우는 아르바이트로 고쳤을 때에 위험한 문자가 아니면 그대로1아르바이트 문자로서 변환합니다. 구체적으로"내각총리대신 코이즈미 쥰이치로"되는 캐릭터 라인의 변환의 상위를 나타내면(자):

 

J2sdk1.4URLEncoder.encode()

%93%E0%8A%74%91%8D%97%9D%91%E5%90%62

+%8F%AC%90%F2%8F%83%88%EA%98%59

NNJavaScriptescape()

%93%E0%8At%91%8D%97%9D%91%E5%90b

%20%8F%AC%90%F2%8F%83%88%EA%98Y

 

이 되고 있습니다. 이 상위는 쌍방에서 흡수됩니다만 , 유감스럽지만NNunescape()함수는 플러스 문자를 스페이스에 되돌리지 않기 때문에 결국 호환성을 취할 수 없게 됩니다.

 

이상로부터JavaScriptescape()unescape()IENN쌍방으로 문제가 있어 ,Web어플리와 같이 서버/IE/NN함께 문제 없고 정보 교환 찌를 수 있으려고 하는 곳 등의 함수는 사용해야 하는 것이 아니다 , 라고 결론 됩니다.

 

선두로 돌아오는

 

 

 


 

4.        JavaScript에 있어서의encodeURI,decodeURI,encodeURIComponent,decodeURIComponent

 

이러한 문제를 해결하려고 , 새롭고4개의 글로벌 함수가 최근 정의되고 있습니다. 이 장에서는 이러한 함수를 조사하고는 있습니다만 , 최근의 버젼의 브라우저 밖에 실장되어 있지 않은 것 ,JavaURL인코딩과의 완전한 호환성이 없는 것으로부터 , 역시 추천은하기 어렵습니다.

 

최근encodeURI,decodeURI,encodeURIComponent,decodeURIComponent4개의 함수가ECMA-262 3rd Edition으로 설정되었습니다. 실장은JavaScript 1.5, NES 6.0부터되고 있습니다.http://developer.netscape.com/docs/manuals/js/core/jsref15/toplev.html#1118346에 그러한 함수의 정의가 이루어지고 있습니다. 거기에 따르면 이러한 함수는UTF-8에 변환하고 있습니다. 다만 이 표와 같이IE5이나NN4으로는 이러한 함수가 실장되어 있지 않기 때문에 사용할 수 없습니다. NN4이나IE5을 사용하고 있는 사람은 아직도 상당 존재하고 있는 것이 현상이며 , 이러한 함수를 사용하는데는 문제가 있습니다.

 

버젼

대표적인 브라우저

비고

1.0

NN2.0 의 도중 ~

IE3.0

JavaScript 의 오리지날의 사양

1.1

NN3

IE3.02+JScript1.3패치

Array object이나 이벤트등의 추가

1.2

NN4.0 ~ 4.05

IE4

Layer/DIV 기능, CSS의 추가

1.3

NN4.06 ~

IE5.0 ~

unicode등 주로 ECMA-262대응

1.4

NN5( 개발 중지 )

catch/try 등 의 예외 처리 등( ECMA-262대응 )

1.5

Mozilla5( NN6 )

IE6~

ECMA-262 3rd Edition

2.0

?

ECMA-262 4th Edition

 

어쨌든 먼저 이러한 함수를 조사해 봅시다. 다음의 두 개의 겉(표)는IE6NN7에 어떠한URL변환이 되는지 실험한 것입니다.

 

encodeURI

원의 캐릭터 라인

IE6의 출력

NN7의 출력

내각총리대신 코이즈미 쥰이치로

%E5%86%85%E9%96%A3%E7%B7

%8F%E7%90%86%E5%A4%A7%E8%87%A3

%20%E5%B0%8F%E6%B3%89%E7

%B4%94%E4%B8%80%E9%83%8E

%E5%86%85%E9%96%A3%E7%B7

%8F%E7%90%86%E5%A4%A7%E8%87%A3

%20%E5%B0%8F%E6%B3%89%E7

%B4%94%E4%B8%80%E9%83%8E

내각총리대신X코이즈미 쥰이치로

%E5%86%85%E9%96%A3%E7%B7

%8F%E7%90%86%E5%A4%A7%E8%87%A3

X%E5%B0%8F%E6%B3%89%E7

%B4%94%E4%B8%80%E9%83%8E

%E5%86%85%E9%96%A3%E7%B7

%8F%E7%90%86%E5%A4%A7%E8%87%A3

X%E5%B0%8F%E6%B3%89%E7

%B4%94%E4%B8%80%E9%83%8E

 

encodeURIComponent

원의 캐릭터 라인

IE6의 출력

NN7의 출력

내각총리대신 코이즈미 쥰이치로

%E5%86%85%E9%96%A3%E7%B7

%8F%E7%90%86%E5%A4%A7%E8%87%A3

%20%E5%B0%8F%E6%B3%89%E7

%B4%94%E4%B8%80%E9%83%8E

%E5%86%85%E9%96%A3%E7%B7

%8F%E7%90%86%E5%A4%A7%E8%87%A3

%20%E5%B0%8F%E6%B3%89%E7

%B4%94%E4%B8%80%E9%83%8E

내각총리대신X코이즈미 쥰이치로

%E5%86%85%E9%96%A3%E7%B7

%8F%E7%90%86%E5%A4%A7%E8%87%A3

X%E5%B0%8F%E6%B3%89%E7

%B4%94%E4%B8%80%E9%83%8E

%E5%86%85%E9%96%A3%E7%B7

%8F%E7%90%86%E5%A4%A7%E8%87%A3

X%E5%B0%8F%E6%B3%89%E7

%B4%94%E4%B8%80%E9%83%8E

 

지금부터 알도록(듯이)NNIE으로는 완전히 같은 결과를 얻을 수 있고 있습니다. 알고리즘은 간단해1아르바이트의 문자는 위험한 문자(이 예에서는” ”) 는%XX형식으로 변환하지만 그렇지 않으면 (이 예에서는”X”) 그 문자를 그대로 출력하는 것입니다. 모두 스페이스는”+”이 아니라”%20”과 변환됩니다.

 

곳에서 참고까지 ,URI으로서의 변환과URIComponent으로서의 변환의 상위는 무엇입니까? 사양에 의하면URI으로서 변환하는 경우는 , 다음의 문자는 변환하지 않지만 ,URIComponent의 경우는 reserved character도 변환의 대상이 되다고 하고 있습니다.

 

카테고리

문자

reserved character

, / ? : @ & = + $ ,

이스케이프 하지 않는 문자

알파벳, 10진수자, - _ . ! ~ * ' ( )

스코아

#

 

따라document.cookie에 꺼낸 캐릭터 라인을 그대로 되돌릴 때는decodeURI()을 사용하게 됩니다. 쿠키의 「값」이나 「이름」을 처리할 때는decodeURIComponent()등 을 사용하다고 하고 있습니다. 이것은JavaScript이 쿠키를 (이름 , 값) 의 오브젝트의 집합이 아니고 , 단순한 캐릭터 라인으로서 취해 급 샀기 때문에 생긴 문제입니다. 이것도 그리고 프로그래머에게 혼란을 주는 요인이 됩니다. 머지않아 그처럼 개정되게 되겠지요.

 

덧붙여서:

 ” %E5%86%85%E9%96%A3%E7%B7%8F%E7%90%86%E5%A4%A7%E8%87%A3%20%E5%B0%8F%E6%B3%89%E7%B4%94%E4%B8%80%E9%83%8E”

되는 캐릭터 라인은 ,decodeURI()decodeURIComponent()도 같다내각총리대신=코이즈미 쥰이치로을 돌려주겠습니다.

 

그렇지만 , 호환성의 문제가 있습니다. 즉j2sdk1.4URLencoder.encode(String,”UTF-8”)이 이스케이프 하는 캐릭터 세트와encodeURI의 이스케이프 하는 캐릭터 세트가 다릅니다. 더욱 ,j2sdk1.4URLencode 한 캐릭터 라인이decodeURI함수에서는 올바르게 읽어낼 수 없게 됩니다.

 

encodeURI함수가 이스케이프 하지 않는 문자는 아래 표 같았습니다.

 

카테고리

문자

reserved character

, / ? : @ & = + $ ,

이스케이프 하지 않는 문자

알파벳, 10진수자, - _ . ! ~ * ' ( )

스코아

#

 

EncodeURIComponent함수 쪽은 이스케이프 하지 않는 캐릭터 라인에는 reserved character가 포함되지 않는 것이 다른 곳(중)입니다.

 

그렇지만 ,j2sdk1.4URLencoder.encode(String,”UTF-8”)이 이스케이프 하지 않는 문자는 아래 표와 같이 되어 있습니다. 적자 한편 기울기 문자의 부분이 이스케이프 하지 않는 문자 , 단 스페이스(SP)”+”에 옮겨놓고 한편 그것은 이스케이프 하지 않는 문자입니다.

 

Char   Decimal    Hex             Char     Decimal    Hex

                                                    

NUL       0        0                  SOH        1        1

STX       2        2                  ETX        3        3

EOT       4        4                  ENQ        5        5

ACK       6        6                  BEL        7        7

BS        8        8                   HT        9        9

NL       10        a                   VT       11        b

NP       12        c                   CR       13        d

SO       14        e                   SI       15        f

DLE      16       10                  DC1       17       11

DC2      18       12                  DC3       19       13

DC4      20       14                  NAK       21       15

SYN      22       16                  ETB       23       17

CAN      24       18                   EM       25       19

SUB      26       1a                  ESC       27       1b

FS       28       1c                   GS       29       1d

RS       30       1e                   US       31       1f

SP       32       20                    !       33       21

"        34       22                    #       35       23

$        36       24                    %       37       25

&        38       26                    '       39       27

(        40       28                    )       41       29

*        42       2a                    +       43       2b

,        44       2c                    -       45       2d

.        46       2e                    /       47       2f

0        48       30                    1       49       31

2        50       32                    3       51       33

4        52       34                    5       53       35

6        54       36                    7       55       37

8        56       38                    9       57       39

:        58       3a                    ;       59       3b

<        60       3c                    =       61       3d

>        62       3e                    ?       63       3f

@        64       40                    A       65       41

B        66       42                    C       67       43

D        68       44                    E       69       45

F        70       46                    G       71       47

H        72       48                    I       73       49

J        74       4a                    K       75       4b

L        76       4c                    M       77       4d

N        78       4e                    O       79       4f

P        80       50                    Q       81       51

R        82       52                    S       83       53

T        84       54                    U       85       55

V        86       56                    W       87       57

X        88       58                    Y       89       59

Z        90       5a                    [       91       5b

\        92       5c                    ]       93       5d

^        94       5e                    _       95       5f

`        96       60                    a       97       61

b        98       62                    c       99       63

d       100       64                    e       101      65

f       102       66                    g       103      67

h       104       68                    i       105      69

j       106       6a                    k       107      6b

l       108       6c                    m       109      6d

n       110       6e                    o       111      6f

p       112       70                    q       113      71

r       114       72                    s       115      73

t       116       74                    u       117      75

v       118       76                    w       119      77

x       120       78                    y       121      79

z       122       7a                    {       123      7b

|       124       7c                    }       125      7d

~       126       7e                  DEL       127      7f

 

따라서 실험 서브 렛의 프로그램으로 ,

      String namestring = "URLencode의 실험";

      String valuestring = " !\"#$%&'()*+"+'\u002c'+"-./01289:;<=>?@ABCXYZ[\\]"+'\u005e'+'\u005f'+'\u0060'+"abcxyz{|}"+'\u007e';

      namestring = URLEncoder.encode(namestring, urlencoding);

      valuestring = URLEncoder.encode(valuestring, urlencoding);

 

으로서'\u0020'부터'\u007e'까지 의 문자가 어떻게URLencode 되어 한편JavaScriptdecodeURI함수가 어떻게 디코드할까를 시험해 보면 다음과 같은 결과를 얻을 수 있습니다.

 

undecoded cookie : URL%E3%82%A8%E3%83%B3%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AE%E5%AE%9F%E9%A8%93

=+%21%22%23%24%25%26%27%28%29*%2B%2C-.%2F01289%3A%3B%3C%3D%3E%3F%40ABCXYZ%5B%5C%5D%5E_%60

abcxyz%7B%7C%7D%7E

decoded cookie : URLencode의 실험=+!"%23%24%%26'()*%2B%2C-.%2F01289%3A%3B<%3D>%3F%40ABCXYZ[\]^_`abcxyz{|}~

 

decodeURI함수는 reserved character와 스코아 문자에의 디코드는 하지 않고 , 그대로 이스케이프 표시를 돌려주어 버리고 있습니다. 실제는encodeURI에 이스케이프 하지 않는 문자에의 디코드총이라고가 비디코드의 대상이 됩니다.

 

「이러한 문자를 사용하지 않는다」라고 하는 것도 하나의 해결법일지도 모르지만 , 너무 좋은 수단이라고는 할 수 없습니다.

 

그러면decodeURI함수를 사용하지 말고decodeURIComponent함수를 사용하면 좋은 것이 아닐까 말씀하시는 (분)편도 있겠지요. 확실히 이쪽의 함수 쪽이 문제를 일으키기 어렵습니다만 ,+을 스페이스에 되돌려 주지 않는 문제는 해결되어 있지 않습니다.

 

그리고 , 쿠키를 일괄 encode 하는 경우는encodeURIComponent함수를 이용해서는 안되는 것도 잊지 말아 주세요.

 

선두로 돌아오는

 

 

 

 

5.        그러면 도대체 어떻게 하면 좋은가?

 

지금까지 산들장도 참 강요하고 설명을 하고 있으면서 , 부정적인 결과 뿐만이 아닌지 , 라는 불만도 지요. 결론으로서는 「Java과 호환성 있는URL인코딩의JavaScript의 함수를 준비한다」라고 하게 됩니다.

 

현재 상태로서는 유감스럽지만encodeURI,decodeURI,encodeURIComponent,decodeURIComponent이라고 하는 함수를 사용할 수 없는 브라우저가 아직도 많이 이용되고 있는 현상인 것 , 이러한 함수가Java 2플랫폼의URLencode와의 호환성이 없는 것을 감안해 ,cookieJavaScript을 사용해 클라이언트/서버간에 정보 교환하는 경우에는 어떻게 하면 좋은 것일까요? 결국 다음과 같은 결론이 됩니다.

 

결론

 

JavaScript이 서버가 세트 한 쿠키를 처리하는 것 같은 어플리케이션에 대해:

1)       쿠키의 이름과 값 모두URLencode 하는 것이 추천 되지만 ,이름은 「위험」이 아닌 (URLencode에 영향을 받지 않는다 ) 문자로부터 되는 영수 캐릭터 라인에 한정해야 합니다. telnet등의 툴에 의한 검증이나 디버그의 변을 배려한 것입니다.

2)       NNIEescape()unescape()함수는 문제가 있으므로 사용해 되지 않습니다. EncodeURI(),decodeURI(),encodeURIComponent(),decodeURIComponent()도 , 낡은 버젼의 브라우저에서는 대응 되어 있지 않고 ,j2sdk1.4과의 호환성의 문제가 있습니다. 결국J2sdk1.4java.net의 패키지에 있는URLEncoder.encode()URLDecoder.decode()과 같은 기능을 가진JavaScript의 함수를 준비하는 것이 베스트입니다. 그러면 자신이 어느 브라우저상의 어느 버젼으로 달리고 있을까를JavaScript은 식별할 필요도 없어집니다.

 

URLencode는 UTF-8에 통일한다. 이것은EncodeURI(),decodeURI(),encodeURIComponent(),decodeURIComponent()UTF-8을 사용하고 있기 때문이라고 하는 이유입니다. 또Java의 사양에 대해도UTF-8을 추천 하고 있습니다. 일본어에서는 지극히 효율이 나쁩니다만 , 이 세계에서는UTF-8을 표준적으로 사용하는 편이 무난하겠지요.

 

선두로 돌아오는

 

 

 

 

 

6.        UTF-8URLencode·디코드 함수의 예:

 

Unicode(1아르바이트 ,2아르바이트장 ,4아르바이트장이 있다 ) 를 외부와 아르바이트열로서 교환하는 형식(Unicode Transfer Format: UTF)에는UTF-8UTF-16이 잘 사용됩니다. 이번은UTF-8에 의한URLencode를 검토하므로 ,UTF-8에의URL변환법을 소개합니다. 이대로 사용할거라고는 말씀드리지 않습니다. 이 프로그램을 참고로 해 주시면 다행입니다.

 

 

UCS부터UTF-8에의 변환법은 다음의 테이블에 나타낸 것처럼 됩니다.

UCS부터UTF-8에의 변환법

UCS-2 (UCS-4)

비트 패턴

제1바이트

제2바이트

제3바이트

제4바이트

U+0000 ..

 U+007F

00000000-0xxxxxxx

0xxxxxxx

 

 

 

U+0080 ..

U+07FF

00000xxx-xxyyyyyy

110xxxxx

10yyyyyy

 

 

U+0800 ..

U+FFFF

xxxxyyyy-yyzzzzzz

1110xxxx

10yyyyyy

10zzzzzz

 

U+10000..

U+1FFFFF

00000000-000wwwxx-

xxxxyyyy-yyzzzzzzz

11110www

10xxxxxx

10yyyyyy

10zzzzzz

 

이 형식의 특징은1아르바이트 문자 이외는 맨 위의 비트(MSB)이 제로가 되지 않는 것으로 , 맨 위의 비트가 제로의 문자는7비트ASCII캐릭터 세트 그 자체라고 하는 것입니다. 따라서 변환의 알고리즘은 지극히 간단합니다. URL변환에 즈음해서는1아르바이트 형식 이외의 형식에서는 반드시 각 아르바이트는%hh과 이스케이프 형식이 됩니다.

 

java.net.URLencoder.encode(String,”UTF-8”)에 상당한 함수를JavaScript에 실현될 때는 , 다음과 같은 알고리즘이 됩니다.

만약 그 문자가'\u0020'라면 , 그것을'\u002b'에 옮겨놓는

  그렇지 않으면 ,

    만약 그 문자가'\u002a','\u002d','\u002e','\u0030'ⅴ'\u0039', '\u0042'ⅴ

'\u005a','\u005f','\u0061'ⅴ'\u007a'(이)가 아니면

      그 문자를UTF-8변환해 , 각 아르바이트를%XX의 이스케이프 캐릭터 라인으로 변환한다

 

java.net.URLdecoder.decode(String,”UTF-8”)에 상당한 함수의 알고리즘은;

만약 그 문자가'+'라면 , 그것을' '에 옮겨놓는

  그 이외의 문자로 ,

만약 그 문자가 이스케이프 캐릭터 라인이라면 이것을UTF-8변환이라고 해 Unicode 문자에 되돌리는

    (그 이외의 문자는 그대로)

과 간단한 것입니다.

 

이하에 그 함수와 실험용HTML을 나타냅니다. 이러한 함수는 장래의 확장4아르바이트장 Unicode(UCS-4)에도 대응하고 있습니다. 이HTML파일을 브라우저로 열리면(자) 다음과 같은 결과를 얻을 수 있겠지요.

JavaScript에 의한J2플랫폼 호환URLencode 함수와 그 테스트

original string    : 내각총리대신 코이즈미 쥰이치로 !"#$%&'()*+,-./01289:;<=>?@ABCXYZ[\]^_`abcxyz{|}~

URL encoded string : %e5%86%85%e9%96%a3%e7%b7%8f%e7%90%86%e5%a4%a7%e8%87%a3+%e5%b0%8f%e6%b3%89%e7%b4%94%e4%b8%80%e9

%83%8e+%21%22%23%24%25%26%27%28%29*%2b%2c-.%2f01289%3a%3b%3c%3d%3e%3f%40ABCXYZ%5b%5c%5d%5e_%60

abcxyz%7b%7c%7d%7e

URL decoded string : 내각총리대신 코이즈미 쥰이치로 !"#$%&'()*+,-./01289:;<=>?@ABCXYZ[\]^_`abcxyz{|}~

 

실제의 어플리케이션에 대해:

아)            웹·서버측은 「값」의 캐릭터 라인을java.net.URLEncoder.encode(String, “UTF-8”)을 사용해

          URLencode 해 쿠키의 「값」에 세트 하는

이)            브라우저는Window.document.cookie에 꺼낸 캐릭터 라인을

(아)   「이름」과「값」에 위험한 문자가 포함되지 않으면 그대로 새로운decodeURL()

         걸치는지 ,

(이)   「값」의 캐릭터 라인을decodeURL()에 걸칠까해 올바른 캐릭터 라인에 되돌리게

         됩니다.

우)             Window.document.cookie을 변경하는 경우는 , 새로운encodeURL()함수를 사용해 역의 조작

           을 합니다. 단Window.document.cookie에 무엇인가 새로운 「이름」의 캐릭터 라인

         을 대입한다고 하는 것은 , 지금까지의 쿠키에 새로운 쿠키가 추가된다

         ( 「이름」이 같으면 그 「값」을 고쳐 쓸 수 있습니다. ) 되는 것에 주의합시다.

 

참고:쿠키중에서 소정의 이름의 값을 추출하는 함수의 예입니다.

  function loadCookie(name) {

        var allcookies = document.cookie;

        if (allcookies == "") return "";

        var start = allcookies.indexOf(name + "=");

        if (start == -1) return "";

        start += name.length + 1;

        var end = allcookies.indexOf(';',start);

        if (end == -1) end = allcookies.length;

        return allcookies.substring(start,end);

  }

 

쿠키의 「값」이URLencode 되고 있는 경우는 , var decodedValue = decodeURL(loadCookie(name)); 과 같이 디코드합니다. 그리고 「이름」이나 「값」에 예약어(reserved word)가 포함되지 않은 것이 뚜렷한 경우는 var allcookies = decodeURL(document.cookie); 이라고 하는 일괄처리의 사용법도 가능합니다.


프로그램예

 

<HTML>

<HEAD>

<TITLE>j2 platform equivalent URL encode/decode functions</TITLE>

<META http-equiv="content-type" CONTENT="text/html; charset=SHIFT_JIS">

</TITLE>

<SCRIPT LANGUAGE="JavaScript">

 

/*  Function Equivalent to java.net.URLEncoder.encode(String, "UTF-8")

    Copyright (C) 2002, Cresc Corp.

    Version: 1.0

*/

function encodeURL(str){

    var s0, i, s, u;

    s0 = "";                // encoded str

    for (i = 0; i < str.length; i++){   // scan the source

        s = str.charAt(i);

        u = str.charCodeAt(i);          // get unicode of the char

        if (s == " "){s0 += "+";}       // SP should be converted to "+"

        else {

            if ( u == 0x2a || u == 0x2d || u == 0x2e || u == 0x5f || ((u >= 0x30) && (u <= 0x39)) || ((u >= 0x41) && (u <= 0x5a)) || ((u >= 0x61) && (u <= 0x7a))){       // check for escape

                s0 = s0 + s;            // don't escape

            }

            else {                  // escape

                if ((u >= 0x0) && (u <= 0x7f)){     // single byte format

                    s = "0"+u.toString(16);

                    s0 += "%"+ s.substr(s.length-2);

                }

                else if (u > 0x1fffff){     // quaternary byte format (extended)

                    s0 += "%" + (oxf0 + ((u & 0x1c0000) >> 18)).toString(16);

                    s0 += "%" + (0x80 + ((u & 0x3f000) >> 12)).toString(16);

                    s0 += "%" + (0x80 + ((u & 0xfc0) >> 6)).toString(16);

                    s0 += "%" + (0x80 + (u & 0x3f)).toString(16);

                }

                else if (u > 0x7ff){        // triple byte format

                    s0 += "%" + (0xe0 + ((u & 0xf000) >> 12)).toString(16);

                    s0 += "%" + (0x80 + ((u & 0xfc0) >> 6)).toString(16);

                    s0 += "%" + (0x80 + (u & 0x3f)).toString(16);

                }

                else {                      // double byte format

                    s0 += "%" + (0xc0 + ((u & 0x7c0) >> 6)).toString(16);

                    s0 += "%" + (0x80 + (u & 0x3f)).toString(16);

                }

            }

        }

    }

    return s0;

}

 

/*  Function Equivalent to java.net.URLDecoder.decode(String, "UTF-8")

    Copyright (C) 2002, Cresc Corp.

    Version: 1.0

*/

function decodeURL(str){

    var s0, i, j, s, ss, u, n, f;

    s0 = "";                // decoded str

    for (i = 0; i < str.length; i++){   // scan the source str

        s = str.charAt(i);

        if (s == "+"){s0 += " ";}       // "+" should be changed to SP

        else {

            if (s != "%"){s0 += s;}     // add an unescaped char

            else{               // escape sequence decoding

                u = 0;          // unicode of the character

                f = 1;          // escape flag, zero means end of this sequence

                while (true) {

                    ss = "";        // local str to parse as int

                        for (j = 0; j < 2; j++ ) {  // get two maximum hex characters for parse

                            sss = str.charAt(++i);

                            if (((sss >= "0") && (sss <= "9")) || ((sss >= "a") && (sss <= "f"))  || ((sss >= "A") && (sss <= "F"))) {

                                ss += sss;      // if hex, add the hex character

                            } else {--i; break;}    // not a hex char., exit the loop

                        }

                    n = parseInt(ss, 16);           // parse the hex str as byte

                    if (n <= 0x7f){u = n; f = 1;}   // single byte format

                    if ((n >= 0xc0) && (n <= 0xdf)){u = n & 0x1f; f = 2;}   // double byte format

                    if ((n >= 0xe0) && (n <= 0xef)){u = n & 0x0f; f = 3;}   // triple byte format

                    if ((n >= 0xf0) && (n <= 0xf7)){u = n & 0x07; f = 4;}   // quaternary byte format (extended)

                    if ((n >= 0x80) && (n <= 0xbf)){u = (u << 6) + (n & 0x3f); --f;}         // not a first, shift and add 6 lower bits

                    if (f <= 1){break;}         // end of the utf byte sequence

                    if (str.charAt(i + 1) == "%"){ i++ ;}                   // test for the next shift byte

                    else {break;}                   // abnormal, format error

                }

            s0 += String.fromCharCode(u);           // add the escaped character

            }

        }

    }

    return s0;

}

</SCRIPT>

</HEAD>

<BODY>

<PRE>

JavaScript에 의한J2플랫폼 호환URLencode 함수와 그 테스트

<SCRIPT LANGUAGE="JavaScript">

    s = "내각총리대신 코이즈미 쥰이치로" + " !\"#$%&'()*+" + '\u002c'+ "-./01289:;<=>?@ABCXYZ[\\]" + '\u005e' + '\u005f' + '\u0060' + "abcxyz{|}" + '\u007e';

    document.writeln("original string    : "+s);

    s = encodeURL(s);

    document.writeln("URL encoded string : "+s);

    s = decodeURL(s);

    document.writeln("URL decoded string : "+s);

</SCRIPT>

</PRE>

</BODY>

</HTML>

 

 

선두로 돌아오는

 

 


 

7.        JSPJavaScript간의 쿠키에 의한 데이터 교환예

 

마지막에 여러분이 실제로 서버의 프로그램이 작성될 때의 참고로서JSPJavaScript간에 텍스트를 쿠키를 개입시켜 교환하는 샘플을 가리키겠습니다.

 

 

JSP을 브라우저가 액세스 하면(자) 다음과 같은 화면을 얻을 수 있을 것입니다.


 


동작은 다음과 같습니다:

1.  최초로 유저는 텍스트 에리어에 임의의 텍스트를 입력해 , 「쿠키 기록과 송신」의 버튼을 누르면(자) , 그 텍스트는URLencode 된 후”userdata”이라고 하는 「이름」의 「값」으로 해서 쿠키에 세트 되고 서버에 보내집니다.

2.  서버는 클라이언트로부터 보내져 온 쿠키의 내용의 모든 것을 먼저 「이름」/「값」세트로서 클라이언트에의HTML텍스트에 기입합니다. 그 때 「값」 쪽 은URL디코드한 것을 괄호로 묶어 함께 기입합니다.

3.  서버는”userdata”이라고 하는 「이름」의 「값」의 부분을URL디코드한 유저로부터의 텍스트를 재차URLencode 해 쿠키에 세트 합니다.

4.  클라이언트 쪽은 , 서버로부터의HTML텍스트를 표시 함과 동시에 , 함께 보내져 온JavaScript에 의해 수신한 쿠키의 내용을 생으로 표시해 , 다음에”userdata”의 「값」의 부분을URL디코드해 표시합니다.

5.  송신한 텍스트와 돌려 보내져 온 텍스트가 일치하면 , 올바르게 교신을 할 수 있던 것이 됩니다.

 

정말로 쿠키가 서버와 클라이언트 쌍방에서 기입하고 있을까 걱정입니까? URLencode 된 쿠키의 「값」을 봐 주세요. 이스케이프 된 문자가JavaScript쪽 은 소문자의16진 표시 ,JSP쪽 은 대문자의16진 표시가 되어 있습니다. 이것은JavaScriptNumber.toString(16)메소드가 소문자로 출력하는데 대해 ,Java 2플랫폼의URLEncoder.encode(String,”UTF-8”)되는 메소드는 대문자로 출력하기 때문입니다. 이것으로 쌍방의URL처리 함수가 기능해 , 한편 호환성이 잡히고 있는 것이 확인됩니다.

 

덧붙여 복귀나 개행도 입력할 수 있기 때문에 시험해 주세요. HTML으로는 이러한 문자는 표시됩니다만 , 올바르고 encode 처리되고 있는 것을 알 수 있습니다. 브라우저의 설정에 따라서는 복귀(CR: %0d)과 개행(NL: %0a)쌍방이 쿠키에 들어가는 경우도 있고 , 개행만의 경우도 있기 때문에 개행 처리에는 주의가 필요합니다.

 

이하에JSP페이지를 소개합니다. JSP속 에JavaScript도 들어가 있으므로 읽기 어려운 곳은 참아 주세요. JSP의 프로그램 부분을 빨강으로 ,JavaScript의 부분을 파랑으로 분류 되어 있습니다. 이 프로그램을 실제로 체험한 후 , 읽어 받는다고 이해가 빠르다고 생각합니다.

<%@ page contentType="text/html; charset=Shift_JIS" session="true"

    import="java.net.*" %>

<%  Cookie[] cookies;

    Cookie cookie;

    cookies = request.getCookies();

    cookie = null;

    if (cookies != null){

        for (int i = 0; i < cookies.length; i++){

            cookie = cookies[i];

            if (cookie.getName().equals("userdata")){break;}

        }

        String encodedUserData = cookie.getValue();

        String decodedUserData = URLDecoder.decode(encodedUserData, "UTF-8");

        cookie = new Cookie("userdata", URLEncoder.encode(decodedUserData, "UTF-8"));

        cookie.setMaxAge(300);  // give 5 minute to survive for the cookie

        response.addCookie(cookie);

    }

%>

 

<HTML>

<HEAD>

<TITLE>JavaScript j2 platform equivalent URL encode/decode functions and their test</TITLE>

<META http-equiv="content-type" CONTENT="text/html; charset=SHIFT_JIS">

</TITLE>

<SCRIPT LANGUAGE="JavaScript">

 

/*  Function Equivalent to URLEncoder.encode(String, "UTF-8")

    Copyright (C) 2002 Cresc Corp.

    Version: 1.0

*/

function encodeURL(str){

    var s0, i, s, u;

    s0 = "";                // encoded str

    for (i = 0; i < str.length; i++){   // scan the source

        s = str.charAt(i);

        u = str.charCodeAt(i);          // get unicode of the char

        if (s == " "){s0 += "+";}       // SP should be converted to "+"

        else {

            if ( u == 0x2a || u == 0x2d || u == 0x2e || u == 0x5f || ((u >= 0x30) && (u <= 0x39)) || ((u >= 0x41) && (u <= 0x5a)) || ((u >= 0x61) && (u <= 0x7a))){     // check for escape

                s0 = s0 + s;           // don't escape

            }

            else {                      // escape

                if ((u >= 0x0) && (u <= 0x7f)){     // single byte format

                    s = "0"+u.toString(16);

                    s0 += "%"+ s.substr(s.length-2);

                }

                else if (u > 0x1fffff){     // quaternary byte format (extended)

                    s0 += "%" + (oxf0 + ((u & 0x1c0000) >> 18)).toString(16);

                    s0 += "%" + (0x80 + ((u & 0x3f000) >> 12)).toString(16);

                    s0 += "%" + (0x80 + ((u & 0xfc0) >> 6)).toString(16);

                    s0 += "%" + (0x80 + (u & 0x3f)).toString(16);

                }

                else if (u > 0x7ff){        // triple byte format

                    s0 += "%" + (0xe0 + ((u & 0xf000) >> 12)).toString(16);

                    s0 += "%" + (0x80 + ((u & 0xfc0) >> 6)).toString(16);

                    s0 += "%" + (0x80 + (u & 0x3f)).toString(16);

                }

                else {                      // double byte format

                    s0 += "%" + (0xc0 + ((u & 0x7c0) >> 6)).toString(16);

                    s0 += "%" + (0x80 + (u & 0x3f)).toString(16);

                }

            }

        }

    }

    return s0;

}

 

/*  Function Equivalent to URLDecoder.decode(String, "UTF-8")

    Copyright (C) 2002 Cresc Corp.

    Version: 1.0

*/

function decodeURL(str){

    var s0, i, j, s, ss, u, n, f;

    s0 = "";                // decoded str

    for (i = 0; i < str.length; i++){   // scan the source str

        s = str.charAt(i);

        if (s == "+"){s0 += " ";}       // "+" should be changed to SP

        else {

            if (s != "%"){s0 += s;}     // add an unescaped char

            else{               // escape sequence decoding

                u = 0;          // unicode of the character

                f = 1;          // escape flag, zero means end of this sequence

                while (true) {

                    ss = "";    // local str to parse as int

                        for (j = 0; j < 2; j++ ) {  // get two maximum hex characters to parse

                            sss = str.charAt(++i);

                            if (((sss >= "0") && (sss <= "9")) || ((sss >= "a") && (sss <= "f"))  || ((sss >= "A") && (sss <= "F"))) {

                                ss += sss;          // if hex, add the hex character

                            } else {--i; break;}    // not a hex char., exit the loop

                        }

                    n = parseInt(ss, 16);           // parse the hex str as byte

                    if (n <= 0x7f){u = n; f = 1;}   // single byte format

                    if ((n >= 0xc0) && (n <= 0xdf)){u = n & 0x1f; f = 2;}   // double byte format

                    if ((n >= 0xe0) && (n <= 0xef)){u = n & 0x0f; f = 3;}   // triple byte format

                    if ((n >= 0xf0) && (n <= 0xf7)){u = n & 0x07; f = 4;}   // quaternary byte format (extended)

                    if ((n >= 0x80) && (n <= 0xbf)){u = (u << 6) + (n & 0x3f); --f;}    // not a first, shift and add 6 lower bits

                    if (f <= 1){break;}             // end of the utf byte sequence

                    if (str.charAt(i + 1) == "%"){ i++ ;}                   // test for the next shift byte

                    else {break;}                   // abnormal, format error

                }

            s0 += String.fromCharCode(u);           // add the escaped character

            }

        }

    }

    return s0;

}

 

/*  Function to get cookie parameter value string with specified name

    Copyright (C) 2002 Cresc Corp.

    Version: 1.0

*/

function loadCookie(name) {

    var allcookies = document.cookie;

    if (allcookies == "") return "";

    var start = allcookies.indexOf(name + "=");

    if (start == -1) return "";

    start += name.length + 1;

    var end = allcookies.indexOf(';',start);

    if (end == -1) end = allcookies.length;

    return decodeURL(allcookies.substring(start,end));

}

 

/*  Function to send the textarea data throuth cookie

    Copyright (C) 2002 Cresc Corp.

    Version: 1.0

*/

function sendThis(){

    document.cookie="userdata="+encodeURL(document.inForm.text.value);  //set data

    window.location.reload();   // and reload this page

}

</SCRIPT>

 

 

</HEAD>

<BODY>

JavaScript에 의한J2플랫폼 호환URLencode 함수와JSP에 의한 그 테스트<BR>

(URLencode 된 쿠키의 파라미터를 개입시킨 서브 렛과JavaScript사이의 데이터의 교환)<BR><BR>

 

<% if (cookies != null){ %>

<DIV STYLE="width: 50% word-break:break-all">

서버가 받은 쿠키(URL디코드 후):<BR><%

        for (int i = 0; i < cookies.length; i++){ %>

이름=<%=    cookies[i].getName() %><BR>

=<%=      cookies[i].getValue() %><BR>

  (<%=     URLDecoder.decode(cookies[i].getValue(), "UTF-8") %>)<BR>

<%      }

    } %>

</DIV>

<P STYLE="width: 50%">

클라이언트가 받은 쿠키(URLencode 되고 있다 ):<BR>

<SCRIPT LANGUAGE="JavaScript">

    document.writeln(document.cookie);

</SCRIPT>

</P>

<P STYLE="width: 50%">

클라이언트 수신한 쿠키안의 데이터 부분:<BR>

<SCRIPT LANGUAGE="JavaScript">

    document.writeln(loadCookie("userdata"));

</SCRIPT>

</P>

서버에 송신하는 텍스트를 기입해 주세요:<BR>

<FORM NAME="inForm">

<TEXTAREA ROWS="3" COLS="60" WRAP="soft" NAME="text"></TEXTAREA><BR>

<INPUT TYPE="button" VALUE="쿠키 기록과 송신" onCLICK="sendThis()">

</FORM>

 

</BODY>

</HTML>