2

我正在将一个 Grails 3.1 库移植到 Grails 4.0,以便使用一些内部 Web 服务。其中一项服务根据请求提供所请求员工的图像。我很难实现(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()]
            }
        }

所以从输入流返回字节。这行得通。

我已经使用 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

这“有效”,但返回的字符串丢失了一些我无法反转的编码:

   // 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  
4

2 回答 2

1

它一定是Grails错误。


将此行添加到logback.groovy

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

然后你应该看到 body 不是空的,但最后它以 error 结尾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 跟踪没有错误。


这是一个从https://picsum.photos网站下载随机图像的声明式 HTTP 客户端示例:

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 中,测试通过并且图像被保存到测试文件中。但在 Grails 中,测试失败了,因为 HttpClient 无法将响应字节转换为byte数组,或者更好地转换为其他任何东西String

于 2019-09-04T20:06:37.263 回答
0

我们目前正在使用这个实现:

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

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

对我们来说很好。

当我使用 HttpResponse 时,我也会收到一个错误,无法让它工作。

于 2020-05-13T12:46:09.633 回答