显示导航

Grails 及 Micronaut HTTP 客户端

在本指南中,我们将学习如何在 Grails 应用中使用 Micronaut HTTP 客户端。

作者:尼拉夫·阿萨尔

Grails 版本 5.0.1

1 Grails 培训

Grails 培训 - 由创建和积极维护 Grails 框架的人员开发和提供!。

2 开始

在本指南中,你将学习如何执行 HTTP 客户端,以进行外部 REST API 调用,以及执行 spock 测试。你将使用 Micronaut HTTP 客户端,它同时具有低级别 API 和更高级别 AOP 驱动的 API。

2.1 你需要

要完成本指南,你需要具备以下条件

  • 一些时间

  • 一个合适的文本编辑器或 IDE

  • 已安装的 JDK 1.8 或更高版本,并已适当配置 JAVA_HOME

2.2 如何完成指南

要开始,请执行以下操作

Grails 指南代码库包含两个文件夹

  • initial 初始项目。通常是一个简单的 Grails 应用,带有其他一些代码,让你轻松上手。

  • complete 一个完成的示例。它是按照指南中提供的步骤操作,并对 initial 文件夹应用这些更改的结果。

要完成指南,请转到 initial 文件夹

  • cd 进入 grails-guides/grails-micronaut-http/initial

并按照下一部分中的说明进行操作。

如果你 cd 进入 grails-guides/grails-micronaut-http/complete,则可以直接进入完成的示例

3 编写应用程序

/initial 文件夹是使用 rest-api 配置文件创建的 grails 应用程序。这是一个查询 Apple iTunes API 的简单应用程序。该应用程序接收 searchTerm,并分层为

  • example.grails.SearchController

  • example.grails.ItunesSearchService

调用被隐藏,直到我们在本指南中实施它

您应该看到返回的虚拟数据 JSON。

4 Micronaut HTTP 客户端

Micronaut HTTP 客户端依赖项包含在 grails rest-api 配置文件中,并替换了 datastore-rest-client。从 Grails 4 开始,它是适合 Grails 的推荐 HTTP 客户端。(旧的 HTTP 客户端在 Grails 4 中仍向后兼容)。

在我们的音乐应用程序中,我们将在服务功能和测试中使用 Micronaut HTTP 客户端,因此我们必须将 build.gradle 中的限定词更改为 compile

build.gradle
dependencies {
    ...
    implementation "io.micronaut:micronaut-http-client"
}

4.1 使用低级别 Micronaut HTTP 客户端 API

我们将查询艺术家的 iTunes 存储库,并期望以 JSON 形式接收多条信息,例如专辑名称和专辑的 URL 链接。为了轻松捕捉返回数据,我们可以创建属性名称与 JSON 结构完全相同的 POJO。ITunes API 将返回 resultCount,其中包含包含下列内容的专辑列表

  • artistName

  • collectionName

  • collectionViewUrl

请注意,以下 POJO 已被创建

src/main/groovy/example/grails/SearchResult.groovy
package example.grails

import groovy.transform.CompileStatic

@CompileStatic
class SearchResult {
    int resultCount
    List<Album> results = []
}
src/main/groovy/example/grails/Album.groovy
package example.grails

import groovy.transform.CompileStatic

@CompileStatic
class Album {
    String artistName
    String collectionName
    String collectionViewUrl
}

我们需要配置 Micronaut 以接受 iTunes API 的 text/javascript MIME 类型。

grails-app/conf/application.yml
---
micronaut:
    codec:
        json:
            additionalTypes:
                - text/javascript

ItunesSearchService 中,我们创建低级别客户端并使用 API 联系 iTunes.apple.com。从概念上讲,我们将使用 baseUrl 创建一个客户端,然后形成一个发送带有 URL 参数的 GET 请求的请求对象。随后,我们将发出阻塞请求并接收一个 String 响应。来自 Jackson Databind 的 ObjectMapper 将传入的 JSON 映射到 POJO。将以下内容添加到 ItunesSearchService

grails-app/services/example/grails/ItunesSearchService.groovy
    List<Album> searchWithApi(String searchTerm) {
        String baseUrl = "https://itunes.apple.com/"
        HttpClient client = HttpClient.create(baseUrl.toURL())
        HttpRequest request = HttpRequest.GET(UriBuilder.of('/search')
                .queryParam('limit', 25)
                .queryParam('media', 'music')
                .queryParam('entity', 'album')
                .queryParam('term', searchTerm)
                .build())
        HttpResponse<String> resp = client.toBlocking().exchange(request, String) (1)
        String json = resp.body()
        ObjectMapper objectMapper = new ObjectMapper()
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) (2)
        SearchResult searchResult = objectMapper.readValue(json, SearchResult) (3)
        searchResult.results
    }
1 发出具有阻塞调用的请求,并接受 String 响应。
2 忽略额外的属性。
3 将 JSON 映射到 POJO 中。

运行 ./gradlew bootRun 及点击 http://localhost:8080/search/searchWithApi?searchTerm=U2,然后查看结果。

4.2 使用声明声明 Micronaut HTTP 客户端

