UTF-8で文字の送信例

## はじめに
文字データがフロントからバックエンドでどう処理されるのかを確認しました。
HTMLで<meta charset="utf-8">を記載し、UTF-8でデータを送信する。
外字を含めています。
バックエンドは何でもいいですが、今回はJavaを使用しています。

## 内容
以下を記載しています。
送信時はUTF-8エンコーディングされたデータがURLエンコードされた形式で送信
サーバー側でリクエストデータをUTF-8としてデコードし処理

普段そのようなことを意識せずともいいですが、イレギュラーの場合、このことを意識しないと対応できない場合がありました。


## 確認
外字含めた文字を送信します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/c7b0132f-90d4-1488-7ac5-cb3a79b096b4.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/3be2f299-8cb2-34ba-208b-caa0406a7658.png)

変な文字は#入力で外字に割り当てて表示させています。

jsで入力時、変換させています。
```js
 const input = document.getElementById('inputText');

input.addEventListener('input', () => {
    // 入力値を取得
    let value = input.value;

	value.split('').forEach((char) => {
		console.log(char, '->', char.charCodeAt(0).toString(16).toUpperCase());
	});

    // # を U+E000 に変換
    value = value.replace(/#/g, '\uE000');

    // 入力欄に変換後の値をセット
    input.value = value;
});

```

フロントは以下で行っています。
```html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>外字入力テスト</title>
    <style>
        @font-face {
            font-family: 'GaijiFont';
            src: url('./fonts/fontTest.woff') format('woff'); /* 外字フォントを指定 */
        }
        .gaiji-input {
            font-family: 'GaijiFont', sans-serif; /* 外字フォントを適用 */
            font-size: 24px;
        }
    </style>
</head>
<body>
    <h1>外字入力テスト</h1>
    <form action="/GaijiFontProject/SubmitFormServlet" method="post" accept-charset="UTF-8">
        <input type="text" name="inputText" id="inputText" class="gaiji-input" placeholder="ここに#を入力してください">
        <button type="submit">送信</button>
    </form>
<script src="js/script.js"></script>
</body>
</html>

```

### 1. **フロントエンドからの送信データの確認**
画像からわかるように、フォームデータ（`inputText`）は **URLエンコード形式**（パーセントエンコーディング）で送信されています。

以下のような値がサーバーに送信されています：
- `inputText=%E3%81%82%EE%80%80`

これは、UTF-8エンコーディングされたデータがURLエンコードされた形式です。

#### URLエンコードとは
- 非ASCII文字や特殊文字を送信する際に、安全に伝送するためのエンコード形式です。
- 例えば、「あ」 は UTF-8 でエンコードすると `E3 81 82` となり、URLエンコード形式では `%E3%81%82` になります。


以下では、このデータをバックエンド（Javaサーブレット）でどのように処理するのかを説明します。

### 2. **Javaサーバーでの処理方法**

#### **フォームデータの取得**
フォームデータを扱う場合は、`request.getParameter("inputText")` を使用します。
サーブレットはリクエストデータをデコードして元の値（`あ` など）を取得します。
以下のようなコードを使用します：

```java
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // リクエストのエンコーディングをUTF-8に指定
    request.setCharacterEncoding("UTF-8");

    // フォームデータを取得
    String inputText = request.getParameter("inputText");

    // データをデバッグ用に表示
    System.out.println("受信したデータ: " + inputText);

    // クライアントに応答
    response.setContentType("text/html; charset=UTF-8");
    response.getWriter().write("<p>受信したデータ: " + inputText + "</p>");
}
```

#### **データの流れ**
1. **クライアント側**:
   - UTF-8でエンコードされたデータをURLエンコード形式で送信。
   - 例：`あ` → `%E3%81%82%EE%80%80`

2. **サーバー側**:
   - `request.setCharacterEncoding("UTF-8")` を指定すると、サーバーはリクエストデータをUTF-8としてデコードします。
   - `request.getParameter("inputText")` を使用すると、自動的にデコードされ、元の値（`あ`）が取得できます。


