aynı formu birden fazla submit etme, ya da back yapıp aynı formu tekrar submit etme gibi sorunları engellemek için struts'ta token kullandım. Bunun için google yaptiginizda back tuşunu engellemek gibi garip bir seçenek hakkında yogun bir tartisma görürsünüz. Fakat back yapmanın binbir yolu olduğundan ve kullanıcıyı kızdırmamak için, back işlevselliğine bulaşmamak daha iyi diye düşündüm. Ama yine de back tuşunu engelleme kodunu yazayım buraya. Bu kod ayrıca, input text alanı ya da textarea alanı içindeyken back tuşu işlevselliğini geri veriyor. Yani bu alanlarda back'i engellemiyor.
if (typeof window.event == 'undefined'){
document.onkeypress = function(e){
var fieldVal = e.target.nodeName.toUpperCase();
if (e.target.type)
var fieldType = e.target.type.toUpperCase();
if ((fieldVal == 'INPUT' && fieldType == 'TEXT') || fieldVal == 'TEXTAREA'){
return e.keyCode;
} else if (e.keyCode == 8) {
e.preventDefault();
}
}
} else {
document.onkeydown = function(){
var fieldVal = event.srcElement.tagName.toUpperCase();
if (event.srcElement.type)
var fieldType = event.srcElement.type.toUpperCase();
if ((fieldVal == 'INPUT' && fieldType == 'TEXT') || fieldVal == 'TEXTAREA'){
return event.keyCode;
}else if (event.keyCode == 8){
event.returnValue=false;
}
}
}
Bir diğer seçenek de location replace yapıp history'ye atılan değeri manipüle etmekti. Ama o da benim için uygun bir çözüm değildi.
Ve gelelim token konumuza.
Ders: Struts
Konu: Token
Token için kullanılan üç adet method var. saveToken(), isTokenValid() ve resetToken()
Forma gitmeden önce çağrılan metoda saveToken()'ı ekliyoruz. Böylece session'a unique bir key koymuş oluyoruz.
açılan jsp'yi view source yaparsak şuna benzer birşey görürüz:
<div><input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="e1473d05ed6b8a3ddf22844f94776dd2"></div>
Formu submit'inde çağrılan metoda da isTokenValid() i koyuyoruz. isTokenValid yaptığımız zaman, Submit ile formdan gelen key'i, session'daki key ile karşılaştırıyor ve form'un expire olup olmadığını anlıyor.
Son olarak, işlemimizin başarılı olduğu case'ler için resetToken çağırıyoruz ki yeni bir key oluştursun ve eski key expire olmuş olsun. Böylece, aynı form submit edildiğinde key'ler uyuşmadığından hata alınacak ve ikinci kez aynı işlemin yapılması engellenmiş olacak.
Eğer işlem başarılı olmamışsa yani hata alınmışsa resetToken'ı çağırmıyoruz, böylece mevcut key hala geçerli oluyor. Hata nedeniyle kullanıcı aynı formu tekrar submit etmek istediğinde key uyumsuzluğu yaşamıyor.
Hakkımda
- Mahir Tarlan
- Istanbul, TR
Wednesday, 11 February 2009
struts token kullanımı
Wednesday, 25 June 2008
struts html:form'da dinamik action degistirme
ekran açilirlen c:if'lerle action degistirmede sorunla karsilastim. nesting error veriyordu çünkü c:if'in html:form'un kapatilmasindan sonra kapatilmis olmasini istiyordu.
html:form yerine html-el:form kullanip bir parametreye action degerini tanimladiktan sonra bu parametreyi action olarak kullanarak sorunu çözdüm. Bu da burada bir kösede dursun. Tekrar karsilasabilecegim bir durum gibi hissettim bir an.
<c:set var="PARAM_BIR" value="1"/>
<c:set var="PARAM_IKI" value="2"/>
<c:if test="${sessionScope.paramAdi eq PARAM_BIR}">
<c:set var="formAction" value="/birAction.do"/>
</c:if>
<c:if test="${sessionScope.paramAdi eq PARAM_IKI}">
<c:set var="formAction" value="/ikiAction.do"/>
</c:if>
<html-el:form action="${formAction}" method="post" >
burada eger farkli action'lara gitme ihtiyaci yoksa dispatchAction ya da mappingDispatchAction kullanarak tek action degeriyle çözüm getirilebilir. sonra yollanan parameter degeri degistirilerek farkli metodlara gitme saglanabilir. ama bu benim için çözüm degildi. struts-config'de farkli action'lara gitmem gerektiginden böyle bir çözüm gerekti.
Thursday, 29 November 2007
struts validation
Server side struts validation'ı çalıştırmaya çalışırken gözden kaçan ufak ama çok zaman alan hatalarla yüzleştim. Bunları not almakta yarar görüyorum. Server side validation'ın bana uygun olmadığını düşünerek client side validation'a döndürdüm.
Bunları da not alalım.
validator-rules'ta zaten tanımlı rule'lar geliyor. Bunlar dışında pek bir rule'a ihtiyaç olacağını sanmıyorum. Ama ihtiyaç olduğu sürece custom rule'lar yazılma imkanı var.
validation.xml'de ise asıl validation adımlarını belirtiyoruz. Burada validator-rules'ta tanımlanmış olan rule'lara depend ediyoruz.
En basitinden required bir field için eklenen kısımı örnek verelim:
<form-validation>
<formset>
<form name="xxxForm">
<field property="zipCode"
depends="required" >
<arg0 key="title.address.adr4"/>
</field>
</form>
</formset>
</form-validation>
validator-rules.xml'deki kullanılmayacak rule'ları temizlemekte yarar var. Buna sonra da değineceğim. validation.xml ve validator-rules.xml'le işimiz bittikten sonra bu xml'lerdeki msg'ları resource properties dosyamıza eklememiz gerekiyor. validator-rules.xml'de msg tag'iyle kullanılan mesajlar içindeki argümanlar da validation.xml'de arg tag'iyla, birden fazla varsa da arg0, arg1 gibi taglerle belirtiliyor. Hem msg hem de arg içindeki key'lerin resource properties dosyamızda tanımlı olmasına dikkat etmeliyiz.
struts-config.xml içinde tanımlanmış olan form ismini validation.xml'de kullanmak gerekiyor.
Sırada, bu validation'ın kullanılacağı ekranda yapılacak değişiklikler var. Öncelikle jsp'nin head kısmına şu bilgiyi eklemeliyiz.
<html:javascript formName="xxxForm"/>
Aynı ekranımızın onSubmit'ine de aşağıdaki gibi js fonksiyon ismini ekliyoruz. Buradaki ismin struts-config'de tanımlı olan form ismiyle aynı olduğuna dikkat edilmeli. Eğer struts-config'de bu form ismi küçük harfle tanımlanmışsa burada büyük harfle başlaması gerektiğini de belirtelim.
<html:form method="post" action="/xxxAction.do" onsubmit="return validateXxxForm(this)">
Bu değişiklikler yapıldıktan sonra client side validation'ın çalışması gerekiyor. Bunu server side'a çevirmek için ise şu değişiklikler gerekiyor.
Öncelikle form'umuzun ActionForm'a değil de ValidatorForm'a extend etmesi sağlanmalı.
Ardından struts-config'deki action mapping'imiz içinde bir input tanımlamalıyız.
<action path="/xxxAction"
type="com.xxx.action.XxxAction"
name="xxxForm"
scope="request"
input="/jsp/xxx/xxxMain.jsp">
validate'i de true set ettiğimiz zaman validation'dan geçemeyen ekranımız input'ta tanımlı olan sayfaya düşecektir.
Akış da şöyle: validate false set edilmediği zaman Struts validate metodunu çağırır. Validate'ten ActionErrors objesi döner. Eğer bu obje null değilse requesti input'ta tanımlı olan ekrana yönlendirir. Eğer ActionErrors objesi null gelmişse Struts validation'ı tamamlar ve execute metoduna devam eder.
Eğer ekran açılışını da aynı action mapping üzerinden yapıyorsa ekran açılırken de validation yapmaya çalışacaktır ve büyük ihtimalle -validation kurallarının nasıl olduğuna bağlı olarak tabi- sürekli input'a düşecektir. O nedenle, server side validasyon için ekran açilisindaki action path'i ile formu submit ettigimizde gitmesini istedigimiz action path'i ayri ayri tanimlamaliyiz.
Client side validation'a dönecek olursak, yukarıda bahsettiğim validator-rules'ta kullanılmayacak kuralların silinmesinin nedeni, client side validation kullanılan ekran açıldığında view source yaparsanız ortaya çıkıyor. Validator-rules'ta tanımlı kuralların ekran tarafında javascript olarak oluşturulduğunu ve tutulduğunu göreceksiniz.
Monday, 23 July 2007
Struts Action .. DispatchAction .. LookupDispatchAction ..
Struts'ta servlet'lerimizi Action'a extend ederek execute metodu içinde tüm aksiyonumuzu alırız. Birçok işlem için aynı action'a geldiğimiz olur. Bu durumda execute içinden ilgili durumlarda ilgili metodlara yönlendirme yaparız.
Bu yönlendirmenin struts tarafından yapılmasını DispatchAction sayesinde sağlayabiliriz. Şöyle ki, müşteri bilgileri ekranımız olsun ve bu ekrandan müşterinin bilgilerini güncelleyebilelim, yeni müşteri ekleyebilelim ve ekranda görünen mevcut müşteriyi silebilelim. Bu aksiyonların her biri için bir action yazıp her bir action'a gidecek birer buton koyabiliriz. Ya da bir action yazıp execute içinden ilgili işlem için ilgili metodun çağrılmasını sağlayabiliriz. Ama bir parametre yollasak ve action bu işlem için yönlendirmeyi kendisi yapsa daha güzel olmaz mı? Burada yapmamız gereken ufak birkaç şey var. Action'ımızı DispatchAction'a extend edip struts-config'de tanımladığımız parametreyi, gitmesini istediğimiz metod ismine jsp tarafında set edeceğiz. Submit ediyorsak bu parametreyi hidden bir input değeri olarak action'a yollayacağız. Url ile ekranın location'ını set ediyorsak aynı parametre ismiyle URL sonuna bu değeri ekleyeceğiz.
struts-config içine:
<action path="/sample"
parameter="methodToGo"
type="SampleAction"
name="sampleForm"
scope="request"
validate="false">
...
</action>
Buradaki parameter, DispatchAction'ın hangi metoda gideceğini bilmesini sağlayan parametredir. Parameter değeri, gidilecek metodun ismidir.
Ardından, jsp'de methodToGo isminde hidden bir değer tutmamız ve yapılacak işleme göre bu hidden alanın değerini set etmemiz yeterli olacaktır.
Bunun için, örneğin, ekranda güncelle butonuna basıldığında methodToGo değerini, javascript vasıtasıyla update'e set ederiz. Form submit edildiğinde methodToGo değerini update gönderdiğimiz için, action'ımızın direk update metodu çağrılacaktır.
Ekleme işlemi için de örneğin Ekle butonuna basıldığında javascript vasıtasıyla yine methodToGo alanının değerini add'e set ederiz. Böylece, action'ımızdaki add metodunun direk çağrılmasını sağlarız.
DispatchAction, execute metodu içindeki yığınla kontrolden ve kod karmaşasından bizi kurtarır. Ama genel bir yapı oluşturuyorsak ve bu yapıyı kullanacak makinelerin standart bir yapısı yoksa yani bizim uygulamamızı kullanacak olan kişilerin bilgisayar konfigürasyonlarına bir standart getiremiyorsak, o zaman bu kullanıcıların explorer'larında javascript'in enabled olup olmadığını da bilemeyiz. Eğer javascript enabled değilse dispatchAction için gerekli olan parametreyi de ekrandan alınmak istenen aksiyona göre değiştiremeyiz ve sistemimiz çalışmaz ya da yanlış çalışır. Kaldı ki, ilgili aksiyon için ilgili metodu otomatik olarak harekete geçiren bir yapıdan söz ederken hala jsp tarafında javascript vasıtasıyla manuel olarak parametre set edilmesi bu sistem içinde ufak da olsa bir ikilem oluşturuyor gibi görünüyor. Bu noktada, dispatch action'ı bir adım öteye götürerek LookupDispatchAction'ın kullanılmasını tavsiye ederim. Ben de bunun varlığını yeni öğrendim ve bir deneme yaptım.
LookupDispatchAction, ekrandaki butonun value kısmını dikkate alarak aksiyon alır. DispatchAction'daki parametre kullanılır yine ama bu parametre butonların property değerine set edilir.
struts-config içine:
<action path="/sample2"
parameter="methodToGo"
type="Sample2Action"
name="sample2Form"
scope="request"
validate="false">
...
</action>
jsp icine de istediğimiz gibi butonları ekliyoruz.
<html:form action="/sample2">
<html:submit property="methodToGo">
<bean:message key="label.sample.insert"/>
</html:submit>
<html:submit property="methodToGo">
<bean:message key="label.sample.update"/>
</html:submit>
</html:form>
Burada gidilecek olan metodu belirleyen submit butonunun value'su yani message.resource içinde almış olduğumuz label.sample.insert ve label.sample.update değerleri.
Yapılması gereken başka bir değişiklik de, DispatchAction yerine LookupDispatchAction'a extend eden action'ımıza eklemek zorunda olduğumuz getKeyMethodMap metodu.
Bunun için de;
protected Map getKeyMethodMap(){
Map map = new HashMap();
map.put("label.sample.insert", "add");
map.put("label.sample.update", "update");
return map;
}
metodunu ekliyorum ve add ve update adında iki metod hazırlıyorum.
Unutmamam gereken birkaç nokta var. Onları da eklemeliyim. Öncelikle, message property dosyama label.sample.insert ve label.sample.update eklemeliyim. İkinci olarak da bu properties dosyasının struts-config'e eklenmiş olduğundan emin olmalıyım.
Çalıştırmaya çalışırken karşılaşılması olası genel bir hata var:
javax.servlet.ServletException: Action[/sample2] missing resource 'label.sample.insert' in key method map
at org.apache.struts.actions.LookupDispatchAction.getLookupMapName(LookupDispatchAction.java:240)
at org.apache.struts.actions.LookupDispatchAction.getMethodName(LookupDispatchAction.java:281)
...
Öncelikle message resource dosyasının doğru olduğunu kontrol etmek gerekiyor. Ve bu dosyanın kesinlikle struts-config içine eklenmiş olması gerekiyor:
<message-resources parameter="MessageResources"/>
getKeyMethodMap metodu içinde tanımladığımız hashMap'in içindeki değerlerin de doğru olduğundan emin olmak gerek. Resource dosyasında tanımladığımız buton isimlerinin sonunda boşluk olmadığından da emin olmalıyız.
Ayrıca, url'den lookupDispatchAction'a extend etmiş bir action'ı çağırırken parametreyi set edip erişmeye çalışıldığında da bu hatayı alabiliriz.
LookupDispatchAction kullanırken karşılaşılan bir sıkıntı var. Ona da değinmek lazım. Butonların property'lerini parametre ismine set ettiğimiz için javascript'le erişimde sıkıntı çekebiliriz. document.all("...") veya benzeri javascript çağırmaları çalışmayabilir. Tüm bu ayrıntılar göz önünde bulundurularak karar verilmelidir diye düşünüyorum. Nicazane görüşüm; ben şimdilik DispatchAction'dan memnunum ve sürekli onu kullanıyorum :)