创建用于发出远程 JSON 请求的控制器方法
在本文的前面,您已经创建了一个 HotelController。将清单 15 中的 near 闭包添加到其中。(您在 “Grails 服务和 Google 地图” 中已经看到了类似的代码)。
清单 15. HotelController
def scaffold = Hotel
def near = {
def addr = "http://local.yahooapis.com/LocalSearchService/V3/localSearch?"
def qs = []
qs << "appid=YahooDemo"
qs << "query=hotel"
qs << "sort=distance"
qs << "output=json"
qs << "latitude=${params.lat}"
qs << "longitude=${params.lng}"
def url = new URL(addr + qs.join("&"))
render(contentType:"application/json", text:"${url.text}")
}
}
所有查询字符串参数都是硬编码的,但最后两个除外:latitude 和 longitude。倒数第二行实例化一个新的 java.net.URL。最后一行调用服务(url.text),并呈现结果。由于没有使用 JSON 转换器,因此必须显式地将 MIME-type 设置为 application/json。除非特意设置,否则 render 会返回 text/plain。
在浏览器中输入下面的内容(不要换行):
39.858409881591797&lng=-104.666999816894531
将结果与前面直接调用 http://local.yahooapis.com 的结果相比,两者应该是相同的。
使用控制器可以让远程 JSON 请求带来两个好处:可以规避同源 Ajax 限制(参见 为什么不能直接从浏览器远程调用 Web 服务? 侧边栏),但是更重要的是,它提供某种封装。控制器将变得与 Data Access Object(DAO)类似。
就像您不希望将 URL 硬编码到远程 Web 服务中一样,您也不希望在视图中出现原始的 SQL。现在,通过调用一个本地控制器,可以保证下游的客户机不受实现更改的影响。表名或字段名的更改会破坏嵌入式的 SQL 语句,URL 的更改则会破坏嵌入式的 Ajax 调用。而通过调用 AirportMapping.iata(),则就可以随意更改本地表和远程 GeoNames 服务中的数据源,并保证客户端界面不受影响。长远来看,为了提升性能,甚至可以将对远程服务的调用缓存到一个本地数据库,为每个请求构建本地缓存。
现在,这个服务已经可以工作,您可以从 Web 页面调用它。