目前在三种主流的Web服务实现方案中,因为REST模式的Web服务与复杂的SOAP和XML-RPC对比来讲明显的更加简洁,越来越多的web服务开始采用REST风格设计和实现。
AD:
首先从维基百科上拷贝一点Rest的基本概念给大家看看,然后我们再开始详解在Android中如何调用Rest服务。表象化状态转变(英文:Representational State Transfer,简称REST)是Roy Fielding博士在2000年他的博士论文中提出来的一种软件架构风格。
目前在三种主流的Web服务实现方案中,因为REST模式的Web服务与复杂的SOAP和XML-RPC对比来讲明显的更加简洁,越来越多的web服务开始采用REST风格设计和实现。例如,Amazon.com提供接近REST风格的Web服务进行图书查找;雅虎提供的Web服务也是REST风格的。
REST从资源的角度来观察整个网络,分布在各处的资源由URI确定,而客户端的应用通过URI来获取资源的表形。获得这些表形致使这些应用程序转变了其状态。随着不断获取资源的表形,客户端应用不断地在转变着其状态,所谓表形化的状态转变(Representational State Transfer)。
这一观点不是凭空臆造的,而是通过观察当前Web互联网的运作方式而抽象出来的。Roy Fielding 认为,
“设计良好的网络应用表现为一系列的网页,这些网页可以看作的虚拟的状态机,用户选择这些链接导致下一网页传输到用户端展现给使用的人,而这正代表了状态的转变。”
请关注51CTO专题:移动用户体验产品设计宝典
需要注意的是,REST是一种设计风格而不是一个标准。REST通常基于使用HTTP,URI,和XML以及HTML这些现有的广泛流行的协议和标准。
◆资源是由URI来指定。
◆对资源的操作包括获取、创建、修改和删除资源,这些操作正好对应HTTP协议提供的GET、POST、PUT和DELETE方法。
◆通过操作资源的表形来操作资源。
◆资源的表现形式则是XML或者HTML,取决于读者是机器还是人,是消费web服务的客户软件还是web浏览器。当然也可以是任何其他的格式。
REST的要求
◆客户端和服务器结构
◆连接协议具有无状态性
◆能够利用Cache机制增进性能
◆层次化的系统
◆Code On Demand – Javascript
实现举例
例如,一个简单的网络商店应用
列举所有商品
- GET http://www.store.com/products
具体某一件商品
- GET http://www.store.com/product/12345
下单购买
- POST http://www.store.com/order,
- <purchase-order>
- <item> … </item>
- </purchase-order>
下面我们就以如上网络商店的Rest服务为例,看看我们通过Android客户端是如何进行调用的(这里只介绍如何发送请求,并获得服务器响应)。
Android类库中已经为我们提供了一切我们需要的东西。
Rest的原理就是向一个资源的URI发送GET、POST、PUT和DELETE进行获取、创建、保存、删除操作。
第一步我们看看如何请求获得所有商品信息:
- //创建一个http客户端
- HttpClient client=new DefaultHttpClient();
- //创建一个GET请求
- HttpGet httpGet=new HttpGet("http://www.store.com/products");
- //向服务器发送请求并获取服务器返回的结果
- HttpResponse response=client.execute(httpGet);
- //返回的结果可能放到InputStream,http Header中等。
- InputStream inputStream=response.getEntity().getContent();
- Header[] headers=response.getAllHeaders();
通过解析服务器返回的流,我们可以将它转为字符串,获取相应的数据。
第二步可以向服务器增加商品,同样的道理,我们创建一个POST请求,带上相关的商品信息即可。
- //创建一个http客户端
- HttpClient client=new DefaultHttpClient();
- //创建一个POST请求
- HttpPost httpPost=new HttpPost("http://www.store.com/product");
- //组装数据放到HttpEntity中发送到服务器
- final List dataList = new ArrayList();
- dataList.add(new BasicNameValuePair("productName", "cat"));
- dataList.add(new BasicNameValuePair("price", "14.87"));
- HttpEntity entity = new UrlEncodedFormEntity(dataList, "UTF-8");
- httpPost.setEntity(entity);
- //向服务器发送POST请求并获取服务器返回的结果,可能是增加成功返回商品ID,或者失败等信息
- HttpResponse response=client.execute(httpPost);
第三步是如果修改商品信息,我们只需要创建一个PUT请求,带上要修改的参数即可。本例假设第三步中增加的商品返回ID为1234,下面将为商品的价格修改为11.99.
- //创建一个http客户端
- HttpClient client=new DefaultHttpClient();
- //创建一个PUT请求
- HttpPut httpPut=new HttpPut("http://www.store.com/product/1234");
- //组装数据放到HttpEntity中发送到服务器
- final List dataList = new ArrayList();
- dataList.add(new BasicNameValuePair("price", "11.99"));
- HttpEntity entity = new UrlEncodedFormEntity(dataList, "UTF-8");
- httpPut.setEntity(entity);
- //向服务器发送PUT请求并获取服务器返回的结果,可能是修改成功,或者失败等信息
- HttpResponse response=client.execute(httpPut);
第四步我们把上面增加的商品删除,只需要向服务器发送一个DELETE请求即可。
- //创建一个http客户端
- HttpClient client=new DefaultHttpClient();
- //创建一个DELETE请求
- HttpDelete httpDelete=new HttpDelete("http://www.store.com/product/1234");
- //向服务器发送DELETE请求并获取服务器返回的结果,可能是删除成功,或者失败等信息
- HttpResponse response=client.execute(httpDelete);
好了,就这么简单,这样就实现了从android客户端调用Rest服务对资源进行增、删、改、查操作。
参考文章二:
Android没有提供任何组件来直接调用WCF,但是我们可以通过第三方的包(例如:org.apache.http,org.json)来相对简单的调用REST形式的WCF服务。
第一步:.Net 基本实体Vehicle:
1 namespace HttpWcfWeb 2 { 3 [DataContract] 4 public class Vehicle 5 { 6 [DataMember(Name = "year")] 7 public int Year 8 { 9 get; 10 set; 11 } 12 13 [DataMember(Name = "plate")] 14 public string Plate 15 { 16 get; 17 set; 18 } 19 20 [DataMember(Name = "make")] 21 public string Make 22 { 23 get; 24 set; 25 } 26 27 [DataMember(Name = "model")] 28 public string Model 29 { 30 get; 31 set; 32 } 33 } 34 }
第二步,.Net基本wcf服务:
创建一个包含两个GET操作和一个POST操作的Service Contract。由于是通过JSON对象传输数据,这里需要指定Request和Response的数据格式为JSON。为了支持多个参数,还需要设置BodyStyle为WrappedRequest。
1 namespace HttpWcfWeb 2 { 3 [ServiceContract(Namespace = "http://services.example.com")] 4 public interface IVehicleService 5 { 6 [OperationContract] 7 [WebGet( 8 UriTemplate = "GetPlates", 9 BodyStyle = WebMessageBodyStyle.WrappedRequest, 10 ResponseFormat = WebMessageFormat.Json, 11 RequestFormat = WebMessageFormat.Json)] 12 IList<string> GetPlates(); 13 14 [OperationContract] 15 [WebGet(UriTemplate = "GetVehicle/{plate}", 16 BodyStyle = WebMessageBodyStyle.WrappedRequest, 17 ResponseFormat = WebMessageFormat.Json, 18 RequestFormat = WebMessageFormat.Json)] 19 Vehicle GetVehicle(string plate); 20 21 [OperationContract] 22 [WebInvoke( 23 Method = "POST", 24 UriTemplate = "SaveVehicle", 25 BodyStyle = WebMessageBodyStyle.WrappedRequest, 26 ResponseFormat = WebMessageFormat.Json, 27 RequestFormat = WebMessageFormat.Json)] 28 void SaveVehicle(Vehicle vehicle); 29 } 30 }
第三部:android调用wcf
1)对象列表
private void getVehicles() { try { // Send GET request to <service>/GetPlates HttpGet request = new HttpGet(SERVICE_URI + "/GetPlates"); request.setHeader("Accept", "application/json"); request.setHeader("Content-type", "application/json"); DefaultHttpClient httpClient = new DefaultHttpClient(); HttpResponse response = httpClient.execute(request); HttpEntity responseEntity = response.getEntity(); // Read response data into buffer char[] buffer = new char[(int)responseEntity.getContentLength()]; InputStream stream = responseEntity.getContent(); InputStreamReader reader = new InputStreamReader(stream); reader.read(buffer); stream.close(); JSONArray plates = new JSONArray(new String(buffer)); // Reset plate spinner ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); for (int i = 0; i < plates.length(); ++i) { adapter.add(plates.getString(i)); } plateSpinner.setAdapter(adapter); } catch (Exception e) { e.printStackTrace(); } }
2)获取单个对象实体:
它发送一个GET请求到WCF服务,通过plate number得到一个vehicle对象,但区别在于,它在结果处理中使用了JSONObject 转换,就像直接从WCF服务中拿到的vehicle对象一样。
public void GetVehicle(View button) { try { // Send GET request to <service>/GetVehicle/<plate> DefaultHttpClient httpClient = new DefaultHttpClient(); HttpGet request = new HttpGet(SERVICE_URI + "/GetVehicle/" + plateSpinner.getSelectedItem()); request.setHeader("Accept", "application/json"); request.setHeader("Content-type", "application/json"); HttpResponse response = httpClient.execute(request); HttpEntity responseEntity = response.getEntity(); // Read response data into buffer char[] buffer = new char[(int)responseEntity.getContentLength()]; InputStream stream = responseEntity.getContent(); InputStreamReader reader = new InputStreamReader(stream); reader.read(buffer); stream.close(); JSONObject vehicle = new JSONObject(new String(buffer)); // Populate text fields makeEdit.setText(vehicle.getString("make")); plateEdit.setText(vehicle.getString("plate")); modelEdit.setText(vehicle.getString("model")); yearEdit.setText(vehicle.getString("year")); } catch (Exception e) { e.printStackTrace(); } }3)保存
当Save按钮点击时,调用onSaveVehicleClick 方法。这个方法中简单的将所有的文本字段的值放入一个JSONObject对象,然后提交(POST)给WCF服务。注意所有的数据包装进了一个叫vehicle的对象,WCF收到后,会将其作为名称为vehicle的参数。
public void onSaveVehicleClick(View button) { try { Editable make = makeEdit.getText(); Editable plate = plateEdit.getText(); Editable model = modelEdit.getText(); Editable year = yearEdit.getText(); boolean isValid = true; // Data validation goes here if (isValid) { // POST request to <service>/SaveVehicle HttpPost request = new HttpPost(SERVICE_URI + "/SaveVehicle"); request.setHeader("Accept", "application/json"); request.setHeader("Content-type", "application/json"); // Build JSON string JSONStringer vehicle = new JSONStringer() .object() .key("vehicle") .object() .key("plate").value(plate) .key("make").value(make) .key("model").value(model) .key("year").value(Integer.parseInt(year.toString())) .endObject() .endObject(); StringEntity entity = new StringEntity(vehicle.toString()); request.setEntity(entity); // Send request to WCF service DefaultHttpClient httpClient = new DefaultHttpClient(); HttpResponse response = httpClient.execute(request); Log.d("WebInvoke", "Saving : " + response.getStatusLine().getStatusCode()); // Reload plate numbers refreshVehicles(); } } catch (Exception e) { e.printStackTrace(); } }