AJAX
Det interessante med denne angrepsvinkelen er altså at vi får svært enkel
koding på klientsiden. Vi får tilgang til objekter og kan plukke
attributter etter ønske.
Hvis vi ser dette i en AJAX situasjon, har vi følgende:
Produksjon av JSON
Hvorvidt bruk av JSON er en effektiv og/eller enkel metode avhenger
av hvordan dataene våre er representerte på tjeneren, og det avhenger
av hva vi må gjøre med dem på klienten. Det er klart at dersom vi skal overføre
en hel fil som er ferdiglagd som et HTML-fragment og vi skal plassere dette
fragmentet samlet på klienten er det mest effektive å bruke ren tekst slik:
document.getElementById("target").innerHTML= myRequest.responseText
Dersom teksten som kommer tilbake består av mange "deler" som skal plasseres
på forskjellige steder på siden stiller saken seg litt annerledes.
Både kommaseparert tekst, XML og JSON kan være alternativer.
På tjenersiden vil mye avhenge av dataformatet og programmeringsverktøyet
vi har til rådighet.
Som regel kan vi vel regne med at programmeringsverktøyet på tjeneren
er bedre og raskere enn JavaScript. Dette skulle i så fall tale for
at så mye som mulig gjøres klart på tjeneren.
På den annen side er det ofte slik at bruken av de data vi sender tilbake vil
endre seg i forskjellige situasjoner avhengig av bruksønsker og designbeslutninger.
Det er kanskje en fordel å lage en generell tjenerfunksjon og så lage spesielle
klientløsninger etter ønske. Dette taler for et fleksibelt format som
peker i retning av JSON, eller XML. De to, JSON og XML, kan langt på vei sidestilles
prinsippielt, men JSON gjør trolig lettere å programmere på klienten.
Nedenfor skal vi gå gjennom noen eksempler på pakking av data på tjenersiden
og bruk på klientsiden. Vi skal bruke vindataene som gjennomgangseksempel.
Vi skal bruke både XML-versjonen og databaseversjonen.
Vi skal i alle tilfellene
lage en klientside som gir oss mulighet for å velge vin av en bestemt type
(rød, hvit, rose, musserende) og fra et bestemt land. Vi skal i alle
løsningen bruke JSON, selv om et par av eksemplene åpenbart kunne løses
med lettere med tekst og alle kunne løses med XML.
Vin som JSON
I alle eksemplene bruker vi et JSON format som ser slik ut, vist med linjeskift:
{"diagnose":"OK",
"overskrift":"Rødvin fra Island",
"viner":[
{"navn":"Masi","terning":"4","beskrivelse":"OK"},
{"navn":"Perequita","terning":"5","beskrivelse":"God"},
...
]}
Altså en diagnose som sier om dataoppslaget har gått bra eller ikke,
en passelig overskrift
pluss en array med vinobjekter.
Merk at vi pakker dataene uten linjeskift slik at de kan leses som en sammenhengende string i JavaScript.
I alle eksemplene bruker vi samme javascript på klienten:
// relies on jquery
function getIt(address,form,targetNodeId)
{
var params="vintype="+form.vintype.value+"&land="+form.land.value;
$.ajax({
url:address,
data:params,
success:function(data){
processJSON(data,targetNodeId);
},
error:function(data){
$('#'+targetNodeId).html("Could not access content");
}
});
}
function processJSON(S,targetId)
{
var ob = null;
try{
ob = JSON.parse(S);
}
catch(ex){
//console.log(S);
$('#'+targetId).html('<p>Feil i dataformatet: '+ex.message+'</p>');
return;
}
var dia=ob.diagnose;
if(dia.indexOf('OK')==0){
var vinlist=ob.viner;
var S='';
for(var ix=0;ix<vinlist.length;ix++){
S+='<p>'+
'<span style="font-size:20px;color:blue;margin-right:20px">'+
vinlist[ix].terning+'</span>'+
vinlist[ix].navn+'<br/>'+
vinlist[ix].beskrivelse+'<br/></p>';
}
S='<h1>'+vinlist.length+' '+ob.overskrift+'</h1>'+S;
$('#'+targetId).html(S);
}
else{
document.getElementById(targetId).innerHTML='<pre>'+dia+'</pre>';
}
}
En typisk tekst som overføres og parses ser slik ut:
Vin fra XML
Produksjon av JSON fra XML
Vi bruker XML-data på tjeneren, skriver DOM-code som identifiserer
de vinene vi er interesserte i og produserer JSON. På klientsiden
lager vi HTML av JSON-formatet.
Koden på tjeneren ser slik ut:
#! /usr/bin/python
# -*- coding: utf-8 -*-
import xml.dom.minidom,urllib,cgi
#-------------------------------------------------------------
# Receive: land and vintype
# Return JSON format for selected wines
#{"diagnose":"OK",
# "overskrift":"Hvite viner fra Italia",
# "viner":[
# {"navn":"A","terning":"4","beskrivelse":"grei"},
# {"navn":"B","terning":"4","beskrivelse":"ok"}
# ]
#}
# B. Stenseth 2009
#-------------------------------------------------------------
#------------------------------------------------------
# datasource
XMLSOURCE='https://borres.hiof.no/resources/data/vin/viner.xml'
#------------------------------------------------------
# string for one wine
wine="""{"navn":"%s","terning":"%s","beskrivelse":"%s"}"""
#-----------------------
# downloading an xml-file as string
def loadUrl(urladdress):
try:
f=urllib.urlopen(urladdress)
s=f.read()
f.close()
return s
except:
return None
#-----------------------
# collect all text in a node
def getText(nodelist):
rc = ''
for node in nodelist:
if node.nodeType == node.TEXT_NODE:
#t=node.data.encode('ISO-8859-1')
t=node.data.encode('utf-8')
rc += t
return rc
#--------------------------
# prepare one wine
def doOneWine(vin,theCountry,theType):
land=getText(vin.getElementsByTagName('country')[0].childNodes)
if land != theCountry:
return ''
vintype=getText(vin.getElementsByTagName('type')[0].childNodes)
if vintype != theType:
return ''
navn=getText(vin.getElementsByTagName('name')[0].childNodes)
navn=navn.replace('"','\\"')
terning=getText(vin.getElementsByTagName('dice')[0].childNodes)
beskrivelse=getText(vin.getElementsByTagName('description')[0].childNodes)
return wine%(navn,str(terning),beskrivelse)
#--------------------------
# buld tree and viner string
def doAll(theCountry,theType):
try:
vintxt=loadUrl(XMLSOURCE)
if vintxt==None:
return None
vindom = xml.dom.minidom.parseString(vintxt)
vinlist=vindom.getElementsByTagName('wine')
result=''
for vin in vinlist:
t=doOneWine(vin,theCountry,theType)
if len(t)>0:
result=result+t+','
return result
except:
return None
#----------------------
form=cgi.FieldStorage()
print 'Content-type: text/html; charset=utf-8\n'
land='Ukjent'
vintype='Ukjent'
dia='"diagnose":"BAD"'
if form.has_key('land'):
land=form['land'].value
if form.has_key('vintype'):
vintype=form['vintype'].value
if vintype=='red':
header='"overskrift":"Røde viner fra %s"'%land
elif vintype=='white':
header='"overskrift":"Hvite viner fra %s"'%land
elif vintype=='rose':
header='"overskrift":"Roseviner fra %s"'%land
elif vintype=='sparkling':
header='"overskrift":"Musserende viner fra %s"'%land
else:
header='"overskrift":"Ukjent vintype"'
viner=doAll(land,vintype)
if viner !=None:
dia='"diagnose":"OK"'
else:
viner='xx'
retstr='{'+dia+','+header+',"viner":['+viner[:-1]+']}'
print retstr
Vin fra XML via XSLT
Produksjon av JSON fra XML med XSL
Vi bruker XML-data på tjeneren, skriver en XSLT-transformasjon som
lager JSON direkte. På klientsiden lager vi HTML av JSON-formatet.
Koden på tjeneren ser slik ut:
#! /usr/bin/python
# -*- coding: utf-8 -*-
import xml.dom.minidom,urllib,cgi
from lxml import etree
import cgitb; cgitb.enable()
#-------------------------------------------------------------
# Receive: land and vintype
# Return JSON format for selected wines
#{"diagnose":"OK",
# "overskrift":"Hvite viner fra Italia",
# "viner":[
# {"navn":"A","terning":"4","beskrivelse":"grei"},
# {"navn":"B","terning":"4","beskrivelse":"ok"}
# ]
#}
# B. Stenseth 2009
#-------------------------------------------------------------
#------------------------------------------------------
# data and transform source
XMLSOURCE='https://borres.hiof.no/resources/data/vin/viner.xml'
XSLSOURCE='trans2.xsl'
#------------------------------------------------------
# string for one wine
wine="""{"navn":"%s","terning":"%s","beskrivelse":"%s"}"""
#-----------------------
# helper to load a text file
def getTextFile(filename):
try:
file=open(filename,'r')
intext=file.read()
file.close()
return intext
except:
return None
#-----------------------
# downloading an xml-file as string
def loadUrl(urladdress):
try:
f=urllib.urlopen(urladdress)
s=f.read()
f.close()
return s
except:
return None
#-----------------------
def doAll(land,vintype):
xsltstr=loadUrl(XSLSOURCE)
xmlstr=loadUrl(XMLSOURCE)
xmlstr=xmlstr.strip()
# hack to keep "
parts=xmlstr.split('\n',1)
xmlstr=parts[0]+'\n'+parts[1].replace('"','\\"')
xmlTree=etree.XML(xmlstr)
xsltTree=etree.XML(xsltstr)
transform=etree.XSLT(xsltTree)
c=etree.XSLT.strparam(land)
t=etree.XSLT.strparam(vintype)
resultTree=transform(xmlTree,theCountry=c,theType=t)
retstr=str(resultTree)
return retstr
#----------------------
form=cgi.FieldStorage()
print 'Content-type: text/html; charset=utf-8\n'
land='Portugal'
vintype='red'
dia='"diagnose":"BAD"'
if form.has_key('land'):
land=form['land'].value
if form.has_key('vintype'):
vintype=form['vintype'].value
if vintype=='red':
header='"overskrift":"Røde viner fra %s"'%land
elif vintype=='white':
header='"overskrift":"Hvite viner fra %s"'%land
elif vintype=='rose':
header='"overskrift":"Roseviner fra %s"'%land
elif vintype=='sparkling':
header='"overskrift":"Musserende viner fra %s"'%land
else:
header='"overskrift":"Ukjent vintype"'
viner=doAll(land,vintype)
if len(viner) > 0:
dia='"diagnose":"OK"'
else:
viner='xx'
retstr='{'+dia+','+header+','+viner+'}'
print retstr
Transformasjonen er slik:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:param name="theCountry" select="'Frankrike'"/>
<xsl:param name="theType" select="'red'"/>
<xsl:template match="/">
"viner":[
<xsl:apply-templates
select="//wine[country=$theCountry and type=$theType]">
<xsl:sort order="descending" select="dice"/>
</xsl:apply-templates>
]
</xsl:template>
<xsl:template match="//wine">
{"terning":"<xsl:value-of select="dice"/>",
"navn":"<xsl:value-of select="name"/>",
"beskrivelse":"<xsl:value-of select="description"/>"}
<xsl:if test="position() != last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Vin fra database
Produksjon av JSON fra DB
Vi bruker en database på tjeneren og lager JSON basert på de
recordene vi plukker opp med en SQL-setning.
På klientsiden lager vi HTML av JSON-formatet.
Koden på tjeneren ser slik ut:
#! /usr/bin/python
# -*- coding: utf-8 -*-
import MySQLdb,cgi,urllib
#-------------------------------------------------------------
# Receive: land and vintype
# Return JSON format for selected wines
#{"diagnose":"OK",
# "overskrift":"Hvite viner fra Italia",
# "viner":[
# {"navn":"A","terning":"4","beskrivelse":"grei"},
# {"navn":"B","terning":"4","beskrivelse":"ok"}
# ]
#}
# Datasource: mySQL base as described in
# http://www.ia.hiof.no/~borres/ml/datasett/p-datasett.html
# B. Stenseth 2007
#-------------------------------------------------------------
#-------------------------------------------------
# connect and execute a sql-request
#-------------------------------------------------
def connectAndExecute(sql):
try:
myBase=MySQLdb.connect(host=' itstud.hiof.no',
user='student',
passwd='student',
db='vin')
myTab=myBase.cursor()
myTab.execute(sql)
myBase.close()
return myTab.fetchall()
except MySQLdb.Error, e:
print "Error %d: %s" % (e.args[0], e.args[1])
return None
#-------------------------------------------------
# do the job
#-------------------------------------------------
def doAll(land,vintype):
resultTxt=''
sql="""select name,dice,description from wines
where country='%s' and type='%s'
order by dice desc;"""
result=connectAndExecute(sql%(land,vintype))
if result==None:
return None
wine="""{"navn":"%s","terning":"%s","beskrivelse":"%s"}"""
for row in result:
navn=str(row[0])
navn=navn.replace('"','\\"')
terning=str(row[1])
beskrivelse=str(row[2])
resultTxt=resultTxt+wine%(navn,terning,beskrivelse)+','
return resultTxt
#----------------------
form=cgi.FieldStorage()
print 'Content-type: text/html; charset=utf-8\n'
land='Ukjent'
vintype='Ukjent'
dia='"diagnose":"BAD"'
if form.has_key('land'):
land=form['land'].value
if form.has_key('vintype'):
vintype=form['vintype'].value
if vintype=='red':
header='"overskrift":"Røde viner fra %s"'%land
elif vintype=='white':
header='"overskrift":"Hvite viner fra %s"'%land
elif vintype=='rose':
header='"overskrift":"Roseviner fra %s"'%land
elif vintype=='sparkling':
header='"overskrift":"Musserende viner fra %s"'%land
else:
header='"overskrift":"Ukjent vintype"'
viner=doAll(land,vintype)
if viner != None:
dia='"diagnose":"OK"'
viner=viner.decode('iso-8859-1')
viner=viner.encode('utf-8')
else:
viner='xx'
retstr='{'+dia+','+header+',"viner":['+viner[:-1]+']}'
print retstr