Invalid Content-Type:text/html - SOAP client - spring-boot

I am implementing a soap client using spring boot. Currently i am able to test it on Postman and it works fine. However when i try to send thesame request from spring i get invalid content type- text/html
I will be posting details below
From postman
Now this is the wsdl and server
https://xpartner.net.pl/soap2.wsdl
https://xpartner.net.pl/wsdlSoapServ2.php
Now in spring boot pom file i have the plugin as follows
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version>0.14.0</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<configuration>
<schemaLanguage>WSDL</schemaLanguage>
<generatePackage>com.autokonto.pl.xpartner</generatePackage>
<schemas>
<schema>
<url>https://xpartner.net.pl/soap2.wsdl</url>
</schema>
</schemas>
</configuration>
</plugin>
And config
#Configuration
public class XpartnerConfig {
#Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("com.autokonto.pl.xpartner");
return marshaller;
}
#Bean
public XpartnerClient xpartnerClient(Jaxb2Marshaller marshaller) {
XpartnerClient client = new XpartnerClient();
client.setDefaultUri("https://xpartner.net.pl/soap2.wsdl");
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
return client;
}
}
One question here is the
client.setDefaultUri("https://xpartner.net.pl/soap2.wsdl");
the uri required is the wsdl ? or soap server. anyways i tried using both
Client looks like this
#Slf4j
public class XpartnerClient extends WebServiceGatewaySupport {
public LoginResultClass login(JAXBElement<LoginDataClass> loginDataClass) {
LoginResultClass response = (LoginResultClass) getWebServiceTemplate().marshalSendAndReceive(loginDataClass);
return response;
}
}
Which is eventually called from the service
looks like this
#Slf4j
#Service
#RequiredArgsConstructor
public class XpartnerApiService {
private final static javax.xml.namespace.QName _LOGIN_DATA_CLASS_QNAME = new javax.xml.namespace.QName("http://schemas.xmlsoap.org/soap/encoding/", "loginDataClass");
#Value("${xpartner.login}")
private String login;
#Value("${xpartner.password}")
private String password;
LoginResultClass loginResultClass;
private final XpartnerClient xpartnerClient;
#PostConstruct
void login() {
log.info("log in xpartnerClient");
ObjectFactory objectFactory = new ObjectFactory();
LoginDataClass data = objectFactory.createLoginDataClass();
data.setLogin(login);
data.setPass(password);
JAXBElement<LoginDataClass> jaxbLoginDataClass = createLoginDataClass(data);
this.loginResultClass = xpartnerClient.login(jaxbLoginDataClass);
log.info("LOGGING RESULT" + loginResultClass.isLoginResult());
}
#XmlElementDecl(namespace = "http://schemas.xmlsoap.org/soap/encoding", name = "loginDataClass")
public JAXBElement<LoginDataClass> createLoginDataClass(LoginDataClass value) {
return new JAXBElement<LoginDataClass>(_LOGIN_DATA_CLASS_QNAME, LoginDataClass.class, null, value);
}
}
Finally the erro i get is
Error creating bean with name 'xpartnerApiService': Invocation of init method failed; nested exception is org.springframework.ws.soap.SoapMessageCreationException: Could not create message from InputStream: Invalid Content-Type:text/html. Is this an error message instead of a SOAP response?; nested exception is com.sun.xml.messaging.saaj.SOAPExceptionImpl: Invalid Content-Type:text/html. Is this an error message instead of a SOAP response?
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:160) ~[spring-beans-5.3.5.jar:5.3.5]
How can i fix this, is there a way to state that i need content-type to be text/xml
My understanding is that the error comes the request sent but not sure why.
From the logs i have found why im getting this error. its because the request created looks like this
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns3:loginDataClass xmlns:ns2="https://xpartner.net.pl/xpartner" xmlns:ns3="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns4="https://xpartner.net.pl/xpartner/">
<ns2:login>46084_0</ns2:login>
<ns2:pass>a6jCVzeJ3mCpNJ8</ns2:pass>
</ns3:loginDataClass>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
so it returns html with an error that says
Procedure 'loginDataClass' not present in /var/www/www.xpartner.net.pl/www/wsdlSoapServ2.php on line
how can i change the request to look like this
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<login xmlns="https://xpartner.net.pl/xpartner/">
<obj>
<login>dsdsdff</login>
<pass>sccccsccccc</pass>
</obj>
</login>
</Body>
</Envelope>

