如何使用micronaunt HttpClient

时间:2019-08-28 18:54:17

标签: download binary httpclient micronaut micronaut-client

我正在移植Grails 3.1库,以便使用一些内部Web服务到Grails 4.0。其中一项服务可应要求提供所要求员工的图像。我很难实现(micronaut)HttpClient代码来处理请求-特别是要获得正确的byte[]就是返回的图像。

命令行上的简单curl命令可与该服务一起使用:

curl -D headers.txt -H 'Authorization:Basic <encodedKeyHere>' https:<serviceUrl> >> image.jpg

,图像正确。 header.txt是:

HTTP/1.1 200 
content-type: image/jpeg;charset=UTF-8
date: Tue, 27 Aug 2019 20:05:43 GMT
x-ratelimit-limit: 100
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 99
x-ratelimit-remaining: 99
X-RateLimit-Reset: 38089
x-ratelimit-reset: 15719
Content-Length: 11918
Connection: keep-alive

旧库使用groovyx.net.http.HTTPBuilder并简单地执行:

http.request(Method.GET, ContentType.BINARY) {
            uri.path = photoUrlPath
            uri.query = queryString
            headers.'Authorization' = "Basic $encoded".toString()
            response.success = { resp, inputstream ->
                log.info "response status: ${resp.statusLine}"                
                return ['status':resp.status, 'body':inputstream.getBytes()]
            }
            response.failure = { resp ->
                return ['status':resp.status, 
                        'error':resp.statusLine.reasonPhrase, 
                         body:resp.getEntity().getContent().getText()]
            }
        }

因此从inputStream返回字节。这可行。

我已经使用micronaut HttpClient尝试了几种方法,包括低级API和声明性API。

带有声明性API的简单示例:


    @Get(value='${photo.ws.pathurl}', produces = MediaType.IMAGE_JPEG)
    HttpResponse<byte[]> getPhoto(@Header ('Authorization') String authValue, 
                                  @QueryValue("emplId") String emplId)

而不是在服务中:

    HttpResponse<byte[]> resp = photoClient.getPhoto(getBasicAuth(),emplId)
    def status = resp.status()            // code == 200 --> worked 
    def bodyStrOne = resp.getBody()       // nope: get Optional.empty 
    // Tried different getBody(class) -> Can't figure out where the byte[]s are   
    // For example can do:
    def buf = resp.getBody(io.netty.buffer.ByteBuf).value // Why need .value?
    def bytes = buf.readableBytes()       // Returns 11918 --> the expected value
    byte[] ans = new byte[buf.readableBytes()]
    buf.readBytes(ans)    // Throws exception: io.netty.util.IllegalReferenceCountException: refCnt: 0

这“有效”,但是返回的String丢失了一些我无法撤消的编码:

   // Client - use HttpResponse<String>
   @Get(value='${photo.ws.pathurl}', produces = MediaType.IMAGE_JPEG)
   HttpResponse<String> getPhoto(@Header ('Authorization') String authValue, 
                                  @QueryValue("emplId") String emplId)
   // Service 
   HttpResponse<String> respOne = photoClient.getPhoto(getBasicAuth(),emplId)
   def status = respOne.status()                  // code == 200 --> worked 
   def bodyStrOne = respOne.getBody(String.class) // <-- RETURNS DATA..just NOT an Image..or encoded or something
   String str = bodyStrOne.value                  // get the String data  
   // But these bytes aren't correct
   byte[] ans = str.getBytes()                    // NOT an image..close but not.
   // str.getBytes(StandardCharsets.UTF_8) or any other charset doesn't work 

我对ByteBuf类进行的所有尝试都抛出io.netty.util.IllegalReferenceCountException: refCnt: 0异常。

任何方向/帮助将不胜感激。

运行:

    Grails   4.0 
    JDK      1.8.0_221 
    Groovy   2.4.7
    Windows  10
    IntellJ  2019.2  

2 个答案:

答案 0 :(得分:0)

一定是Grails的错误。


将此行添加到logback.groovy

logger("io.micronaut.http", TRACE)

