科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道应用软件PHP Web服务的价值

PHP Web服务的价值

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

希望本文能够使您以正确的角度理解应用程序的web服务。我知道并没有讲述所有REST、XML/RPC或SOAP基础知识。

作者:PHPer 来源:CSDN 2008年2月25日

关键字: RUBY Web服务 PHP 软件

  • 评论
  • 分享微博
  • 分享邮件
Articles

  据我所知,Web服务是最酷的技术,但它最终会让大家觉得沉闷。不知道您感觉如何,每当我参加讨论web服务的演讲时,总是听到很多缩写,如REST、XML/RPC、SOAP和RSS。然后就开始打盹,梦到有个地方树上长满Krispy Kreme甜面卷。醒来时,我认识到实际上我做了很多web服务工作,并没完全用到那些毫无意义的标准程序。我看待web服务的方式非常简单,它就是web应用程序的备用接口,允许其他程序以编程语言可以理解的方式与我的应用程序通信。人们通过HTML与应用程序通信,应用程序通过XML、CSV或其他标准与我的应用程序通信。

  

  

  图1能更好地解释这个理论:

  图1. 传统的web应用程序与面向服务的架构(SOA)

  左侧为传统的web应用程序。服务器通过HTML与web客户端通信。该服务器可以是任何技术:Java、.NET、Rails、PHP、Python或ColdFusion。右侧为相同的web应用程序。但是它除了提供HTML,还使用XML通信,因此可以与其他应用程序或RSS阅读器之类的专用服务器交流。

  我经常听到人们谈论完全基于服务的应用服务器。这对于后端技术来说可能不错。但是对于普通的web应用程序,您总是希望它同时支持HTML和XML接口。拥有两种接口的一项好处在于,它强制您将业务逻辑集中到一个位置(可能是一个“中间层”),因此HTML和XML接口都可以与数据库通信并获得相同的结果。

  允许web应用程序通过XML与其他程序通信的价值对您来说可能显而易见,但是对我来说并非如此,至少最初是这样。因此本文讲述一个示例,演示通过在应用程序中设置XML接口可以实现哪些功能。首先讲述简单的HTML前端,然后显示如何构建XML接口并添加各种阅读器(包括Ajax、RSS和Adobe Flex)。

  文章应用程序

  首先讲述的测试应用程序是数据库中有一个文章列表的程序。清单1显示了这个数据库。

  清单 1. articles.sql

  DROP TABLE IF EXISTS articles;

  CREATE TABLE articles (

  id INTEGER NOT NULL AUTO_INCREMENT,

  title VARCHAR(255),

  author VARCHAR(255),

  description TEXT,

  PRIMARY KEY( id ) );

  INSERT INTO articles VALUES ( null,

  'What I like about dogs', 'Megan Herrington',

  'Everything that I love about dogs I learned in preschool' );

  INSERT INTO articles VALUES ( null,

  'Making action movies', 'Jack Herrington',

  'How to script, produce and direct Hong Kong action flicks' );

  INSERT INTO articles VALUES ( null,

  'Super Paper Mario Tips', 'Lori Herrington',

  'Everything you need to know to win at Paper Mario' );

  INSERT INTO articles VALUES ( null,

  'Why I bark', 'Oso Herrington',

  '' );

  该程序非常简单。它包含一个存储文章列表的简单表格;每篇文章都有标题、作者和描述。

  清单2显示此表格的详细HTML前端。

  清单2. articles.php

  

  

  

  

require_once( "DB.php" );

$db =& DB::Connect( 'mysql://root@localhost/articles1', array() );

