Paluu utf8 sivulle
Alkuperäinen vastaukseni muropaketissa
Tässä temput jotka otettava huomioon UTF-8 tuen kanssa. Unicode vaatii tarkkuutta MySql,
sovelluspalvelin ja clienttiin lähetettävän html sivujen kanssa. Itse olen aikoinaan kivuliaasti
opiskellut mitä tarkalleen tapahtuu eri tasoilla, jotta saan mahdolliset ongelmat korjattua.
Tässä lyhyt oppimäärä.
MySQL tietokannan perustaminen
Ei luoteta serverin ja tietokannan oletus charsettiin, vaan kerrotaan se taulukohtaisesti.
Älä luota oletuksiin, vaikka itse olisit luonut tietokannat.
-- perusta tietokanta utf-8 oletuksella
CREATE DATABASE mydb DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_swedish_ci;
-- perusta taulu utf-8 oletuksella
CREATE TABLE tMyTable (
id int(11) NOT NULL auto_increment,
code VARCHAR(20) NOT NULL,
name VARCHAR(20) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_swedish_ci;
JDBC tietokantaurlin määritys
Mysql ajurin (com.mysql.jdbc.Driver) urliin määritetään unicoden käyttö, jotta varmasti päristin
tietää mitä tekee.
jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8
Tomcatin mywebapp/META-INF/context.xml tiedostoon jdbcpool määritys
Määritetään jdbcpool resurssin käyttö webapplikaation context.xml tiedostoon. Voit optimoida min-max
yhteyksien määrää. Yleensä ei kovin montaa yhtäaikaista tarvitse koska DAO-luokat eivät pidä
sqlconnection oliota kovin kauan varattuna.
<Resource name="jdbc/mydb" auth="Container" type="javax.sql.DataSource"
maxActive="10" maxIdle="2" maxWait="10000"
username="myuid" password="mypwd"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8"
validationQuery="SELECT 1"
/>
Tähän asti kaikkia on vielä helppoa, mutta sitten päästäänkin hauskaan osuuteen eli sovelluspalvelimen
ja html-selaimen yhteistyöhön. Siihen liittyy paljon salatiedettä, jos ei ole asioihin tarkkaan perehtynyt.
Tomcat sovelluspalvelimet T4, T5 ja T6
Alkusanoina mainittakoon Tomcat4.x ja Tomcat5.x toimivat eri tavalla GET moodin postauksessa mitä
tulee charsetin käyttöön. Tomcat4.x versiossa GET ja POST mooodin url-parametrit muutetaan
tavukoodista merkkijonoksi requestista löytyvällä charset arvolla. Uudemmassa Tomcat5.x lähtien
oletuksena requestin charset arvo käytetään vain POST moodin tiedoille ja GET moodin parametrit
muutetaan ISO-8859-1 charsetilla.
Requestin charset tulee joko requestin otsikkokentässä Content-Type: text/plain; charset=UTF-8
tai servletin alussa asetetaan manuaalisesti request.setCharacterEncoding("UTF-8") metodilla.
Mutta T5 tapauksessa request.setCharacterEncoding metodi vaikuttaa vain post moodin formikenttiin
eikä get moodissa tulevin joihin se käyttää aina ISO-8859-1 merkistöä.
Enabloitava connectoriin useBodyEncodingForURI parametri conf/server.xml tiedostossa, jos haluaa
muuttaa T5/T6 version toiminnan samanlaiseksi kuin T4 versiossa. Tätä ei tarvitse tehdä,
jos htm-sivun formit lähetetään aina post moodissa. Muutoksen jälkeen
setCharacterEncoding metodi
vaikuttaa sekä post ja get moodin parametritietoihin. Itse lisään parametrin kaikkiin Tomcat asennuksiin.
<!-- non-SSL Coyote HTTP/1.1 Connector -->
<Connector port="8080"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" redirectPort="8443" acceptCount="100"
debug="0" connectionTimeout="20000"
disableUploadTimeout="true"
useBodyEncodingForURI="true"
/>
Servletin ja .jsp sivun alussa kerrotaan charset
Servletin alussa asetetaan charset, koska se yleensä puuttuu requestin otsikkotiedoista. Nykyiset
selaimet ovat bugisia ja eivät kerro charset attribuutilla missä muodossa parametrit
lähetettiin selaimesta serveriin. Suckers.
Servletin alussa asetetaan charset, jota selaimen postaus (get tai post) on käyttänyt. Se on
kerrottava ennen kuin luet mitään tietoja requestista, tai saattaa olla jo myöhäistä.
Tässä on huomioitava mahdolliset filtterit, jotka esiprosessoivat requestia ennen omaa servlettikoodia.
Silloin se on jo myöhäistä asettaa täällä ja saat parametrien arvot väärin. Charset on asetettava
filtterin alussa, jotta toimisi oikein koko putken läpi.
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException {
doPost(req, res);
}
public void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException {
// set utf-8 charset
if (req.getCharacterEncoding() == null)
req.setCharacterEncoding("UTF-8");
String value = request.getParameter("fieldName");
...
}
Sitten .jsp sivujen kanssa on oltava erityisen tarkkana mitä tekee, koska viattomat välilyönnit ja
rivinvaihdot saattava pilata hyvän yrityksen. Alla esimerkki miten .jsp sivun otsikkotiedot pitää
kirjoittaa, huomaa miten turhat whitespace merkit poistettu alusta. Blokkien <%.... ja ....%>
lopetusmerkit ovat peräkkäin ilman rivinvaihtoa, jolloin ei whitespace merkkejä generoidu liian aikaisin.
contentType arvo laitetaan responsen header tietoihin ja pageEncoding kertoo kääntäjälle .jsp
tekstitiedoston levypinnan talletusformaatin.
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="ISO-8859-1"
import="java.util.*, java.io.*"
%><%
request.setCharacterEncoding("UTF-8");
String myvalue = "hello all and ÅÄÖ";
String param = request.getParameter("fieldName");
myvalue += " " + param;
%>
<html>
<head>
<title>Page Title</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="keywords" content="some,fine,keywords" />
</head>
<body>
your html content goes here ja sama suomeksi ABCÅÄÖ.... <%= myvalue %>
</body>
</html>
Servlet taglib kirjaston käyttö .jsp sivussa
Jos käytät taglib kirjastoja .jsp sivulla, kirjoita sivun alkuun tiedot seuraavasti.
Huomaa miten viattomat whitespace merkit vältetään kirjoittamalla <%|%> tagit tarkasti
ilman turha välimerkkejä.
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><%@
taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml" %><%@
page contentType="text/html; charset=UTF-8" pageEncoding="ISO-8859-1"
import="java.util.*,
java.io.*
"
%><%
request.setCharacterEncoding("UTF-8");
String myvalue = "hello all and ÅÄÖ";
String param = request.getParameter("fieldName");
myvalue += " " + param;
%>
<html>
<head>
<title>Page Title</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="keywords" content="some,fine,keywords" />
</head>
<body>
your html content goes here.... <%= myvalue %>
</body>
</html>
XML dokumentin generointi .jsp sivulla
XML-dokumentin generointi jsp-sivusta näyttäisi tältä. Huomaa miten xml dokumentin otsikkorivi
on samalla rivilla %> merkin kanssa jotta ei tule "viatonta" whitespace merkkiä alkuun.
XML parserit ovat tarkkoja ettei otsikkorivin alussa ole ylimääräistä merkkiä.
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><%@
page contentType="text/xml; charset=UTF-8" pageEncoding="ISO-8859-1"
import="java.util.*,
java.io.*
"
%><%
// MyBean has getId() and getName() getters
List<MyBean> items = new ArrayList<MyBean>();
items.add( new MyBean(1, "first") );
items.add( new MyBean(2, "second") );
items.add( new MyBean(3, "third") );
// publish variables to JSTL scope
pageContext.setAttribute("items", items);
%><?xml version="1.0" encoding="UTF-8"?>
<mydoc>
<c:forEach var="item" items="${items}">
<item>
<id>${item.id}</id>
<name>${item.name}</name>
</item>
</c:forEach>
</mydoc>
Selainten charset kikkailu
Jep, eli selaimet ovat bugisia tästä oletustilanteesta on valitettavasti aina lähdettävä liikkeelle.
Ne eivät kerro postauksen (get tai post) otsikkotiedoissa mitä charsettia url- tai formiparametrien
bytesToString konversiossa olisi käytettävä. Sinun kooderina on se vain tiedettävä sivukohtaisesti.
Jos html-sivun responsen headeriarvosta löytyy Content-Type: text/html; charset=UTF-8 tai
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> sivun alussa kertoo charsetin,
tulee selain käyttämään mainittua charset arvoa formin kenttien postaukseen.
Selaimet käyttävät samaa charsettia sekä POST ja GET moodin formipostaukseen, tämän takia suosittelen
Tomcat5/6 serverin konfigurointiin aiemmin mainittua useBodyEncodingForURI="true" lisäystä.
Mutta jos kirjoitat käsin osoitekentän urlin, selain saattaa lähettää osoitteen
ISO-8859-1 charsetilla. Joudut käsin kirjoittamaan valmiiksi eskapoidun urlin tai selaimen
asetuksista laittamaan oletuscharsetin utf8 tilaan.
ISO-8859-1 eskapoitu "ABC ÅÄÖ" tieto: ?key=ABC%20%C5%C4%D6
UTF-8 eskapoitu "ABC ÅÄÖ" tieto: ?key=ABC%20%C3%85%C3%84%C3%96
Huom! välilyönnin voit eskapoida joko + merkillä tai %20 hexaeskapoinnilla.
Huom! tämän selainten ominaisuuden takia Tomcat5.x versioissa request.setCharacterEncoding("UTF-8")
metodi oletuksena vaikuttaa vain POST moodin kenttiin eikä GET moodiin. Se olettaa, että
get moodin tiedot ovat "käsin syötettyjä" tietoja ja niille on parempi käyttää ISO-8859-1 charsettia.
Minun mielestäni Tomcat5/6 olettavat väärin, nyt pitäisi jo olla kaikki sovellukset unicode kansalaisia.
Tämän yksityiskohdan takia itse suosittelen aiemmin mainittua useBodyEncodingForURI käyttöä
Tomcat connectorien server.xml asetuksissa. Sitten html-sivujen formien get tai post moodi toimivat
oikein. Käsin syötetty urli toimivat kunhan siihen syöttää turvallisia us-ascii merkkejä tai osaa
eskapoida valmiiksi utf-8 muodossa merkit.
Itselleni nämä ovat jo helppoa kauraa ja olen vuosia tehnyt sovellukset UTF-8 enabloituna clientista
tietokantaan saakka. Ei tarvitse välittää vaikka mitä viidakkomerkkejä käyttäjät syöttävät kenttiin
ja aina toimii. Kaikki sovelluksen konffifilutkin talletan UTF-8 formaatissa.
edit 1 (Hezauruksen kommentti):
mysql kannan collation määrityksen tarkennus.
utf8_general_ci määritys sorttaa ääkköset sekä isot/pienet kirjaimet väärin. Se on globaalin sovelluksen
kompromissi jos ei muuta kollaatiota voi käyttää. Käyttämällä
utf8_swedish_ci asetusta toimivat mysql lajittelut meidän näkökulmasta oikein.
edit 2 (Tronic kommentti):
Osa selaimista osaa lähettää osoitekenttään suoraan kirjoitetut merkit valmiiksi utf-8 eskapoituna,
tai pystyy erikseen asettamaan päälle selaimesta.