WCF-REST Parte 4 - Client di un servizio WCF-Rest in Android

Friday, August 20, 2010 3:47:00 AM Categories: Android Java Mobile WCF WebServices

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#

 

Copyright © Luca Cavicchioli


Site Map | Printable View | © 2008 - 2014 Luca Cavicchioli - P.IVA: IT-02770040364 | Powered by mojoPortal | HTML 5 | CSS | Design by styleshout