Struktur
Vi skal bygge en struktur som på figuren
Survey Webservice er en komplett Webservice. Vi ønsker ikke å integrere denne i alle de vevsidene som skal bruke den
så vi lager en hjelpeside, Survey Provider, som formidler kontakten til tjenesten. Brukersidene, Survey users,
kommuniserer med denne hjelpesiden via AJAX-forspørsler. På denne måten oppnår
vi at vi på en enkel måte kan vise og administere surveys som en del av en vevside.
For at vi skal kunne bruke AJAX-forspørsler fra vevsider som er plassert på andre domener,
har vi laget en proxy som kommuniserer med hjelpesiden. I vårt tilfelle er denne proxyen et Pythonskript som
oversetter parameterne i AJAX-forspørselen til parameter i en HTTP/GET-forespørsel og bare formidler kontakten.
Alle disse komponenetene er beskrevet nedenfor.
Webservice
Selve webservicen er altså en komplett webservice med SOAP og WSDSL og det hele.
Den presenterer seg slik:
http://donau.hiof.no/borres/dn/survey/Service.asmx
.
Koden som kjører er slik:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Xml;
using System.Web.Services;
[WebService(Namespace = "http://www.donau.hiof.no/borres/dn/survey")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
// To allow this Web Service to be called from script,
// using ASP.NET AJAX, uncomment the following line.
[System.Web.Script.Services.ScriptService]
public class Service : System.Web.Services.WebService
{
public Service()
{
//Uncomment the following line if using designed components
//InitializeComponent();
}
#region data
private XmlDocument loadData(ref string msg)
{
try
{
XmlDocument doc = new XmlDocument();
String filename = HttpContext.Current.Server.MapPath(".").ToString() +
"\\App_Data\\results.xml";
doc.Load(filename);
return doc;
}
catch (Exception ex)
{
msg = ex.Message;
return null;
}
}
private String saveData(XmlDocument doc)
{
try
{
String filename = HttpContext.Current.Server.MapPath(".").ToString() +
"\\App_Data\\results.xml";
XmlWriterSettings xs = new XmlWriterSettings();
xs.Indent = true;
XmlWriter xr = XmlWriter.Create(filename, xs);
doc.Save(xr);
xr.Close();
return null;
}
catch (Exception ex)
{
return ex.Message;
}
}
#endregion data
#region methods
[WebMethod(Description = "A simple service for handling surveys")]
public string What()
{
return "This is a simple service for handling surveys";
}
[WebMethod(Description = "Handle respons and return status")]
public String doSurvey(String id, String answer)
{
try
{
// load XML data
String msg = "";
XmlDocument doc = loadData(ref msg);
if (msg.Length > 1)
throw new Exception(msg);
String xpath = String.Format("//survey[@id='{0}']", id);
XmlNodeList list = doc.SelectNodes(xpath);
if (list.Count != 1)
throw new Exception("id troubble");
XmlElement survey = (XmlElement)list[0];
XmlElement op1 = (XmlElement)survey.GetElementsByTagName("option1")[0];
XmlElement op2 = (XmlElement)survey.GetElementsByTagName("option2")[0];
XmlElement op3 = (XmlElement)survey.GetElementsByTagName("option3")[0];
XmlElement head = (XmlElement)survey.GetElementsByTagName("heading")[0];
if (answer.CompareTo("1") == 0)
op1.InnerText = Convert.ToString(Convert.ToUInt32(op1.InnerText)+1);
else if (answer.CompareTo("2") == 0)
op2.InnerText = Convert.ToString(Convert.ToUInt32(op2.InnerText)+1);
else
op3.InnerText = Convert.ToString(Convert.ToUInt32(op3.InnerText)+1);
// save XML data
String t = saveData(doc);
if (t != null)
throw new Exception(t);
int a1 = Convert.ToInt32(op1.InnerText);
int a2 = Convert.ToInt32(op2.InnerText);
int a3 = Convert.ToInt32(op2.InnerText);
int p1 = Convert.ToInt32(100.0f * a1 / (a1 + a2+a3));
int p2 = Convert.ToInt32(100.0f * a2 / (a1 + a2 + a3));
int p3 = Convert.ToInt32(100.0f * a3 / (a1 + a2 + a3));
String T = String.Format(@"
<div>{0} har svart slik</div>
<table>", Convert.ToString(a1 + a2 + a3));
T += String.Format("<tr><td>{0}</td><td>{1}%</td></tr>",
op1.GetAttribute("txt"), p1);
T += String.Format("<tr><td>{0}</td><td>{1}%</td></tr>",
op2.GetAttribute("txt"), p2);
if(op3.GetAttribute("txt").Length >0)
T += String.Format("<tr><td>{0}</td><td>{1}%</td></tr>",
op3.GetAttribute("txt"), p3);
T += "</table>";
return T;
}
catch (Exception ex)
{
return "error: " + ex.Message;
}
}
[WebMethod(Description = "Establish a new survey")]
public String SetupSurvey(String heading,
String option1, String option2,String option3)
{
try
{
// load XML data
String msg = "";
XmlDocument doc = loadData(ref msg);
if (msg.Length > 1)
throw new Exception(msg);
// find an id
XmlElement root = doc.DocumentElement;
String id =
Convert.ToString(Convert.ToUInt64(root.GetAttribute("lastid")) + 1);
XmlElement survey = doc.CreateElement("survey");
XmlElement op1 = doc.CreateElement("option1");
XmlElement op2 = doc.CreateElement("option2");
XmlElement op3 = doc.CreateElement("option3");
XmlElement head = doc.CreateElement("heading");
survey.SetAttribute("id", id);
head.AppendChild(doc.CreateTextNode(heading));
op1.SetAttribute("txt", option1);
op1.AppendChild(doc.CreateTextNode("0"));
op2.SetAttribute("txt", option2);
op2.AppendChild(doc.CreateTextNode("0"));
op3.SetAttribute("txt", option3);
op3.AppendChild(doc.CreateTextNode("0"));
survey.AppendChild(head);
survey.AppendChild(op1);
survey.AppendChild(op2);
survey.AppendChild(op3);
root.AppendChild(survey);
root.SetAttribute("lastid", id);
String t = saveData(doc);
if (t != null)
throw new Exception(t);
String radioLine=@"
<div><input type=""radio"" name=""choice{0}"" value=""{2}""/>{1}</div>";
String T=
String.Format(@"Copy this code to your webpage, within a form:
<h3>{1}</h3>
<div id=""wrap{0}"">",id,heading);
T+=String.Format(radioLine, id , option1, "1");
T += String.Format(radioLine, id, option2, "2");
if(option3.Length > 0)
T+=String.Format(radioLine,id,option3,"3");
T+=String.Format(@"
<div>
<input type=""button"" value=""Stem"" onclick=""showIt('{0}',this.form.choice{0});return false;""/>
</div>",id);
T+=@"
</div>";
T = T.Replace("<", "<");
T = T.Replace(">", ">");
return "<pre>"+T+"</pre>";
}
catch (Exception ex)
{
return "survey says: error because: " + ex.Message;
}
}
#endregion methods
}
Dataene for hver survey lagres i en enkel XML.fil.
Den kan se slik ut: Results.xml
Provider
Vi ønsker ikke å lenke til webservicen i alle vevsider som skal/kan bruke denne
denne tjenesten, så vi lager en proxy som handterer AJAX-forespørsler på en slik måte at forspørslene
sendes til survey-tjenesten, og svaret formidles tilbake til den som spør.
Koden som kjøres i denne proxyen, surveyprovider, er slik:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections.Specialized;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
NameValueCollection coll=null;
if (Request.HttpMethod.ToLower().CompareTo("post") == 0)
coll = Request.Params;
else
coll = Request.QueryString;
if (!coll.AllKeys.Contains("job"))
{
Response.Write("provider says: no job");
for (int ix = 0; ix < coll.AllKeys.Length; ix++)
Response.Write(coll.AllKeys[ix]);
return;
}
String job = coll.Get("job");
if (job.CompareTo("make") == 0)
makeQuery(coll);
else
getResult(coll);
}
protected void getResult(NameValueCollection coll)
{
String id = coll.Get("sid");
String choice = coll.Get("choice");
survey.ServiceSoapClient proxy = new survey.ServiceSoapClient();
String T = proxy.doSurvey(id, choice);
Response.Write(T);
Response.Flush();
}
protected void makeQuery(NameValueCollection coll)
{
String header = coll.Get("header");
String opt1 = coll.Get("option1");
String opt2 = coll.Get("option2");
String opt3 = coll.Get("option3");
if (opt3 == null)
opt3 = "";
survey.ServiceSoapClient proxy = new survey.ServiceSoapClient();
String T = proxy.SetupSurvey(header,opt1,opt2,opt3);
Response.Write(T);
Response.Flush();
}
}
Bruk
En typisk bruksside kan se slik ut:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Surveydemo</title>
<script src="http://donau.hiof.no/borres/dn/_jslibs/prototype.js"
type="text/javascript"> </script>
<script src="showresult.js"
type="text/javascript"> </script>
</head>
<body>
<h1>Survey demo</h1>
<form id="form1" runat="server">
<div>
<p>Et enkelt eksempel:</p>
<h2>Blir det snø 17.mai ?</h2>
<div id="wrap4">
<div><input type="radio" name="choice2" value="1"/>Ja</div>
<div><input type="radio" name="choice2" value="2"/>Nei</div>
<div><input type="radio" name="choice3" value="2"/>Nei</div>
<div><input type="button" value="Stem" onclick="showIt('2',this.form.choice2);return false;"/></div>
</div>
</div>
</form>
<h2>Hvordan sette opp en survey</h2>
<p><a href="setup.aspx">Setup</a></p>
</body>
</html>
Vi ser at dette eksempelet ikke bruker AJAX-kontrollerne som er tilgjengelige i MS verktøykassa, men
den illustrerer hvordan vi kan nå proxyen vår vi AJAX. Javaskriptet som formidler dette
er showresult.js, som er basert på prototype [1] :
var theUrl = 'http://donau.hiof.no/borres/dn/surveyprovider/Default.aspx';
function get_radio_value(radios) {
for (var i = 0; i < radios.length; i++) {
if (radios[i].checked) {
return radios[i].value;
}
}
return null;
}
function showIt(sid, radios) {
var theChoice = get_radio_value(radios);
if (theChoice == null) {
alert("make your choice");
return;
}
var params = "job=result&sid=" + sid + "&choice=" + theChoice;
//alert(params);
$("#wrap" + sid).html('<img src="waiter.gif" alt="waiting..." />');
$.ajax(
{url:theUrl,
type:'POST',
data:params,
success:function(transport)
{
var T = transport;
$("#wrap" + sid).html(T);
},
error:function()
{
$("#wrap" + sid).html("Could not access content")
}
}
);
}
function makeIt(form) {
if ((form.header.value.length == 0) ||
(form.option1.value.length == 0) ||
(form.option2.value.length == 0)) {
alert("Fill in the necessary fields");
return;
}
var params = "job=make" +
"&header=" + encodeURIComponent(form.header.value) +
"&option1=" + encodeURIComponent(form.option1.value) +
"&option2=" + encodeURIComponent(form.option2.value) +
"&option3=" + encodeURIComponent(form.option3.value);
$("#wrap").html('<img src="waiter.gif" alt="waiting..." />');
$.ajax({
url:theUrl,
type:'GET',
data: params,
success: function(transport)
{
var T = transport;
$("#wrap").html(T);
},
error: function()
{
$("#wrap").html("Could not access content")
}
}
);
}
Bruk fra et annet domene
Den siden du leser nå ligger på domenet "brage.hiof.no", mens hele mekanismen med survey og surveyprovider
ligger på domenet "donau.hiof.no". Vi kan altså ikke få tak i serviceprovider via en AJAX-forspørsel direkte,
men vi kan sette opp en kontakt på tvers av domene i et skript, f.eks. et Pythonskript:
#! /usr/bin/python
import sys,cgi
import urllib,os
import cgitb; cgitb.enable()
"""
This generic program receives a request,
format the parameters, passes it on to a webserviceprovider
and return the result.
Documentation of sample setup and use in
http://www.it.hiof.no/~borres/dn/survey/p-survey.html
"""
theUrl='http://donau.hiof.no/borres/dn/surveyprovider/Default.aspx'
def doIt(params):
#post it
f=urllib.urlopen(theUrl,urllib.urlencode(params))
S=f.read()
f.close()
print S
#-----------------------------------------
print "Content-type: text/html; charset=utf-8\n"
form=cgi.FieldStorage()
params={}
keys=form.keys()
if len(keys)>0:
for k in keys:
params[k]=form[k].value
doIt(params)
else:
print '<div>Bad request, missing parameters</div>'
Dersom dette skriptet plasseres på "brage.hiof.no", kan vi nå det fra denne vevsiden med en AJAX-forespørsel.
Javascriptet som ordner dette er nøyaktig likt det som brukes ovenfor, men adressen, URL'en, er byttet.
var theUrl='http://www.it.hiof.no/~borres/cgi-bin/survey/SurveyContact.py'
function get_radio_value(radios) {
for (var i = 0; i < radios.length; i++) {
if (radios[i].checked) {
return radios[i].value;
}
}
return null;
}
function showIt(sid, radios)
{
var theChoice = get_radio_value(radios);
if (theChoice == null)
{
alert("make your choice");
return;
}
var params = "job=result&sid=" + sid + "&choice=" + theChoice;
$("#wrap" + sid).html('<img src="waiter.gif" alt="waiting..." />');
$.ajax(
{url:theUrl,
type:'GET',
data:params,
success:function(transport)
{
var T = transport;
$("#wrap" + sid).html(T);
},
error:function()
{
$("#wrap" + sid).html("Could not access content")
}
}
);
}
function makeIt(form)
{
if ((form.header.value.length == 0) ||
(form.option1.value.length == 0) ||
(form.option2.value.length == 0)) {
alert("Fill in the necessary fields");
return;
}
var params = "job=make"+
"&header=" + encodeURIComponent(form.header.value) +
"&option1=" + encodeURIComponent(form.option1.value)+
"&option2=" + encodeURIComponent(form.option2.value)+
"&option3=" + encodeURIComponent(form.option3.value);
$("#wrap").html('<img src="waiter.gif" alt="waiting..." />');
$.ajax({
url:theUrl,
type:'GET',
data: params,
success: function(transport)
{
var T = transport;
$("#wrap").html(T);
},
error: function()
{
$("#wrap").html("Could not access content")
}
}
);
}
Derfor er dette mulig:
Ny survey
Å opprette et nytt spørsmål kan styres via samme kanaler som en respons.
Fra samme domene som service:
eller fra et annet (dette) domenet:
Koden er slik:
<form action="#">
<div id="wrap">
<div>Heading: <input type="text" size="40" name="header"/><span style="color:red">*</span></div>
<div>Option1: <input type="text" size="40" name="option1"/><span style="color:red">*</span></div>
<div>Option2: <input type="text" size="40" name="option2"/><span style="color:red">*</span></div>
<div>Option3: <input type="text" size="40" name="option3"/> </div>
<div><input type="button" value="Make it" onclick="makeIt(this.form);return false;"/></div>
</div>
</form>