### 3. **デバッグ: 生データを確認する**
URLエンコードされたデータを直接確認したい場合は、`request.getReader()` を使用してリクエストボディを読み取ります。(同時に `request.getParameter()` は使えません。)

以下は生データを確認するコードです：

```java
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // リクエストのエンコーディングをUTF-8に指定
    request.setCharacterEncoding("UTF-8");

    // 生データをデバッグ用に表示
    BufferedReader reader = request.getReader();
    StringBuilder rawData = new StringBuilder();
    String line;
    while ((line = reader.readLine()) != null) {
        rawData.append(line);
    }
    System.out.println("受信した生データ: " + rawData.toString());

    // クライアントに応答
    response.setContentType("text/html; charset=UTF-8");
    response.getWriter().write("<p>受信した生データ: " + rawData.toString() + "</p>");
}
```

#### **結果例**
- 生データとして取得される内容:
  ```
  inputText=%E3%81%82%EE%80%80
  ```
- このデータは、必要に応じて `URLDecoder.decode()` を使用してデコードできます。

---

### 4. **手動でデコードする方法**
もし `request.getReader()` で取得した生データを手動でデコードする必要がある場合、以下のようにします：

```java
import java.net.URLDecoder;

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // 生データを読み取る
    BufferedReader reader = request.getReader();
    StringBuilder rawData = new StringBuilder();
    String line;
    while ((line = reader.readLine()) != null) {
        rawData.append(line);
    }
    String encodedData = rawData.toString();
    System.out.println("生データ: " + encodedData);

    // デコード
    String decodedData = URLDecoder.decode(encodedData, "UTF-8");
    System.out.println("デコードされたデータ: " + decodedData);

    // クライアントに応答
    response.setContentType("text/html; charset=UTF-8");
    response.getWriter().write("<p>デコードされたデータ: " + decodedData + "</p>");
}
```

#### **結果例**
- 入力データ：`inputText=%E3%81%82%EE%80%80`
- デコード結果：`inputText=あ`

---

### 5. **問題が起きる場合の確認事項**

- **エンコーディングが一致しているか**:
  - クライアント側のHTMLで `meta charset="UTF-8"` を指定していることを確認します。
  - フォームに `accept-charset="UTF-8"` を指定します。

- **Javaのエンコーディング設定**:
  - `request.setCharacterEncoding("UTF-8")` が適切に設定されているか確認します。


### データ表示確認
サーバーで受け取った値をそのまま出しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176603/b813f092-7d5a-ff52-1f55-a65b7b634572.png)

```java
import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/SubmitFormServlet")
public class SubmitFormServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // リクエストのエンコーディングをUTF-8に指定
        request.setCharacterEncoding("UTF-8");

        // フォームデータを取得
        String inputText = request.getParameter("inputText");

        // デバッグ用: サーバー側で取得したデータをコンソールに表示
        System.out.println("受信したデータ: " + inputText);

        // HTMLページとしてクライアントに返す
        response.setContentType("text/html; charset=UTF-8");
        response.getWriter().write(
            "<!DOCTYPE html>" +
            "<html lang='ja'>" +
            "<head>" +
            "    <meta charset='UTF-8'>" +
            "    <meta name='viewport' content='width=device-width, initial-scale=1.0'>" +
            "    <title>外字結果</title>" +
            "    <style>" +
            "        @font-face {" +
            "            font-family: 'GaijiFont';" +
            "            src: url('./fonts/fontTest.woff') format('woff');" +
            "        }" +
            "        .gaiji-result {" +
            "            font-family: 'GaijiFont', sans-serif;" +
            "            font-size: 24px;" +
            "        }" +
            "    </style>" +
            "</head>" +
            "<body>" +
            "    <h1>外字送信結果</h1>" +
            "    <p class='gaiji-result'>サーバーが受信したデータ: " + inputText + "</p>" +
            "    <a href='/GaijiFontProject/index.html'>戻る</a>" +
            "</body>" +
            "</html>"
        );
    }
}


```