可通过 Micronaut 的声明式 HTTP 客户端实现相同的功能。可以在接口中声明 @Client 注解,并在编译时为您创建一个客户端。已通过 @Get 声明的界面方法并返回数据绑定的 POJO。这种简单且优雅的方法消除了先前示例中的大部分代码。

创建声明式客户端

src/main/groovy/example/grails/ItunesClient.groovy
package example.grails

import io.micronaut.http.annotation.Get
import io.micronaut.http.client.annotation.Client

@Client("https://itunes.apple.com/") (1)
interface ItunesClient {

    @Get("/search?limit=25&media=music&entity=album&term={term}") (2)
    SearchResult search(String term)
}
1 使用 @Client 声明并设置 URL。
2 使用参数定义 @Get 请求。

将客户端注入到服务中,然后向界面方法添加调用。

grails-app/services/example/grails/ItunesSearchService.groovy
...
    @Autowired
    ItunesClient itunesClient

    List<Album> searchWithDeclarativeClient(String searchTerm) {
        SearchResult searchResult = itunesClient.search(searchTerm)
        searchResult.results
    }
...

添加一个将路由转至服务方法的控制器方法

grails-app/controllers/example/grails/SearchController.groovy
...
    def searchWithDeclarativeClient(String searchTerm) {
        if(searchTerm) {
            List<Album> albums = itunesSearchService.searchWithDeclarativeClient(searchTerm)
            respond([searchTerm: searchTerm, albums: albums])
        }
    }
...

运行 ./gradlew bootRun,然后单击 http://localhost:8080/search/searchWithDeclarativeClient?searchTerm=U2 并查看结果。

5 使用 Micronaut HTTP 客户端进行测试

Micronaut 客户端还可用于使用 Spock 进行测试。在我们的音乐应用程序中,让我们创建一个域对象来获取唱片公司。我们将使用 grails 内置的 域类的 REST API

创建一个域对象,并使用 @Resource 和给定的 URI 对其进行标记。这将公开对象的 REST 功能。

grails-app/domain/example/grails/RecordLabel.groovy
package example.grails

import grails.rest.Resource

@Resource(uri='/recordlabels')
class RecordLabel {
    String name
}

BootStrap.groovy 中,为测试目的添加一些种子数据

grails-app/services/example/grails/RecordLabelService.groovy
package example.grails

import grails.gorm.services.Service

@Service(RecordLabel)
interface RecordLabelService {
    RecordLabel save(String name)
}
grails-app/init/example/grails/BootStrap.groovy
package example.grails

import groovy.transform.CompileStatic

@CompileStatic
class BootStrap {

    RecordLabelService recordLabelService

    def init = { servletContext ->
        recordLabelService.save("Warner")
        recordLabelService.save("Sony")
    }
    def destroy = {
    }
}

让我们创建一个集成测试套件,该套件首先执行 GET 请求以检索现有 RecordLabel 列表。测试还将发出 POST 以插入一个包含所传入数据的映射的唱片公司。在两个测试中,我们将验证响应是否返回正确。

src/integration-test/groovy/example/grails/RecordLabelControllerSpec.groovy
package example.grails

import grails.gorm.transactions.Rollback
import grails.testing.mixin.integration.Integration
import grails.testing.spock.OnceBefore
import io.micronaut.core.type.Argument
import io.micronaut.http.HttpRequest
import io.micronaut.http.HttpResponse
import io.micronaut.http.HttpStatus
import io.micronaut.http.client.HttpClient
import spock.lang.Shared
import spock.lang.Specification

@Integration
@Rollback
class RecordLabelControllerSpec extends Specification {

    @Shared
    HttpClient client

    @OnceBefore
    void init() { (1)
        String baseUrl = "http://localhost:$serverPort"
        this.client  = HttpClient.create(baseUrl.toURL())
    }

    void "test rest get record labels"() {
        when:"record labels exist"
        HttpResponse<List<Map>> resp = client.toBlocking().exchange(HttpRequest.GET("/recordlabels"), Argument.of(List, Map)) (2)

        then: "client can retrieve them"
        resp.status == HttpStatus.OK  (3)
        resp.body().size() == 2
        resp.body()[0].name == "Warner"
        resp.body()[1].name == "Sony"
    }

    void "test rest post record labels"() {
        when:"a post is issued"
        HttpResponse<Map> resp = client.toBlocking().exchange(HttpRequest.POST("/recordlabels", [name: "Universal"]), Map) (4)

        then: "element is created"
        resp.status == HttpStatus.CREATED (5)
        resp.body().size() == 2
        resp.body().id
        resp.body().name == "Universal"

    }
}
1 为所有测试初始化客户端一次。使用 serverPort 为其提供 URL,它是集成测试的已分配端口。
2 发出 GET。预期的正文类型将是映射列表。
3 验证 JSON 的状态和正文元素。
4 发送数据映射到 POST,该映射将作为 JSON 进行解释。
5 验证测试的状态和返回数据。

使用 ./gradlew check 测试应用程序。

6 运行应用程序

要运行应用程序,请使用 ./gradlew bootRun 命令,该命令将在 8080 端口启动应用程序。

7 使用 Grails 帮助

Object Computing, Inc. (OCI) 赞助了本指南的创建。可提供各种咨询和支持服务。

OCI 是 Grails 的所在地

认识团队