Dopo un periodo di pausa, ho deciso di completare, finalmente, questa serie di post dedicati a WCF-Rest, parlando di quello che in realtà è stato l’inizio di tutto, ovvero accedere ai miei “preziosissimi” dati da uno smartphone Android, quindi creare in Android un client per il servizio WCF-Rest.
Iniziamo con il creare un client http che preveda una basic http authentication:
public class RestClient {
private static String USERNAME ;
private static String PASSWORD ;
private static HttpHost HOST;
private static DefaultHttpClient HTTPCLIENT = null;
public RestClient(HttpHost host, String user, String password){
USERNAME = user;
PASSWORD = password;
HOST = host;
}
public HttpClient GetAuthenticatedClient() {
if (HTTPCLIENT != null)
return HTTPCLIENT;
AuthScope authScope = new AuthScope(HOST.getHostName(), HOST.getPort(), AuthScope.ANY_REALM);
final HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, "UTF-8");
HttpProtocolParams.setUseExpectContinue(params, false); // solves the '417' issue
BasicCredentialsProvider credentials = new BasicCredentialsProvider();
credentials.setCredentials(authScope, new UsernamePasswordCredentials(
USERNAME, PASSWORD));
HTTPCLIENT = new DefaultHttpClient(params);
HTTPCLIENT.setCredentialsProvider(credentials);
return HTTPCLIENT;
}
Useremo questo client per eseguire i metodi GET, PUT, POST e DELETE, in particolare creiamo i wrapper per ogni verbo http, i quali prenderanno in ingresso l’url a cui fare la la request, eventualmente la stringa XML da utilizzare come entity della request e restituiscono l’entity della response sotto forma di stringa XML.
Come punto di partenza ho tratto spunto dall’articolo Android: How to Deserialize both XML and JSON
public String DoGetRequest(String uri)
throws HttpException {
String responseValue = "";
HttpClient client = GetAuthenticatedClient();
HttpGet httpGet = new HttpGet(uri);
try {
HttpResponse response = client.execute(httpGet);
responseValue = GetResponseContentAsString(response);
} catch (Exception ex) {
Log.e("Error connecting to url:" + uri, ex.getMessage());
throw new HttpException(ex.getMessage());
}
return responseValue;
}
public String DoPostRequest(String uri, String body)
throws HttpException {
String responseValue = "";
try {
HttpClient client = GetAuthenticatedClient();
HttpPost httpPost = new HttpPost(uri);
httpPost.setHeader("Content-Type", "text/xml");
httpPost.addHeader("Accept", "application/xml");
httpPost.setEntity(new StringEntity(body, HTTP.UTF_8));
HttpResponse response = client.execute(httpPost);
responseValue = GetResponseContentAsString(response);
} catch (Exception ex) {
Log.e("Error connecting to url:" + uri, ex.getMessage());
throw new HttpException(ex.getMessage());
}
return responseValue;
}
public String DoPutRequest(String uri, String body)
throws HttpException {
String responseValue = "";
try {
HttpClient client = GetAuthenticatedClient();
HttpPut httpPut = new HttpPut(uri);
httpPut.setHeader("Content-Type", "text/xml");
httpPut.addHeader("Accept", "application/xml");
httpPut.setEntity(new StringEntity(body, HTTP.UTF_8));
HttpResponse response = client.execute(httpPut);
responseValue = GetResponseContentAsString(response);
} catch (Exception ex) {
Log.e("Error connecting to url:" + uri, ex.getMessage());
throw new HttpException(ex.getMessage());
}
return responseValue;
}
public String DoDeleteRequest(String uri)
throws HttpException {
String responseValue = "";
HttpClient client = GetAuthenticatedClient();
HttpDelete httpDelete = new HttpDelete(uri);
try {
HttpResponse response = client.execute(httpDelete);
responseValue = GetResponseContentAsString(response);
} catch (Exception ex) {
Log.e("Error connecting to url:" + uri, ex.getMessage());
throw new HttpException(ex.getMessage());
}
return responseValue;
}
Per la conversione dallo stream contenuto nella entity della response:
private String GetResponseContentAsString(HttpResponse response)
throws Exception {
String responseValue = "";
HttpEntity entity = response.getEntity();
try {
if (entity != null) {
InputStream instream = entity.getContent();
responseValue = ConvertStreamToString(instream);
instream.close();
}
} catch (Exception ex) {
throw ex;
}
return responseValue;
}
public String ConvertStreamToString(InputStream is) {
/* To convert the InputStream to String we use the
* BufferedReader.readLine() method. We iterate until the BufferedReader
* return null which means there's no more data to read. Each line will
* appended to a StringBuilder and returned as String. */
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sb.toString();
}
Questo completa la creazione della classe RestClient.java (in allegato)
Da qui si può iniziare a creare i repository che utilizzeranno questa classe per l’accesso ai dati. In particolare, riferndoci anche ai precedenti articoli sull’argomento, facciamo riferimento all’entità “Causale” e al relativo “CausaleRepository”
Quindi, molto semplicemente, la classe “Causale” sarà:
public class Causale {
private int _id;
private String _descrizione;
private int _segno;
public int getId() {
return _id;
}
public void setId(int id) {
_id = id;
}
public String getDescrizione() {
return _descrizione;
}
public void setDescrizione(String descrizione) {
_descrizione = descrizione;
}
public int getSegno() {
return _segno;
}
public void setSegno(int segno) {
_segno = segno;
}
}
Il relativo repository:
public class CausaleRepository {
private final String CAUSALE_NAMESPACE = "http://www.dev-spark.com/easymoneytracker/causale";
private final String CAUSALE_ROOTTAG = "CausaleDto";
private static String USERNAME ;
private static String PASSWORD ;
private static HttpHost HOST;
public CausaleRepository(Context context)
{
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
USERNAME = prefs.getString("Username", "");
PASSWORD = prefs.getString("Password", "");
HOST = new HttpHost(prefs.getString("HostAddress", ""), prefs.getInt("HostPort", 80), "http");
}
public CausaleRepository(HttpHost host, String user, String password){
USERNAME = user;
PASSWORD = password;
HOST = host;
}
public List<Causale> GetAll() {
List<Causale> causali = new ArrayList<Causale>();
String uri = HOST.toURI() + "/causali/getall";
String responseValue;
try {
responseValue = new RestClient(HOST, USERNAME, PASSWORD).DoGetRequest(uri);
causali = GetCausaleListFromXML(responseValue);
} catch (HttpException e) {
e.printStackTrace();
}
return causali;
}
public Causale Get(Integer id) {
Causale causale = new Causale();
String responseValue;
String uri = HOST.toURI() + "/causali/get/" + id.toString();
try {
responseValue = new RestClient(HOST, USERNAME, PASSWORD).DoGetRequest(uri);
causale = GetCausaleFromXML(responseValue);
} catch (HttpException e) {
e.printStackTrace();
}
return causale;
}
public void Create(Causale causale) {
String uri = HOST.toURI() + "/causali/admin/create";
String responseValue;
String xml = GetXMLFromCausale(causale);
try {
responseValue = new RestClient(HOST, USERNAME, PASSWORD).DoPostRequest(uri, xml);
causale = GetCausaleFromXML(responseValue);
} catch (HttpException e) {
e.printStackTrace();
}
}
public Causale Update(Causale causale) {
Causale updatedCausale = new Causale();
String uri = HOST.toURI() + "/causali/admin/update";
String responseValue;
String xml = GetXMLFromCausale(causale);
try {
responseValue = new RestClient(HOST, USERNAME, PASSWORD).DoPutRequest(uri + "/" + causale.getId(), xml);
updatedCausale = GetCausaleFromXML(responseValue);
} catch (HttpException e) {
e.printStackTrace();
}
return updatedCausale;
}
public void Delete(Causale causale) {
Delete(causale.getId());
}
public void Delete(Integer causaleId) {
String uri = HOST.toURI() + "/causali/admin/delete";
try {
new RestClient(HOST, USERNAME, PASSWORD).DoDeleteRequest(uri + "/" + causaleId.toString());
} catch (HttpException e) {
e.printStackTrace();
}
}
I metodi per la serializzazione e deserializzazione XML dell’entità sono specifici per ogni entità, in quanto non ho trovato modo di serializzare/deserializzare entità generiche utilizzando la reflection.
private List<Causale> GetCausaleListFromXML(String xmlData) {
int parserEvent = -1;
XmlPullParser parser = null;
ArrayList<Causale> items = new ArrayList<Causale>();
String tag = "";
Causale item = null;
try {
parser = Helpers.loaXmldDataFromString(xmlData);
parserEvent = parser.getEventType();
while (parserEvent != XmlPullParser.END_DOCUMENT) {
switch (parserEvent) {
case XmlPullParser.START_TAG:
tag = parser.getName();
if (tag.compareTo(ROOT_TAG) == 0) {
item = new Causale();
}
break;
case XmlPullParser.END_TAG:
tag = parser.getName();
if (tag.compareTo(ROOT_TAG) == 0) {
items.add(item);
}
break;
case XmlPullParser.TEXT:
String text = parser.getText();
if (text.trim().length() == 0)
break;
if (tag.compareTo("Id") == 0) {
item.setId(Integer.parseInt(text));
} else if (tag.compareTo("Descrizione") == 0) {
item.setDescrizione(text);
} else if (tag.compareTo("Segno") == 0) {
item.setSegno(Integer.parseInt(text));
}
break;
}
parserEvent = parser.next();
}
} catch (XmlPullParserException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return items;
}
private Causale GetCausaleFromXML(String xmlData) {
// Not written because is almost the same as GetCausaleList method, except for the while loop
}
private String GetXMLFromCausale(Causale causale) {
// Create a XmlSerializer in order to write xml data
XmlSerializer serializer = Xml.newSerializer();
StringWriter writer = new StringWriter();
try {
serializer.setOutput(writer);
serializer.startDocument("UTF-8", false);
// set indentation option
// serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
serializer.setPrefix(null, NAMESPACE);
serializer.startTag(NAMESPACE, ROOT_TAG);
serializer.startTag(null, "Id");
serializer.text(String.valueOf(causale.getId()));
serializer.endTag(null, "Id");
serializer.startTag(null, "Descrizione");
serializer.text(causale.getDescrizione());
// set an attribute called "attribute" with a "value" for <child2>
// serializer.attribute(null, "attribute", "value");
serializer.endTag(null, "Descrizione");
serializer.startTag(null, "Segno");
serializer.text(String.valueOf(causale.getSegno()));
serializer.endTag(null, "Segno");
serializer.endTag(NAMESPACE, ROOT_TAG);
serializer.endDocument();
// write xml data into the stringWriter
serializer.flush();
return writer.toString();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Particolarità:
L’istruzione serializer.setPrefix(null, NAMESPACE); è necessaria per togliere i prefissi di qualificazione del namespace che verrebbero posti prima o dopo i nomi delle entità o delle proprietà che sembrano essere un po' indigesti lato .Net.
- Senza settare il prefisso a null, errore lato .Net, stringa xml sarà:
<?xml version='1.0' encoding='UTF-8' standalone='no' ?>
<n0:CausaleDto xmlns:n0="http://www.dev-spark.com/easymoneytracker/causale">
<Id>3</Id>
<Descrizione>Causale updated from Android</Descrizione>
<Segno>1</Segno>
</n0:CausaleDto>
- Settando il prefisso a null invece:
<?xml version='1.0' encoding='UTF-8' standalone='no' ?>
<CausaleDto xmlns="http://www.dev-spark.com/easymoneytracker/causale">
<Id>3</Id>
<Descrizione>Causale updated from Android</Descrizione>
<Segno>1</Segno>
</CausaleDto>
e infine, per il caricamento della stringa XML in nell’oggetto XmlPullParser:
public static XmlPullParser loaXmldDataFromString(String xmlData)
throws XmlPullParserException, IOException {
XmlPullParserFactory parserFactory = XmlPullParserFactory.newInstance();
XmlPullParser parser = parserFactory.newPullParser();
parser.setInput(new StringReader(xmlData));
return parser;
}

Related posts:
- WCF-REST Parte 1 - Creare servizi WCF-Rest in C#
- WCF-REST Parte 2 – Autenticazione
- WCF-REST Parte 3 - Client di un servizio WCF-Rest in C#