然后,您应该看到该主体不是空的,但最终它以错误Unable to convert response body to target type class [B结尾。看到痕迹:

2019-09-11 11:19:16.235 TRACE --- [ntLoopGroup-1-4] i.m.http.client.DefaultHttpClient        : Status Code: 200 OK
2019-09-11 11:19:16.235 TRACE --- [ntLoopGroup-1-4] i.m.http.client.DefaultHttpClient        : Content-Type: image/jpeg
2019-09-11 11:19:16.235 TRACE --- [ntLoopGroup-1-4] i.m.http.client.DefaultHttpClient        : Content-Length: 11112
2019-09-11 11:19:16.237 TRACE --- [ntLoopGroup-1-4] i.m.http.client.DefaultHttpClient        : Accept-Ranges: bytes
2019-09-11 11:19:16.237 TRACE --- [ntLoopGroup-1-4] i.m.http.client.DefaultHttpClient        : Response Body
2019-09-11 11:19:16.237 TRACE --- [ntLoopGroup-1-4] i.m.http.client.DefaultHttpClient        : ----
2019-09-11 11:19:16.238 TRACE --- [ntLoopGroup-1-4] i.m.http.client.DefaultHttpClient        : ���� C   
...
2019-09-11 11:19:16.241 TRACE --- [ntLoopGroup-1-4] i.m.http.client.DefaultHttpClient        : ----
2019-09-11 11:19:16.243 TRACE --- [ntLoopGroup-1-4] i.m.http.client.DefaultHttpClient        : Unable to convert response body to target type class [B

但是,如果您在独立的Microunaut应用程序中尝试相同的操作(将<logger name="io.micronaut.http" level="trace"/>添加到logback.xml中),结果将有所不同:

09:02:48.583 [nioEventLoopGroup-1-5] TRACE i.m.http.client.DefaultHttpClient - Status Code: 200 OK
09:02:48.583 [nioEventLoopGroup-1-5] TRACE i.m.http.client.DefaultHttpClient - Content-Type: image/jpeg
09:02:48.589 [nioEventLoopGroup-1-5] TRACE i.m.http.client.DefaultHttpClient - content-length: 23195
09:02:48.590 [nioEventLoopGroup-1-5] TRACE i.m.http.client.DefaultHttpClient - Response Body
09:02:48.590 [nioEventLoopGroup-1-5] TRACE i.m.http.client.DefaultHttpClient - ----
09:02:48.612 [nioEventLoopGroup-1-5] TRACE i.m.http.client.DefaultHttpClient - ���� C���
...
09:02:48.620 [nioEventLoopGroup-1-5] TRACE i.m.http.client.DefaultHttpClient - ----

Micronaut跟踪没有错误。


以下是声明性HTTP客户端的示例,该客户端从https://picsum.photos网站下载随机图像:

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

@Client('https://picsum.photos')
interface LoremPicsumClient {
    @Get(value = '{width}/{height}', produces = MediaType.IMAGE_JPEG)
    HttpResponse<byte[]> getImage(Integer width, Integer height)
}

Spock对其进行单元测试:

import io.micronaut.http.HttpStatus
import io.micronaut.test.annotation.MicronautTest
import spock.lang.Specification

import javax.inject.Inject
import java.nio.file.Files
import java.nio.file.Paths

@MicronautTest
class LoremPicsumClientSpec extends Specification {
    @Inject
    LoremPicsumClient client

    void 'image is downloaded'() {
        given:
        def output = Paths.get('test')

        when:
        def response = client.getImage(300, 200)

        then:
        response.status == HttpStatus.OK
        response.getBody().isPresent()

        when:
        Files.write(output, response.getBody().get())

        then:
        Files.probeContentType(output) == 'image/jpeg'
    }
}

在Micronaut中,测试通过,图像被保存到 test 文件中。但是在Grails中,测试失败,因为HttpClient无法将响应字节转换为byte数组,或者更好地转换为String之后的其他字节。

答案 1 :(得分:0)

我们当前正在使用此实现:

@Client(value = "\${image-endpoint}")
interface ImageClient {

  @Get("/img")
  fun getImageForAddress(
    @QueryValue("a") a: String
  ): CompletableFuture<ByteArray>
}

对我们来说很好。

当我使用HttpResponse时,我也收到一个错误,无法使其正常运行。