技术开发 频道

精通 Grails: Grails 服务和 Google 地图

  Grails 服务

  到目前为止,通过学习 精通 Grails 系列文章,您应该已经明白域类、控制器和 Groovy 服务器页面(Groovy Server Pages,GSP 是如何协调工作的。它们简化了在单一数据类型上执行基本的创建/检索/更新/删除(Create/Retrieve/Update/Delete,CRUD)操作。这个地理编码服务似乎略微超出了简单 Grails Object Relational Mapping(GORM)转换(从关系数据库记录到普通的旧 Groovy 对象(plain old Groovy objects,POGO))的范围。同样,这个服务很可能由多种方法使用。稍后您将看到,对 IATA 代码进行地理编码需要用到 save 和 update。Grails 为您提供了保存常用方法的位置,并且超越了任何单个的域类:即服务。

  要创建 Grails 服务,请在命令行输入 grails create-service Geocoder。在文本编辑器中查看 grails-app/services/GeocoderService.groovy,如清单 2 所示:

  清单 2. 一个无存根(stubbed-out)Grails 服务

class GeocoderService {
    
boolean transactional = true
    def serviceMethod() {

    }
}

  如果使用同一个方法进行多个数据库查询,那么将涉及到 transactional 字段。它将所有内容都包装在一个单个数据库事务中,如果任何一个查询失败,该数据库事务将回滚到原来的状态。因为在本示例中您远程地调用 Web 服务,所以可以安全地将它设置为 false。

  名称 serviceMethod 是一个占位符(placeholder),可以将其改为更具描述性的内容(服务可以包含任意多种方法)。在清单 3 中, 我把名称改为 geocodeAirport:

  清单 3. geocodeAirport() 地理编码器服务方法

class GeocoderService {
    
boolean transactional = false

    
// http://ws.geonames.org/search?name_equals=den&fcode=airp&style=full
    def geocodeAirport(
String iata) {
      def base
= "http://ws.geonames.org/search?"
      def qs
= []
      qs
<< "name_equals=" + URLEncoder.encode(iata)
      qs
<< "fcode=airp"
      qs
<< "style=full"
      def url
= new URL(base + qs.join("&"))
      def connection
= url.openConnection()

      def result
= [:]
      
if(connection.responseCode == 200){
        def xml
= connection.content.text
        def geonames
= new XmlSlurper().parseText(xml)
        result.name
= geonames.geoname.name as String
        result.lat
= geonames.geoname.lat as String
        result.lng
= geonames.geoname.lng as String
        result.state
= geonames.geoname.adminCode1 as String
        result.country
= geonames.geoname.countryCode as String
      }
      
else{
        
log.error("GeocoderService.geocodeAirport FAILED")
        
log.error(url)
        
log.error(connection.responseCode)
        
log.error(connection.responseMessage)
      }      
      return result
    }
}

  geocodeAirport 方法的第一部分构建 URL 并进行连接。查询字符串元素先集中在一个 ArrayList 里,然后和一个 & 符号连接起来。方法的最后部分使用 Groovy XmlSlurper 解析 XML 结果并将结果存储在 hashmap 里。

  Groovy 服务不可以直接从 URL 访问。如果您想在 Web 浏览器中测试这个新的服务方法,请将一个简单的闭包添加到 AirportController,如清单 4 所示:

  清单 4. 在控制器中向服务提供一个 URL

import grails.converters.*

class AirportController {
  def geocoderService
  def scaffold
= Airport
  
  def geocode
= {
    def result
= geocoderService.geocodeAirport(params.iata)
    render result
as JSON
  }  
  
  ...
}

  如果您定义一个与服务同名的成员变量,Spring 会自动地将服务注入控制器(要想让这种方法奏效,您必须把服务名的第一个字母由大写改为小写,使它遵循 Java 风格的变量命名约定)。

  要测试服务,请在 Web 浏览器中输入 URL http://localhost:9090/trip/airport/geocode?iata=den。您将看到如清单 5 所示的结果:

  清单 5. 地理编码器请求的结果

{"name":"Denver International Airport",
"lat":"39.8583188",
"lng":"-104.6674674",
"state":"CO",
"country":"US"}

  AirportController 中的 geocode 闭包只是用于对服务进行检查。因此,可以把它删除,或者保留下来供以后的 Ajax 调用使用。下一步是重新构造 Airport 基础设施,以利用这个新的地理编码服务。

0
相关文章