I thought this would be as easy as adding an annotation but I can't find a solution to this.
I have a simple endpoint that takes an XML request body:
#RequestMapping(value = "/import", method = RequestMethod.POST, consumes = MediaType.TEXT_XML_VALUE)
public ResponseEntity<Result> importReceipts(#Valid #RequestBody ImportRequest request) throws Exception {
Where ImportRequest is a JAXB class generated from an XSD. This works fine when a client sends a request, but if the request is not valid there is not error.
Please can anyone suggest the best way to validate this request body given the XSD?
Thanks
Thanks Alex,
I saw this response earlier but I looked again at my code and spotted the error :)
#Bean
public MarshallingHttpMessageConverter marshallingHttpMessageConverter()
{
MarshallingHttpMessageConverter marshallingHttpMessageConverter = new MarshallingHttpMessageConverter();
marshallingHttpMessageConverter.setMarshaller(jaxb2Marshaller());
marshallingHttpMessageConverter.setUnmarshaller(jaxb2Marshaller());
return marshallingHttpMessageConverter;
}
#Bean
public Jaxb2Marshaller jaxb2Marshaller()
{
Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
jaxb2Marshaller.setSchemas(new ClassPathResource("Import.xsd"), new ClassPathResource("BasicTypes.xsd"));
jaxb2Marshaller.setClassesToBeBound(Import.class);
return jaxb2Marshaller;
}
I had a typo but the main problem was I called jaxb2Marshaller.setSchemas more than once and the second call removed the first schemas.
Related
I have a requirement to integrate OpenAPI 3 documentation for my Spring Boot 2 project. We did not used modals/DTOs on controllers.
Here is the sample controller:
#RestController
#RequestMapping(value = "/pet")
public class PetController {
#RequestMapping(value = "/save", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public ResponseEntity<Map<String, Object>> savePet(
#RequestBody Map<String, Object> petObj, HttpServletRequest request)
throws Exception {
String petResponse = petDAO.savePet(petObj, request, true);
return new ResponseEntity<Map<String, Object>>(petResponse, HttpStatus.OK);
}
}
Request body:
{
"name":"Test",
"category":"school"
}
My response:
{
"petId":"1",
"petName":"Test",
"petCategory":"school",
"petStaus":"active"
}
I am not able to find a way to add the OpenAPI doc for my custom Map object. I want to add key, description, type, example(s) for each property in my Map manually.
Can anyone suggest how to do this?
This is the default behaviour of the springdoc-openapi library in order to ignore other injectable parameters supported by Spring MVC.
https://docs.spring.io/spring/docs/5.1.x/spring-framework-reference/web.html#mvc-ann-arguments
If you want to change this behaviour, you can just exlcude it as follow:
SpringDocUtils.getConfig().removeRequestWrapperToIgnore(Map.class);
I am trying to add common request parameters to every request using RestTemplate.
For example if my url is http://something.com/countries/US then I want to add common request param ?id=12345. This common request parameter needs to be added on all request. I don't want to add this on each call and want something common.
this post has answer that was marked correct, but I am not sure how you can add request parameters on org.springframework.http.HttpRequest
Any other way I can achieve this ?
To add request parameters to the HttpRequest , you can first use UriComponentsBuilder to build an new URI based on the existing URI but adding the query parameters that you want to add.
Then use HttpRequestWrapper to wrap the existing request but only override its URI with the updated URI.
Code wise it looks like:
public class AddQueryParamterInterceptor implements ClientHttpRequestInterceptor {
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
URI uri = UriComponentsBuilder.fromHttpRequest(request)
.queryParam("id", 12345)
.build().toUri();
HttpRequest modifiedRequest = new HttpRequestWrapper(request) {
#Override
public URI getURI() {
return uri;
}
};
return execution.execute(modifiedRequest, body);
}
}
And set this interceptor to the RestTemplate:
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
interceptors.add(new AddQueryParamterInterceptor());
restTemplate.setInterceptors(interceptors);
Two things are required to add common request parameters to every request using RestTemplate.
Create a prototype bean RestTemplate
#Configuration
public class RestTemplateConfig {
#Bean
#Scope(
value = ConfigurableBeanFactory.SCOPE_PROTOTYPE,
proxyMode = ScopedProxyMode.TARGET_CLASS)
public RestTemplate restTemplate() {
RestTemplate localRestTemplate = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors = localRestTemplate.getInterceptors();
if (CollectionUtils.isEmpty(interceptors)) {
interceptors = new ArrayList<>();
}
interceptors.add(new AddQueryParamterInterceptor());
localRestTemplate.setInterceptors(interceptors);
return localRestTemplate;
}
}
Use the Interceptor code as suggested by #ken-chan to add the request parameters. A new instance of Resttemaple will be created and for each and every request.
You can achieve this by adding interceptor to rest template
I am trying to call a RESTfull web service resource, this resource is provided by a third party, the resource is exposed with OPTIONS http verb.
To integrate with the service, I should send a request with a specific body, which identities by a provider, but when I did that I got a bad request. After that I trace my code then I recognized that the body of the request is ignored by rest template based on the below code:
if ("POST".equals(httpMethod) || "PUT".equals(httpMethod) ||
"PATCH".equals(httpMethod) || "DELETE".equals(httpMethod)) {
connection.setDoOutput(true);
}
else {
connection.setDoOutput(false);
}
my question, is there a standard way to override this behavior or I should use another tool?
The code you've pasted is from
SimpleClientHttpRequestFactory.prepareConnection(HttpURLConnection connection, String httpMethod)
I know because I've debugged that code few hours ago.
I had to do a HTTP GET with body using restTemplate. So I've extend SimpleClientHttpRequestFactory, override prepareConnection and create a new RestTemplate using the new factory.
public class SimpleClientHttpRequestWithGetBodyFactory extends SimpleClientHttpRequestFactory {
#Override
protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
super.prepareConnection(connection, httpMethod);
if ("GET".equals(httpMethod)) {
connection.setDoOutput(true);
}
}
}
Create a new RestTemplate based on this factory
new RestTemplate(new SimpleClientHttpRequestWithGetBodyFactory());
A test to prove the solution is working using spring boot (#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT))
public class TestRestTemplateTests extends AbstractIntegrationTests {
#Test
public void testMethod() {
RestTemplate restTemplate = new RestTemplate(new SimpleClientHttpRequestWithBodyForGetFactory());
HttpEntity<String> requestEntity = new HttpEntity<>("expected body");
ResponseEntity<String> responseEntity = restTemplate.exchange("http://localhost:18181/test", HttpMethod.GET, requestEntity, String.class);
assertThat(responseEntity.getBody()).isEqualTo(requestEntity.getBody());
}
#Controller("/test")
static class TestController {
#RequestMapping
public #ResponseBody String testMethod(HttpServletRequest request) throws IOException {
return request.getReader().readLine();
}
}
}
My goal is to call web service, which is require authentification (when I opne it's wsdl in my browser, browser asks me login+password).
As a base, I use the sample from this tutorial.
And now I have to add authentification configurations.
Accoding to the documentation something like configuring WebServiceTemplate bean may help.
But with Spring Boot there are no applicationContext.xml or any other configuration xml's in a project.
So, how to configure WebServiceTemplate using Spring Boot, or what else can solve such task?
In Spring Boot you are able to configure your beans with the #Bean annotation. You can use configuration classes for different beans. In those classes you need the #Configuaration annotation.
This tutorial describes the "second part" of the Spring tutorial. The main things of provided tutorial is: (based on the Spring tutorial)
The problem
The SOAP webservice I consume requires basic http authentication, so I
need to add authentication header to the request.
Without authentication
First of all you need to have implemented a request without the
authentication like in the tutorial on the spring.io. Then I will
modify the http request with the authentication header.
Get the http request in custom WebServiceMessageSender
The raw http connection is accessible in the WeatherConfiguration
class. There in the weatherClient you can set the message sender in
the WebServiceTemplate. The message sender has access to the raw http
connection. So now it’s time to extend the
HttpUrlConnectionMessageSender and write custom implementation of it
that will add the authentication header to the request. My custom
sender is as follows:
public class WebServiceMessageSenderWithAuth extends HttpUrlConnectionMessageSender{
#Override
protected void prepareConnection(HttpURLConnection connection)
throws IOException {
BASE64Encoder enc = new sun.misc.BASE64Encoder();
String userpassword = "yourLogin:yourPassword";
String encodedAuthorization = enc.encode( userpassword.getBytes() );
connection.setRequestProperty("Authorization", "Basic " + encodedAuthorization);
super.prepareConnection(connection);
}
#Bean
public WeatherClient weatherClient(Jaxb2Marshaller marshaller){
WebServiceTemplate template = client.getWebServiceTemplate();
template.setMessageSender(new WebServiceMessageSenderWithAuth());
return client;
}
I faced the same issue and solved by following.
Basic idea was to create CredentialsProvider with basic username and password along with AuthScope.ANY:
#Bean
public WebServiceMessageSender showReqMessageSender(#Value("${ws.username}") String username,
#Value("${ws.passowrd}") String password) throws Exception {
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
return new HttpComponentsMessageSender(
HttpClientBuilder.create().setDefaultCredentialsProvider(credentialsProvider)
.addInterceptorFirst(new RemoveSoapHeadersInterceptor()).build());
}
Just for further info, this message sender bean is further used (set using class extedning WebServiceGatewaySupport)
void org.springframework.ws.client.core.support.WebServiceGatewaySupport.setMessageSender(WebServiceMessageSender messageSender)
Another walk-around is to add an interceptor and add the requestHeader within the handleRequest() method from which the HttpUrlConnection can be easily derived from the TransportContextHolder;
here is the code of the interceptor class:
public class SecurityInterceptor implements ClientInterceptor {
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
TransportContext context = TransportContextHolder.getTransportContext();
HttpUrlConnection connection = (HttpUrlConnection) context.getConnection();
try {
connection.addRequestHeader("Authorization","Basic VVNFUk5BTUU6cGFzc3dvcmQ=");
} catch (IOException e) {
log.error(e.getMessage());
}
return true;
}
//TODO:: other methods and constructor..
}
and of course add the interceptor to the WebTemplate:
WebServiceTemplate webServiceTemplate = new WebServiceTemplate(marshaller);
ClientInterceptor[] interceptors = new ClientInterceptor[]{new SecurityInterceptor()};
webServiceTemplate.setInterceptors(interceptors);
webServiceTemplate.marshalSendAndReceive(uriWebService, request)
I am using Spring #RESTController for my REST webservice. instead of returning the object of ModelAndView I am trying to return the object of ResponseEntity object in my rest method. for the Strgin type of response it is working ut when I am building ResponseEntity with a Jaxbobject it is giving me HTTP error 406
#RestController
#RequestMapping(value="/service")
public class MyController {
public #ResponseBody ResponseEntity<String> getDashBoardData() throws JAXBException {
// Some Operation
return new ResponseEntity<String>(myStringXML, responseHeaders, HttpStatus.OK);
}
}
Below is not working
#RestController
#RequestMapping(value="/service")
public class MyController {
public #ResponseBody ResponseEntity<MyJaxbClass> getDashBoardData() throws JAXBException {
// Some Operation
return new ResponseEntity<MyJaxbClass>(MyJaxbClassObject, HttpStatus.OK);
}
}
The #RestController annotation already implies the #ResponseBody annotation for all request handling methods, that is one of its purposes (it saves you from putting all those annotations there). So you can/should remove it.
Processing the return value of the method is done by a 'HandlerMethodReturnValueHandlerand the specific one which should handle this delegates to aHttpMessageConverter. It selects a specificHttpMessageConverterbased on the requested/supported response types for the current request and the support response types from theHandlerMethodReturnValueHandler`.
In general when using #EnableWebMvc or <mvc:annotation-driven /> everything should be setup automatically. The automatic setup does some detection on which libs are available (jaxb, json etc).
Based on the response code (406) you either have manually configured something wrong on the server side or the client doesn't support xml as a response type.