I need to make an API request using feign. Method type: POST
; Headers must include Content-Type = text/xml
.
My code:
@Component
@ConfigurationProperties("client.machine")
class MachineProperties {
lateinit var url: String
lateinit var timeout: String
}
@Configuration
class MachineClientConfig(
private val connectionProperties: MachineProperties,
private val meterRegistry: MeterRegistry,
private val objectMapper: ObjectMapper
) {
@Bean
fun machineApi() = Feign.builder()
.addCapability(MicrometerCapability(meterRegistry))
.retryer(DefaultEtsmIntegratonRetryer())
.encoder(JacksonEncoder(objectMapper))
.decoder(JacksonDecoder(objectMapper))
.decode404()
.logger(Slf4jLogger(MachineApi::class.java))
.options(Request.Options(connectionProperties.timeout.toInt(), connectionProperties.timeout.toInt()))
.target(MachineApi::class.java, connectionProperties.url)!!
}
@Headers(
HttpHeaders.ACCEPT + ": " + MediaType.TEXT_XML_VALUE,
HttpHeaders.CONTENT_TYPE + ": " + MediaType.TEXT_XML_VALUE
)
interface MachineApi {
@RequestLine("POST /api/v1/charge")
fun sendRequest(body: String): MachineResponse
}
@Service
class MachineClient(private val machineApi: MachineApi) {
fun sendRequest(body: String, dealId: Long): MachineResponse {
return machineApi.sendRequest(body)
}
}
public class JacksonEncoder implements Encoder {
private final ObjectMapper mapper;
public JacksonEncoder() {
this(Collections.<Module>emptyList());
}
public JacksonEncoder(Iterable<Module> modules) {
this(new ObjectMapper()
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
.configure(SerializationFeature.INDENT_OUTPUT, true)
.registerModules(modules));
}
public JacksonEncoder(ObjectMapper mapper) {
this.mapper = mapper;
}
@Override
public void encode(Object object, Type bodyType, RequestTemplate template) {
try {
JavaType javaType = mapper.getTypeFactory().constructType(bodyType);
template.body(mapper.writerFor(javaType).writeValueAsBytes(object), Util.UTF_8);
} catch (JsonProcessingException e) {
throw new EncodeException(e.getMessage(), e);
}
}
}
When I call the sendRequest method from the MachineClient class I get an error like this:
feign.FeignException$Forbidden: [403 Forbidden] during [POST] to [https://dev.ed.com/tsm/v1/charge] [MachineApi#sendRequest(String)]: [{
"httpCode": "403",
"moreInformation": "SQL-Injection Error"
}]
But when I try to make a call through curl it works great.
curl --location --request POST 'https://dev.ed.com/tsm/v1/charge' \
--header 'Content-Type: text/xml' \
--data-raw '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ChargeRq>...</ChargeRq>'
What am I missing in my code and why am I getting an error? Help please, I have no more ideas
The solution was as follows:
Since Content -Type = "text / xml" is required, I need to use JAXBEncoder. In this case, I need to send not a String, but a ChargeRqType - an object that was received using jaxb.
@Component
@ConfigurationProperties("client.machine")
class MachineProperties {
lateinit var url: String
lateinit var timeout: String
}
@Configuration
class MachineClientConfig(
private val connectionProperties: MachineProperties,
private val meterRegistry: MeterRegistry,
private val jaxbContextFactory: JAXBContextFactory,
private val objectMapper: ObjectMapper
) {
@Bean
fun creditMachineApi() = Feign.builder()
.addCapability(MicrometerCapability(meterRegistry))
.retryer(DefaultEtsmIntegratonRetryer())
.encoder(JAXBEncoder(jaxbContextFactory))
.decoder(JacksonDecoder(objectMapper))
.logger(Slf4jLogger(MachineApi::class.java))
.options(Request.Options(connectionProperties.timeout.toInt(), connectionProperties.timeout.toInt()))
.target(MachineApi::class.java, connectionProperties.url)!!
}
@Headers(
HttpHeaders.ACCEPT + ": " + MediaType.TEXT_XML_VALUE,
HttpHeaders.CONTENT_TYPE + ": " + MediaType.TEXT_XML_VALUE
)
interface MachineApi {
@RequestLine("POST /tsm/v1/charge")
fun sendRequest(body: ChargeRqType): MachineResponse
}
@Service
class MachineClient(private val machineApi: MachineApi) {
fun sendRequest(body: ChargeRqType, dealId: Long) =
machineApi.sendRequest(body)
}
@Configuration
class JAXBConfig {
@Bean
fun jaxbContextFactory() = JAXBContextFactory.Builder()
.withMarshallerJAXBEncoding("UTF-8")
.withMarshallerSchemaLocation(ClassPathResource("xsd/SrvCreateApp03.xsd").path)
.build()
}