![](/img/trans.png)
[英]REST Web Service: Acceptable HTTP response content-type when responding with status 4XX (Client Error)
[英]Micronaut HTTP Client Fails to Bind Response that is Missing Content-Type Header
过去,我已经成功地将 Micronaut HTTP 客户端与几个不同的外部服务一起使用。 但是,我真的在为一项外部服务而苦苦挣扎。 我认为这可能与来自外部服务的响应不包含Content-Type
标头这一事实有关,但我不确定。
客户端和响应类型在同一个 groovy 文件中定义。
package us.cloudcard.api.transact
import groovy.transform.ToString
import io.micronaut.http.HttpResponse
import io.micronaut.http.MediaType
import io.micronaut.http.annotation.Body
import io.micronaut.http.annotation.Post
import io.micronaut.http.annotation.Produces
import io.micronaut.http.client.annotation.Client
import javax.validation.constraints.NotNull
@Client('${transact.url}')
interface TransactAuthenticationClient {
@Post
@Produces(MediaType.TEXT_PLAIN)
HttpResponse<TransactAuthenticationResponse> authenticate(@NotNull @Body String token)
}
@ToString
class TransactAuthenticationResponse {
Boolean Expired
String InstitutionId
String UserName
String customCssUrl
String email
String role
}
我正在使用一个简单的控制器对其进行测试,该控制器只调用客户端并呈现响应状态和正文。
package us.cloudcard.api
import grails.compiler.GrailsCompileStatic
import grails.converters.JSON
import grails.plugin.springsecurity.annotation.Secured
import io.micronaut.http.HttpResponse
import org.springframework.beans.factory.annotation.Autowired
import us.cloudcard.api.transact.TransactAuthenticationClient
import us.cloudcard.api.transact.TransactAuthenticationResponse
@GrailsCompileStatic
@Secured("permitAll")
class MyController {
static responseFormats = ['json', 'xml']
@Autowired
TransactAuthenticationClient transactAuthenticationClient
def show(String id) {
String goodToken = "5753D...REDACTED...647F"
HttpResponse response = transactAuthenticationClient.authenticate(goodToken)
TransactAuthenticationResponse authenticationResponse = response.body()
log.error("status: ${response.status()} body: $authenticationResponse")
render "status: ${response.status()} body: $authenticationResponse"
}
}
但是,我得到的结果是
status: OK body: null
调试时,我可以检查 HttpResponse 对象并查看所有正确的标头,因此我知道我正在成功发出请求。 我只是无法绑定响应。
我尝试将客户端更改为绑定到String
@Post
@Produces(MediaType.TEXT_PLAIN)
HttpResponse<String> authenticate(@NotNull @Body String token)
我得到了以下回复
status: OK body: PooledSlicedByteBuf(ridx: 0, widx: 176, cap: 176/176, unwrapped: PooledUnsafeDirectByteBuf(ridx: 484, widx: 484, cap: 513))
这很有趣,因为widx: 176, cap: 176/176
与成功响应的内容长度完美匹配。
我真的很茫然,所以我很感激你能提供任何帮助。
Micronaut HTTP 客户端无法使用响应中不包含content-type
标头的 API。 我与 Jeff Scott Brown 讨论过这个问题,这就是 Micronaut 的设计方式。 如果响应中没有content-type
标头,客户端将不知道如何解析响应正文。
package us.cloudcard.api.transact
import groovy.json.JsonSlurper
import groovy.transform.ToString
import org.apache.http.client.methods.CloseableHttpResponse
import org.apache.http.client.methods.HttpPost
import org.apache.http.entity.StringEntity
import org.apache.http.impl.client.CloseableHttpClient
import org.apache.http.impl.client.HttpClientBuilder
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component
@Component
class TransactAuthenticationClient {
@Value('${transact.url}')
String transactAuthenticationUrl
TransactAuthenticationResponse workaround2(String token) {
HttpPost post = new HttpPost(transactAuthenticationUrl)
post.addHeader("content-type", "text/plain")
post.setEntity(new StringEntity(token))
CloseableHttpClient client = HttpClientBuilder.create().build()
CloseableHttpResponse response = client.execute(post)
def bufferedReader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()))
def json = bufferedReader.getText()
println "response: \n" + json
def resultMap = new JsonSlurper().parseText(json)
return new TransactAuthenticationResponse(resultMap)
}
}
@ToString(includeNames = true)
class TransactAuthenticationResponse {
Boolean Expired
String InstitutionId
String UserName
String customCssUrl
String email
String role
}
TransactAuthenticationResponse thisAlsoDoesNotWork (String token) {
String baseUrl = "https://example.com"
HttpClient client = HttpClient.create(baseUrl.toURL())
HttpRequest request = HttpRequest.POST("/path/to/endpoint", token)
HttpResponse<String> resp = client.toBlocking().exchange(request, String)
String json = resp.body()
println "json: $json"
ObjectMapper objectMapper = new ObjectMapper()
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
TransactAuthenticationResponse response = objectMapper.readValue(json, TransactAuthenticationResponse)
return response
}
遇到同样的问题,这是我迄今为止找到的最佳解决方案:
import io.micronaut.core.convert.ConversionService;
import io.micronaut.core.convert.TypeConverter;
import io.netty.buffer.ByteBuf;
// (...)
ConversionService.SHARED.addConverter(ByteBuf.class, String.class, new TypeConverter<ByteBuf, String>() {
@Override
public Optional<String> convert(ByteBuf object, Class<String> targetType, ConversionContext context) {
return Optional.ofNullable(object).map(bb -> bb.toString(StandardCharsets.UTF_8));
}
});
HttpRequest<String> req = HttpRequest.POST("<url>", "<body>");
// res is instance of io.micronaut.http.client.netty.FullNettyClientHttpResponse which uses the shared conversion service as "last chance" to convert the response body
HttpResponse<String> res = httpClient.toBlocking().exchange(req);
String responseBody = res.getBody(String.class).get();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.