if (PEAR::isError($db)) { die($db->  require_once( "DB.php" );

  $db =& DB::Connect( 'mysql://root@localhost/articles1', array() );

  if (PEAR::isError($db)) { die($db->getMessage()); }

  $res = $db->query( "SELECT * FROM articles" );

  $rows = array();

  while( $res->fetchInto($row, DB_FETCHMODE_ASSOC) ) {

  >

  

  

  0 ) { ?>

  

  

  

  

  

  

  如果一切工作正常并且设置了此数据库,就能在浏览器中导航到此页面时显示图2所示的页面。

  

  

  

  图2. 文章页面

  看到的内容并不多,但是我希望这个示例尽量简单。主要是因为后面的内容不容易处理。

  提供XML接口的一个主要原因是人们不必编写清单3所示的代码。

  清单 3. fetch.rb

  require 'net/http'

  articles = []

  Net::HTTP.start('localhost', 80) { |http|

  response = http.get('/ws/articles.php')

  body = response.body

  body.scan( /(

.*?)/m ) { |item|

  title = item[0].scan( /

(.*?)<\/div>/ )

  author = item[0].scan( /

(.*?)<\/div>/ )

  description = item[0].scan( /

(.*?)<\/div>/ )

  title = title[0][0] if ( title[0].length >0 )

  author = author[0][0] if ( author[0].length >0 )

  description = ( description.length >0 ) ? description[0][0] : ''

  articles.push( { :title => title,

  :author => author, :description => description } )

  }

  }

  p articles

  这是基于Ruby的“屏幕截取器”。这个脚本以一个大文本字符串获取页面,然后使用一组复杂的正则表达式解析页面的标题、作者和描述元素。

  在命令行运行时,将看到清单4所示的代码。

  清单4. 运行fetch.rb

  % ruby fetch.rb

  [{:author=>"Megan Herrington", :description=>"Everything that I love about dogs I

  learned in preschool", :title=>"What I like about dogs"}, {:author=>"Jack

  Herrington", :description=>"How to script, produce and direct Hong Kong action

  flicks", :title=>"Making action movies"}, {:author=>"Lori Herrington",

  :description=>"Everything you need to know to win at Paper Mario", :title=>"Super

  Paper Mario Tips"}, {:author=>"Oso Herrington", :description=>"", :title=>"Why I

  bark"}]

  是的,它获得了数据,但是不能获得这些元素的ID,因为HTML没有ID。

  您可能想知道为什么我选择使用Ruby实现。这主要是因为它很酷,并且易于阅读,可是这里却看不到这些优点。但这不是Ruby的错。屏幕截取器本身就很复杂。它们难于编写、易于出错、难以维护,即使对接口的HTML稍加修改就可能崩溃。本文中的所有屏幕截取器代码都只有一个目的:说服您对数据设置XML接口。如果您的数据很有趣,人们就能通过某种途径找到它。如果您不支持XML,当您更新站点以“美化”界面时,就会获得屏幕截取,并且触怒客户。

  添加XML服务

  因此,为了避免屏幕截取问题,并制作一些炫酷的程序来使用我们的数据,我将对表格编写一个XML接口,如清单5所示。

  清单5. artxml.php

  

require_once( "DB.php" );

$db =& DB::Connect( 'mysql://root@localhost/articles1', array() );

if (PEAR::isError($db)) { die($db->  require_once( "DB.php" );

  $db =& DB::Connect( 'mysql://root@localhost/articles1', array() );

  if (PEAR::isError($db)) { die($db->getMessage()); }

  $dom = new DomDocument();

  $dom->formatOutput = true;

  $root = $dom->createElement( "articles" );

  $dom->appendChild( $root );

  $res = $db->query( "SELECT * FROM articles" );

  $rows = array();

  while( $res->fetchInto($row, DB_FETCHMODE_ASSOC) ) {

  $art = $dom->createElement( "article" );

  $art->setAttribute( 'id', $row['id'] );

  $root->appendChild( $art );

  $title = $dom->createElement( "title" );

  $title->appendChild( $dom->createTextNode( $row['title'] ) );

  $art->appendChild( $title );

  $author = $dom->createElement( "author" );

  $author->appendChild( $dom->createTextNode( $row['author'] ) );

  $art->appendChild( $author );

  $desc = $dom->createElement( "description" );

  $desc->appendChild( $dom->createTextNode( $row['description'] ) );

  $art->appendChild( $desc );

  }

  header( "Content-type: text/xml" );

  echo $dom->saveXML();

  >

  在命令行运行此脚本时,将获得清单6所示的输出。

  清单6. 文章XML

  % php artxml.php

  

  

  

  

  Megan Herrington

  Everything that I love about dogs I learned in

  preschool

  

  

  

  Jack Herrington

  How to script, produce and direct Hong Kong action

  flicks

  

  ...

  这非常直观。有一个根文章标记,它包含一组文章标记。每个文章标记都有id属性(包含记录的数字id),以及存储相应数据的作者和描述标记。

  我使用了PHP中的XML文档对象模型(Document Object Model,DOM)功能,而不是手动编写标记。这样DOM将为我处理所有XML节点平衡和编码工作。这是确保页面返回的XM总是有效的简便方式。强烈推荐使用XML DOM功能来输出XML。所有主要的web语言都支持构建和导出XML DOM。

  获取XML

  上文中我展示了从HTML中提取数据的HTML和Ruby代码。既然拥有了此XML服务,下面将观察获得相同数据的Ruby代码片断,但是这次使用XML语言。清单7显示了XML提取代码。

  清单7. fetchxml.rb

  require 'net/http'

  require 'rexml/document'

  articles = []

  Net::HTTP.start('localhost', 80) { |http|

  response = http.get('/ws/artxml.php')

  body = response.body

  doc = REXML::Document.new body

  doc.each_element( '/articles/article' ) { |art|

  articles.push( {

  :id => art.attributes['id'],

  :title => art.elements['title'].text,

  :author => art.elements['author'].text,

  :description => art.elements['description'].text

  } )

  }

  }

  p articles

  这更简单。仍然能以相同的方式获得页面,但是将页面内存提供给REXML库,并使用XML功能轻松快捷地获得id、标题、作者和描述数据。此代码易于阅读、易于维护并且不会崩溃,除非XML格式发生变化,但这不太可能。

  作为比较,我使用C#编写了相同的代码以显示如何使用两种不同的语言阅读单个数据源。如清单8所示。

  清单8. WebServiceTest.cs

  using System;

  using System.IO;

  using System.Net;

  using System.Xml;

  namespace wstest1

  {

  class WebServiceTest

  {

  [STAThread]

  static void Main(string[] args)

  {

  HttpWebRequest r = (HttpWebRequest)WebRequest.Create(

  "http://localhost/ws/artxml.php" );

  WebResponse res = r.GetResponse();

  string sPage;

  StreamReader reader = new StreamReader( res.GetResponseStream() );

  sPage = reader.ReadToEnd();

  reader.Close();

  res.Close();

  XmlDocument doc = new XmlDocument();

  doc.LoadXml( sPage );

  foreach( XmlElement elArticle in doc.GetElementsByTagName( "article" ) )

  {

  string sTitle = (elArticle.SelectSingleNode( "title" )).InnerXml;

  string sAuthor = (elArticle.SelectSingleNode( "author" )).InnerXml;

  string sDescription = (elArticle.SelectSingleNode( "description"

  )).InnerXml;

  int nID = Int32.Parse( elArticle.Attributes["id"].Value );

  }

  }

  }

  }

  解决了本文中最难以处理的部分后,下面应该讨论有趣的事情了,比如以其他方式使用XML可以实现什么功能。

  在XSLT中使用XML

  等等,我刚才是不是说最难以处理的部分已经解决了?哦,不好意思,还有另外一项。结果发现使用XML Style Sheet或XSL可以快速设置XML数据格式。清单9所示的代码设置web服务(从articles.php页面写入到HTML)所返回的XML代码的格式。

  清单9. articles.xsl

  

xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

xmlns="http://www.w3.org/TR/xhtml1/strict">  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

  xmlns="http://www.w3.org/TR/xhtml1/strict">

  

  

  

  

  

  

  

  

  

  

  

  

  

  

  

  读起来有点不太容易,但是这是您的XSL。基本上,XSL是模式匹配器,我定义了匹配传入XML树的根标记的XSL模板。它输出HTML报头,然后使用for-each循环遍历每篇文章,并输出标题、作者和描述的值(如果有)。

  此样式表可以附加到XML输出本身,大多数浏览器将使用它将XML呈现到HTML以自动显示。怎么样!

  Ajax

  从应用程序中导出XML的最可能的理由是为了能够在web客户端中使用。客户端的JavaScript可以在加载页面之后从服务器请求XML,并以它所选择的任何方式(经常根据用户输入动态更改)呈现,并且不需要刷新页面。

  清单10显示了一个基于Ajax的简单表格,它呈现来自XML feed的数据。

  清单10. ajax.html

  

  

  

  

  

  此代码使用Prototype.js库从数据库访问数据,然后使用浏览器中的XML DOM功能访问作者、标题和描述字段。然后,使用HTML DOM函数针对数据集中的每篇文章向“articles”表格添加新行和单元格。

  图3显示了Ajax代码在浏览器中的输出。

  

  

  

  图3. 页面的Ajax版本

  这是非常基础的示例,但是完全不必向服务器端请求额外的数据,即可轻松地设想添加客户端排序或搜索。

  使用Flex访问XML

  下一代内容丰富的Internet应用程序框架(如Adobe Flex)是基于XML产生和发展起来的。因此可以轻松使用和显示XML数据。观察清单11所示的示例Flex应用程序。

  清单11. wstest.mxml

  

  

  

  

  

  

  

  

  

  

  

  

  

  其中并没有实际代码,仅仅是对XML数据源的引用,然后此数据源被传送到DataGrid控件。图4显示了输出。

  

  

  

  图4. 列表的Flex版本

  是不是很酷?实现此功能不需要任何代码。这只是Flex 和ActionScript使用XML可以实现的基本功能。ActionScript有一个内置的语言扩展,名为E4X。借助E4X,可以像使用“点标注”语法一样简单地导航XML文档树。这意味着没有太多沉闷的XML DOM方法,仅仅是直观对象和数组引用,就像内存中有任何其他数据结构一样。

  标准化

  我使用了仅适于本示例的XML风格。但是也可以使用标准XML格式实现,如RSS。清单12中的代码以RSS格式显示了相同数据库输出。

  清单12. artrss.php

  

require_once( "DB.php" );

$db =& DB::Connect( 'mysql://root@localhost/articles1', array() );

if (PEAR::isError($db)) { die($db->  require_once( "DB.php" );

  $db =& DB::Connect( 'mysql://root@localhost/articles1', array() );

  if (PEAR::isError($db)) { die($db->getMessage()); }

  $dom = new DomDocument();

  $dom->formatOutput = true;

  $rss = $dom->createElement( "rss" );

  $rss->setAttribute( "version", "0.91" );

  $dom->appendChild( $rss );

  $root = $dom->createElement( "channel" );

  $rss->appendChild( $root );

  $rtitle = $dom->createElement( "title" );

  $rtitle->appendChild( $dom->createTextNode( "Article list" ) );

  $root->appendChild( $rtitle );

  $rdesc = $dom->createElement( "description" );

  $rdesc->appendChild( $dom->createTextNode( "The article list" ) );

  $root->appendChild( $rdesc );

  $res = $db->query( "SELECT * FROM articles" );

  $rows = array();

  while( $res->fetchInto($row, DB_FETCHMODE_ASSOC) ) {

  $art = $dom->createElement( "item" );

  $root->appendChild( $art );

  $title = $dom->createElement( "title" );

  $title->appendChild( $dom->createTextNode( $row['title'] ) );

  $art->appendChild( $title );

  $title = $dom->createElement( "link" );

  $title->appendChild( $dom->createTextNode(

  "http://myhost/showarticle.php?id=".$row['id'] ) );

  $art->appendChild( $title );

  $desc = $dom->createElement( "description" );

  $desc->appendChild( $dom->createTextNode( $row['description'] ) );

  $art->appendChild( $desc );

  }

  header( "Content-type: text/xml" );

  echo $dom->saveXML();

  >

  此方法的好处在于,除了可以阅读XML的任何自定义代码之外,还可以使用所有的RSS工具。例如,可以在feed中指向我的Firefox浏览器,就会创建可以放到工具栏并检查更新的“活动书签”,如图5所示。

  

  

  

  图5. 使用Firefox的RSS feed

  当然,不是所有数据都能方便地设置为RSS格式,这没什么问题。但是如果可以成为RSS、RDF或任何其他方便的XML格式。那么最好遵循这些格式,而不是自己发明。

  结束语

  希望本文能够使您以正确的角度理解应用程序的web服务。我知道并没有讲述所有REST、XML/RPC或SOAP基础知识。有很多文章讨论过这些技术,多年以来,技术人员已经历过很多基于标准的噩梦。相反,我希望展示从应用程序中获得XML数据并以实用的方式使用它是件多么轻松的事情。如果我成功了,请写信告知我并展示从您的应用程序中提取的XML数据。也许我们可以使用其他web服务一起完成一个mash-up。

  资源

  Flex是基于开放源码的内容丰富的Internet应用程序开发环境,由Adobe提出。

  REST是简单的web服务标准,用来更直接地映射到HTTP协议。

  SOAP是HTTP协议之上的高级的对象方法调用协议。

  XML/RPC是HTTP协议之上的中间层方法调用协议,与SOAP相比,是略微轻量级的协议。

  RSS是聚合标准,用于博客条目和新文章之类的内容。

  Google的Reader服务就是web浏览器或智能电话的强大、免费的RSS管理器。

  Prototype.js是免费的JavaScript库,可以帮助编写易于维护的跨浏览器Ajax代码。  

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章