我正在移植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
答案 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时,我也收到一个错误,无法使其正常运行。