Related

No endpoint mapping found for [SaajSoapMessage {http://com.springbootsoap.allapis}addProductRequest]

I have been having issues trying to get endpoint mapping to work for my web service. I am using Tomcat to host my web service and I am using soapUI to send test messages to it.
Endpoint
#Endpoint
public class ProductEndpoint {
private static final String NAMESPACE_URL="http://com.springbootsoap.allapis";
#Autowired
ProductService productService;
#PayloadRoot(namespace = NAMESPACE_URL, localPart = "addProductRequest")
#ResponsePayload
public AddProductResponse addProduct(#RequestPayload AddProductRequest request) {
AddProductResponse response= new AddProductResponse();
ServiceStatus servicestatus=new ServiceStatus();
Product product=new Product();
BeanUtils.copyProperties(request.getProductInfo(),product);
productService.addProduct(product);
servicestatus.setStatus("Success");
servicestatus.setMessage("Content Added Successfully");
response.setServiceStatus(servicestatus);
return response;
}
#PayloadRoot(namespace = NAMESPACE_URL, localPart = "getProductByIdRequest")
#ResponsePayload
public GetProductResponse GetProduct(#RequestPayload GetProductByIdRequest request) {
GetProductResponse response=new GetProductResponse();
ProductInfo productInfo=new ProductInfo();
BeanUtils.copyProperties(productService.getProductById(request.getProductId()),productInfo);
response.setProductInfo(productInfo);
return response;
}
}
SoapUI
enter image description here
here is what I got in soapUi.
I do not have any idea what should I do to make it correct, I saw many questions regarding this problem but did not find any solution.
I also had the same issue. At that time I change the version of java to 1.8 in pom.xml file

Fail to consume SOAP WS with Spring-WS in Spring boot but works from SOAPUI

I'm using Spring Boot to consume a SOAP WS which I generate from a WSDL. I added the spring-ws-security so I can pass the user/password as security header as shown in the configuration:
#Configuration
public class ClientConfig {
public static final String SIEBEL_ENDPOINT = "http://...";
#Bean
public CustomerClient customerClient() {
CustomerClient client = new CustomerClient();
client.setDefaultUri(SIEBEL_ENDPOINT);
client.setWebServiceTemplate(webServiceTemplate(marshaller()));
return client;
}
#Bean
public WebServiceTemplate webServiceTemplate(Jaxb2Marshaller marshaller) {
WebServiceTemplate template = new WebServiceTemplate(marshaller, marshaller);
template.setDefaultUri(SIEBEL_ENDPOINT);
ClientInterceptor[] interceptors = new ClientInterceptor[] {new LogHttpHeaderClientInterceptor(), wsSecurityInterceptor()};
template.setInterceptors(interceptors);
return template;
}
#Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("com.test.dms.gen");
return marshaller;
}
#Bean
public Wss4jSecurityInterceptor wsSecurityInterceptor() {
Wss4jSecurityInterceptor wss4jSecurityInterceptor = new Wss4jSecurityInterceptor();
wss4jSecurityInterceptor.setSecurementActions(WSHandlerConstants.USERNAME_TOKEN);
wss4jSecurityInterceptor.setSecurementPasswordType(WSConstants.PW_TEXT);
wss4jSecurityInterceptor.setSecurementUsername("rf_USER");
wss4jSecurityInterceptor.setSecurementPassword("rf_USER");
return wss4jSecurityInterceptor;
}
}
And the service call:
public class CustomerClient extends WebServiceGatewaySupport {
public CustomerInfoOutput getCustomerInfo(String vin) {
ObjectFactory request = new ObjectFactory();
final CustomerInfoInput custInfoInput = request.createCustomerInfoInput();
custInfoInput.setVINNumber(vin);
return (CustomerInfoOutput) getWebServiceTemplate().marshalSendAndReceive(ClientConfig.SIEBEL_ENDPOINT, custInfoInput);
}
}
everything is well generated, and this logged output:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
SOAP-ENV:mustUnderstand="1">
<wsse:UsernameToken wsu:Id="UsernameToken-e8f183db-44db-4c0b-90d9-ca57e89225fd">
<wsse:Username>rf_USER</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">rf_USER</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns3:CustomerInfo_Input xmlns:ns2="http://siebel.com/testdashboard"
xmlns:ns3="http://test.com/rf/customerinfo" xmlns:ns4="http://test.com/rf"
xmlns:ns5="http://www.siebel.com/xml/IBM%20test%20Dashboard">
<ns3:VINNumber>123456789</ns3:VINNumber>
</ns3:CustomerInfo_Input>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
When I send this request using SOAP UI, it works perfectly. But when it's sent using the generated objects from the WSDL, I have this error:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Server</faultcode>
<faultstring>There is no active Web Service with operation named
'http://test.com/rf/customerinfo:CustomerInfo_Input'.(SBL-EAI-04313)
</faultstring>
<detail>
<siebelf:siebdetail xmlns:siebelf="http://www.siebel.com/ws/fault">
<siebelf:logfilename>EAIObjMgr_enu_0023_24117286.log</siebelf:logfilename>
<siebelf:errorstack>
<siebelf:error>
<siebelf:errorcode>SBL-EAI-04313</siebelf:errorcode>
<siebelf:errorsymbol>IDS_EAI_WS_OP_NOT_FOUND</siebelf:errorsymbol>
<siebelf:errormsg>There is no active Web Service with operation named
'http://test.com/rf/customerinfo:CustomerInfo_Input'.(SBL-EAI-04313)
</siebelf:errormsg>
</siebelf:error>
</siebelf:errorstack>
</siebelf:siebdetail>
</detail>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Any ideas please?
PS: please don't focus on URIs because I changed them, but the generated request works fine in SOAPUI.
With the hint of #RanjithR I processed as follow:
I traced the SOAP Call using Wireshark, and I discovered that Spring WS doesn't include the header SOAPAction. The following SOF question talks about this, and how to configure it (it sends you to the spring documentation which explains well the thing). Spring-WS client not setting SOAPAction header
The second thing is that, even if I added the header, it continues to tell me that the endpoint is not sent.
In my WSDL, the SOAPAction is defined as follows:
<soap:operation soapAction="document/http://test.com/rf/customerinfo:CustomerInfo"></soap:operation>
And my Spring ws call was like that:
return getWebServiceTemplate().marshalSendAndReceive(ClientConfig.SIEBEL_ENDPOINT,
customerInfoInput,
webServiceMessage -> ((SoapMessage)webServiceMessage).setSoapAction("document/http://ripl.com/rforce/customerinfo:CustomerInfo"));
In Wireshark, I had:
SOAPAction: "http://ripl.com/rforce/customerinfo:CustomerInfo"
But when I trace the same call from SOAP UI I have:
SOAPAction: "document/http://ripl.com/rforce/customerinfo:CustomerInfo"
So I tried to send a string literal as a SOAPAction:
"\"document/http://ripl.com/rforce/customerinfo:CustomerInfo\"";
Note the \"\" around the action and it works :)
Maybe it can help someone that have to integrate with Jurasik Park systems...
This post saved me a lot of time by pointing me in the right direction. The Action header was not added for me using the method described above, but the following method worked fine:
// add action header
final String ActionHeaderName = "Action";
final String ActionHeaderPrefix = "wsa";
final String ActionHeaderNamespace = "http://www.w3.org/2005/08/addressing";
final String ActionHeaderContent = "http://example/ActionHeader";
var actionName = soapEnvelope.createName(ActionHeaderName, ActionHeaderPrefix, ActionHeaderNamespace);
var actionHeader = soapEnvelope.getHeader().addHeaderElement(actionName);
actionHeader.setTextContent(ActionHeaderContent);
Thanks a lot #Marouane

Type The annotation #ResponsePayload is disallowed for this location

I had make a endpoint to my SOAP application and when a make this:
#ResponsePayload
public GetCountryResponse getCountry(#ResponsePayload GetCountryRequest request) {
GetCountryResponse response = new GetCountryResponse();
response.setCountry(countryRepository.findCountry(request.getName()));
}
This second #ResponsePayload in the method getCountry give me this error:
The annotation #ResponsePayload is disallowed for this location
In ResponsePayload doc we can find below:
Annotation which indicates that a method return value should be bound
to the response payload. Supported for annotated endpoint methods.
SO using #RequestPayload instead of #ResponsePayload
#ResponsePayload
public GetCountryResponse getCountry(#RequestPayload GetCountryRequest request) {
GetCountryResponse response = new GetCountryResponse();
response.setCountry(countryRepository.findCountry(request.getName()));
}
More details can be found at ResponsePayload and RequestPayload

When URL suffixed by a file extension, Spring Restful ControllerAdvice exception handler response html

I use #RestControllerAdvice to handle exception thrown by a controller globally, and response json string to client. My rest controller #RequestMapping path can accept URL suffixed by a file extension. If the controller throw a exception and URL is suffixed by a known extension , the exception handler will response html instead of json.
build.gradle
...
dependencies {
compile 'com.google.code.gson:gson:2.7'
compileOnly 'org.apache.tomcat:tomcat-servlet-api:8.0.33'
compile 'org.springframework:spring-webmvc:4.3.1.RELEASE'
}
servlet-context.xml
...
<mvc:annotation-driven/>
<context:component-scan base-package="com.demo"/>
...
DemoController.java
#RestController
public class DemoRestController {
#RequestMapping(value = "/{name:.+}")
public String doSomething(#PathVariable String name){
throw new RuntimeException(name);
}
}
RestExceptionHandler
#RestControllerAdvice
public class RestExceptionHandler {
#ExceptionHandler(Exception.class)
public Message handleException(Exception ex, WebRequest request) {
Message ret=new Message(System.currentTimeMillis(),ex.getMessage());
return ret;
}
}
client
$ curl localhost:8080/abc //no extension, it's ok
{"time":1479235304196,"url":"abc"}
$ curl localhost:8080/abc.opq //unknown extension, ok
{"time":1479235545303,"url":"abc.opq"}
$ curl localhost:8080/abc.jpg
<!DOCTYPE html><html><head><title>Apache Tomcat/8.0.33 - Error report</title> ...
...
the last output is html, it is not i want, what's matter? can you help me , thank you !
It seems that if the request path has an unknown extension then Spring doesn't know what to do with the return value from handleException and falls back to HTML. You can fix this by rendering JSON directly in your handleException method. This worked in my case because my API always returns JSON in the case of errors, instead of protobuf or csv or whatever.
#RestControllerAdvice(annotations = {RestController.class})
public class ApiExceptionHandler {
private final ObjectMapper objectMapper;
public ApiExceptionHandler(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
#ExceptionHandler
public void exceptionHandler(HttpServletRequest req, HttpServletResponse res, Exception x) throws IOException {
Message ret = new Message(System.currentTimeMillis(),ex.getMessage());
res.setStatus(500);
res.setContentType(MediaType.APPLICATION_JSON_UTF8.toString());
objectMapper.writeValue(res.getOutputStream(), ret);
}
}

Spring MVC Test with Validation JSR-303/349: Locale or accept-language does not work in testing

I am working with Spring Framework 4.3.3
I have the following situation for a #Controller either for POST or PUT
For example for POST I have the following:
#PostMapping(value="/save",
consumes=MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces=MediaType.TEXT_HTML_VALUE)
public String saveOne(#Validated #ModelAttribute Persona persona,
BindingResult result,
RedirectAttributes redirectAttributes){
logger.info("saveOne: {}", persona.toString());
if(result.hasErrors()){
logger.error("# Errors: {}", result.getErrorCount());
logger.error("result: {}", result.toString());
return "persona/saveOne";
}
personaRestController.saveOne(persona);
redirectAttributes.addFlashAttribute("message", "process.successful");
return "redirect:/message";
}
The app works how is expected, through a Web Form this #PostMapping either saves/persists the data or validates the data and if it is invalid the same web form is shown with the error messages for each field and based about i18n such as English, Spanish and Portuguese. It related with JSR-439
About Java config infrastructure configuration I have the following:
#Bean
public LocaleChangeInterceptor localeChangeInterceptor(){
LocaleChangeInterceptor localeChangeInterceptor=new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("language");
//localeChangeInterceptor.setHttpMethods("post", "put");
return localeChangeInterceptor;
}
#Bean(name="localeResolver")
public LocaleResolver cookieLocaleResolver(){
CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver();
cookieLocaleResolver.setCookieName("language");
cookieLocaleResolver.setDefaultLocale(new Locale("en","US"));
return cookieLocaleResolver;
}
And
#EnableWebMvc
#Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
...
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptorConfig.localeChangeInterceptor());
}
The problem is about Spring MVC Test. I have the following:
#Before
public void setUp(){
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
...
#Test
public void saveOneHtmlPostInvalidMaxTest() throws Exception {
logger.info("saveOneHtmlPostInvalidMaxTest");
resultActions = mockMvc.perform(post(url)
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.locale(locale)
.header("accept-language", locale.toString())
.header("language", locale.toString())
.param("id", personaInvalidMax.getId())
.param("nombre", personaInvalidMax.getNombre())
.param("apellido", personaInvalidMax.getApellido())
....
.andDo(print());
Note observe:
.locale(locale)
.header("accept-language", locale.toString())
.header("language", locale.toString())
Even when some of them have no sense, they are there because I am trying to resolve the following situation:
The validation control from the server side works how is expected until some point.
First
I can confirm through .andDo(print()) method that MockHttpServletRequest prints
Headers = {Content-Type=[application/x-www-form-urlencoded],
accept-language=[pt_BR], language=[pt_BR]}
Observe pt_BR
Second
The #PostMapping that contains:
if(result.hasErrors()){
logger.error("# Errors: {}", result.getErrorCount());
logger.error("result: {}", result.toString());
return "persona/saveOne";
}
Prints...
7370 [Test worker] ERROR PersonaSaveOneController - result:
org.springframework.validation.BeanPropertyBindingResult: 4 errors
Field error in object 'persona' on field 'id': rejected value [0];
codes [Size.persona.id,Size.id,Size.java.lang.String,Size]; arguments
[org.springframework.context.support.DefaultMessageSourceResolvable:
codes [persona.id,id]; arguments []; default message [id],3,3];
default message [The size for the field must be between 3 and 3]
Observe the default message [id],3,3];
default message [The size for the field must be between 3 and 3]
Here two problems:
The locale or accept-language are ignored
The error messages prints always in English
What I need is that the error data prints for example in Portuguese and Spanish according the Locale sent. It should returns the error messages for each field in an expected language, in this case Portuguese
Again, it works when I use directly the Web Form, but not through the Test. I am assuming that exists an internal Spring component that works through production and has not been defined for Testing
Therefore, what is need it to accomplish this approach?
BTW: With the following (see I use classpath:), furthermore my .properties file are located in src/main/resources
#Bean(name="messageSource")
public ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource() {
ReloadableResourceBundleMessageSource resource = new ReloadableResourceBundleMessageSource();
resource.setBasenames(...
"classpath:/com/manuel/jordan/validation/validation");
resource.setDefaultEncoding("UTF-8");
return resource;
}
My #Test methods working around with Rest works fine about show and returning the error message according the Locale or accept-language defined (Access to MessageSource happens in the server side and works fine with the previous configuration). It just fails for non-Rest controllers

Resources