#RequestBody Annotation not working on Websphere Application Server - spring

I am trying to use #RequestBody annotation in one of my controllers method:
#Auditable(application = AuditApplication.DEP_TRXN, actionCategory = AuditActionCategory.READ_RESPONSE, actionDetail = "Viewed a tracking group.", event = AuditEventType.ACTION)
#RequestMapping(value = "groupView", method = RequestMethod.POST, produces = "application/json")
public #ResponseBody
String ajaxGroupView(#RequestBody String payload, final HttpServletRequest request) throws TrackingServiceException, JsonGenerationException, JsonMappingException,
IOException
{
String requestBody = java.net.URLDecoder.decode(payload, "UTF-8");
return requestBody;
}
When i run this code on Jetty server my 'payload' gets the post data, but when i run the same code on Websphere Application Server -8, payload is 'null'. I have mvc annotation driven turn on. How do i make this code work on Websphere Application Server?
Thanks!

Related

Not able to call SOAP API in WebServiceGatewaySupport by Spring WebServiceTemplate - Need help to fix this issue

I am trying to call SOAP API in Java Spring Boot using WebServiceGatewaySupport by Spring WebServiceTemplate
Config java class
public WebServiceTemplate createWebServiceTemplate(Jaxb2Marshaller marshaller, ClientInterceptor clientInterceptor) {
WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
//SOAP URL
webServiceTemplate.setDefaultUri("http://host/Services.asmx");
//Auth ---It seems issue is here only????? need to check
webServiceTemplate.setMessageSender(new Authentication());
webServiceTemplate.setMarshaller(marshaller);
webServiceTemplate.setUnmarshaller(marshaller);
webServiceTemplate.afterPropertiesSet();
webServiceTemplate.setCheckConnectionForFault(true);
webServiceTemplate.setInterceptors((ClientInterceptor[]) Arrays.asList(createLoggingInterceptor()).toArray());
return webServiceTemplate;
}
SOAP Client Call
public class TicketClient extends WebServiceGatewaySupport {
public String getTicket(Ticket req) {
System.out.println("test inside webservice support1");
response = (AcquireTicketResponse) getWebServiceTemplate().marshalSendAndReceive(req);
Authentication Class
public class Authentication extends HttpUrlConnectionMessageSender {
#Override protected void prepareConnection(HttpURLConnection connection) {
String userpassword = username+":"+password+":"+domain;
String encoded =
Base64.getEncoder().withoutPadding().encodeToString(userpassword.getBytes(StandardCharsets.UTF_8));
connection.setRequestProperty("Authorization", "Basic "+encoded); connection.setRequestProperty("Content-Type", "application/xml"); super.prepareConnection(connection);
}
Not using Authetication class and add the above into
ClientInterceptor
public class SoapLoggingInterceptor implements ClientInterceptor {
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
String username="test";
String password="test";
String domain = "#test";
String userpassword = username+":"+password+domain;
String encoded = Base64.getEncoder().withoutPadding().encodeToString(userpassword.getBytes(StandardCharsets.UTF_8));
messageContext.setProperty("Authorization", "Basic "+encoded);
messageContext.setProperty("Content-type", "XML");
Case -1 --->When I passed (user, pwd, domain and content-type) through messagesender, content type is taking but throwed "BAD REQUEST ERROR 400"....When i comment contenttype property, then it throwed "INTERNAL SERVER ERROR 500".
Case-2...when I passed (user, pwd, domain and content-type) through ClientInterceptor , always it throwed "INTERNAL SERVER ERROR 500"......It seems Authentication properties for the service are not going to API call.............................Please suggest some options
Both the cases, Authentication is not passing to service, if i comment,Authentication code (userid/pwd/domain) in both cases also...no efforts in output
After setting the user ID/pwd
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
String username="test";
String password="test";
String domain = "#test";
String userpassword = username+":"+password+domain;
byte[] userpassword = (username+":"+password).getBytes(StandardCharsets.UTF_8);
String encoded = Base64.getEncoder().encodeToString(userpassword);
ByteArrayTransportOutputStream os = new
ByteArrayTransportOutputStream();
try {
TransportContext context = TransportContextHolder.getTransportContext();
WebServiceConnection conn = context.getConnection();
((HeadersAwareSenderWebServiceConnection) conn).addRequestHeader("Authorization", "Basic " + encoded);
} catch (IOException e) {
throw new WebServiceIOException(e.getMessage(), e);
}
First of all don't set the content type Spring WebServices will do that for you, messing around with that will only make things worse.
You should get the WebServiceConnection and cast that to a HeadersAwareSenderWebServiceConnection to add a header.
public class BasicAuthenticationInterceptor implements ClientInterceptor {
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
String username="test#test";
String password="test";
byte[] userpassword = (username+":"+password).getBytes(UTF_8);
String encoded = Base64.getEncoder().encodeToString(userpassword);
WebServiceConnection conn = TransportContext.getConnection();
((HeadersAwareSenderWebServiceConnection) conn).addHeader("Authorization", "Basic " + encoded);
}
}
You also need to configure it. Assuming it is a bean don't call afterPropertiesSet (and ofcourse you are now using the ClientInterceptor remove the new Authentication() for your customized message sender.
The List<ClientInterceptor> will automatically create a list with all the interceptors so you can easily inject them.
#Bean
public WebServiceTemplate createWebServiceTemplate(Jaxb2Marshaller marshaller, List<ClientInterceptor> clientInterceptors) {
WebServiceTemplate webServiceTemplate = new WebServiceTemplate(marshaller);
//SOAP URL
webServiceTemplate.setDefaultUri("http://host/Services.asmx");
webServiceTemplate.setCheckConnectionForFault(true);
webServiceTemplate.setInterceptors(clientInterceptors);
return webServiceTemplate;
}
If this doesn't work there is something else you are doing wrong and you will need to get in touch with the server developers and get more information on the error.
Update:
Apparently you also need to provide a SOAP Action in your request, which you currently don't. For this you can specify the SoapActionCallback in the marshalSendAndReceive method. Which action to specify you can find in the WSDL you are using.
SoapActionCallback soapAction = new SoapActionCallback("SoapActionToUse");
response = (AcquireTicketResponse) getWebServiceTemplate().marshalSendAndReceive(req, soapAction);

Spring MVC - Stop redirect in Controller function

I have a Spring MVC Controller and a PUT mapping that consumes JSON. I receive the JSON and everything just fine, the problem is whenever I fire off the JSON the mapper wants to redirect to the URL, giving me error 500 because the server can't find any template for the URL. How can I stop Spring MVC from trying to redirect to the URL and just receive the JSON?
My relevant Controller code :
#RequestMapping(value = "admin/users/VMs", method = RequestMethod.PUT, consumes = "application/json")
public void removeVM(#RequestBody ManageVMRequest packet, Authentication authentication) {
System.out.println(packet.getVm());
System.out.println(packet.getUser_id());
}
You can try to return ResponseEntity<Void>
#RequestMapping(value = "admin/users/VMs", method = RequestMethod.PUT, consumes = "application/json")
public #ResponseBody ResponseEntity<Void> removeVM(#RequestBody ManageVMRequest packet, Authentication authentication) {
System.out.println(packet.getVm());
System.out.println(packet.getUser_id());
return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
}

Camel rest API to provide dynamic download

How can we provide document download using camel API, I need to provide an api using camel rest to response the file as download and I have the logic to create the pdf using apache fop, but i need to get some information how to respond the file as rest response using camel rest.
#RestController
public class MyController {
#Autowired
ICityService cityService;
#RequestMapping(
value = "/pdfreport",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_PDF_VALUE
)
public ResponseEntity<InputStreamResource> citiesReport() throws IOException {
List<City> cities = (List<City>) cityService.findAll();
ByteArrayInputStream bis = GeneratePdfReport.citiesReport(cities);
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "inline;
filename = citiesreport.pdf");
return ResponseEntity
.ok()
.headers(headers)
.contentType(MediaType.APPLICATION_PDF)
.body(new InputStreamResource(bis));
}
}

Spring boot large file upload and download support

I have a spring boot web application which will handle large file (max size of 5g) upload and then save it to s3. The request and response may last for a long time.
So what is the best practice to handle the upload and download like this? How to make a good performance to prevent my server down when download or upload large files?
you can use multipart/form-data
#RequestMapping(value = "/agency/create", method = RequestMethod.POST, consumes = "multipart/form-data")
public ResponseEntity<List<String>> createAgency(
#RequestParam(value = "username", required = true) String username,
#RequestParam(value = "pic1", required = true)MultipartFile pic1File,
MultipartHttpServletRequest request, ModelAndView modelAndView) {
List<String> requestKeys=new ArrayList<String>();
List<String> originalFileName=new ArrayList<String>();
request.getFileNames().forEachRemaining(requestKeys::add);
for(String multipartFile:requestKeys) {
originalFileName.add(request.getFile(multipartFile).getOriginalFilename());
}
storageService.store(pic1File);
return new ResponseEntity<List<String>>(originalFileName, HttpStatus.CREATED);
}
Posting in case someone finds this useful in the future. This works with a REST controller as of Spring Boot 2.4.2.
Class annotations:
#RestController
#RequestMapping("/api")
Method declaration:
#RequestMapping(path = "/file-upload/{depot}/{fileName}", method = {RequestMethod.POST, RequestMethod.PUT})
public ResponseEntity<String> fileUpload(
#PathVariable(name = "depot") String depot,
#PathVariable(name = "fileName") String fileName,
InputStream inputStream,
HttpServletRequest request,
HttpServletResponse response)
The above is the Spring Boot configuration for a REST Controller that worked for me for large file upload. The key was adding InputStream inputStream directly.

MocMVC giving HttpMessageNotReadableException

I'm still learning my way around testing and I'm trying to get a MockMvc test to work for me. It's a simple REST controller that at this point is only doing some authentication using information from json in the post. I've actually implemented the code, so I know it's working because I get back both the correct response with the correct input and the error messages I've put together, both in a json format. My problem is that the test keeps failing with a HttpMessageNotReadableException, even though the actual code works, so I'm assuming I don't have my test set up right. Any help you guys can give would be great.
Here's my controller
#Controller
public class RequestPaymentController {
protected final Log logger = LogFactory.getLog(getClass());
private PaymentService paymentService;
private LoginService loginService;
#Autowired
public void setPaymentService(PaymentService paymentService){
this.paymentService = paymentService;
}
#Autowired
public void setLoginService(LoginService loginService){
this.loginService = loginService;
}
#RequestMapping(value = "/requestpayment", method = RequestMethod.POST, headers="Accept=application/json")
#ResponseBody
public ResponseEntity<PaymentResult> handleRequestPayment(#RequestBody PaymentRequest paymentRequest, HttpServletRequest request, HttpServletResponse response, BindingResult result) throws Exception{
ResponseEntity<PaymentResult> responseEntity = null;
new LoginValidator().validate(paymentRequest, result);
boolean valid = loginService.isLoginValid(paymentRequest, result);
if (valid){
responseEntity = setValidResponse(paymentRequest);
}else {
throw new TumsException("exception message");
}
return responseEntity;
}
private ResponseEntity<PaymentResult> setValidResponse(PaymentRequest paymentRequest){
PaymentResult paymentResult = paymentService.getResults(paymentRequest);
return new ResponseEntity<PaymentResult>(paymentResult, HttpStatus.OK);
}
}
And here's my test code:
public class RequestPaymentControllerTest {
PaymentService mockPaymentService;
RequestPaymentController requestPaymentController;
HttpServletRequest mockHttpServletRequest;
HttpServletResponse mockHttpServletResponse;
PaymentRequest mockPaymentRequest;
BindingResult mockBindingResult;
LoginService mockLoginService;
PaymentResult mockPaymentResult;
MockMvc mockMvc;
#Before
public void setUp() throws Exception {
mockPaymentService = createMock(PaymentService.class);
mockHttpServletRequest = createMock(HttpServletRequest.class);
mockHttpServletResponse = createMock(HttpServletResponse.class);
mockPaymentRequest = createMock(PaymentRequest.class);
requestPaymentController = new RequestPaymentController();
mockBindingResult = createMock(BindingResult.class);
mockLoginService = createMock(LoginService.class);
requestPaymentController.setPaymentService(mockPaymentService);
mockPaymentResult = createMock(PaymentResult.class);
mockMvc = MockMvcBuilders.standaloneSetup(new RequestPaymentController()).build();
}
#After
public void tearDown() throws Exception {
mockPaymentService = null;
mockHttpServletRequest = null;
mockHttpServletResponse = null;
mockPaymentRequest = null;
requestPaymentController = null;
mockBindingResult = null;
mockLoginService = null;
mockPaymentResult = null;
mockMvc = null;
}
#Test
public void testHandleRequestPayment() throws Exception{
initializeStateForHandleRequestPayment();
createExpectationsForHandleRequestPayment();
replayAndVerifyExpectationsForHandleRequestPayment();
}
private void initializeStateForHandleRequestPayment(){
}
private void createExpectationsForHandleRequestPayment(){
mockPaymentRequest.getServiceUsername();
expectLastCall().andReturn("testuser");
mockPaymentRequest.getServicePassword();
expectLastCall().andReturn("password1!");
mockLoginService.isLoginValid(mockPaymentRequest,mockBindingResult);
expectLastCall().andReturn(true);
mockPaymentService.getResults(mockPaymentRequest);
expectLastCall().andReturn(mockPaymentResult);
}
private void replayAndVerifyExpectationsForHandleRequestPayment() throws Exception{
replay(mockPaymentService, mockBindingResult, mockHttpServletRequest, mockHttpServletResponse, mockPaymentRequest, mockLoginService);
requestPaymentController.setLoginService(mockLoginService);
requestPaymentController.handleRequestPayment(mockPaymentRequest, mockHttpServletRequest, mockHttpServletResponse, mockBindingResult);
mockMvc.perform(post("/requestpayment")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isBadRequest());
verify(mockPaymentService, mockBindingResult, mockHttpServletRequest, mockHttpServletResponse, mockPaymentRequest, mockLoginService);
}
}
The results of the andDo(print()) are:
MockHttpServletRequest:
HTTP Method = POST
Request URI = /requestpayment
Parameters = {}
Headers = {Content-Type=[application/json], Accept=[application/json]}
Handler:
Type = portal.echecks.controller.RequestPaymentController
Method = public org.springframework.http.ResponseEntity<portal.echecks.model.PaymentResult> portal.echecks.controller.RequestPaymentController.handleRequestPayment(portal.echecks.model.PaymentRequest,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse,org.springframework.validation.BindingResult) throws java.lang.Exception
Resolved Exception:
Type = org.springframework.http.converter.HttpMessageNotReadableException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
MockHttpServletResponse:
Status = 400
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
Process finished with exit code 0
As you can see, the test passes when I'm expecting a bad request status, but I've put in logging and I know that the ResponseBody I'm sending back has a 200 status. Like I said, this is my first time with MockMvc, so I assume I've not set something up right. Any suggestions?
An HttpMessageNotReadableException is
Thrown by HttpMessageConverter implementations when the read method
fails.
You also get a 400 Bad Request in your response. This should all tell you that you are not sending what your server is expecting. What is your server expecting?
#RequestMapping(value = "/requestpayment", method = RequestMethod.POST, headers="Accept=application/json")
#ResponseBody
public ResponseEntity<PaymentResult> handleRequestPayment(#RequestBody PaymentRequest paymentRequest, HttpServletRequest request, HttpServletResponse response, BindingResult result) throws Exception{
The main thing here is the #RequestBody annotated parameter. So you are telling your server to try and deserialize a PaymentRequest instance from the body of the HTTP POST request.
So let's see the request you are making
mockMvc.perform(post("/requestpayment")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isBadRequest());
I don't see you providing a body to the request. There should be a content(String) call somewhere in there to set the content of the POST request. This content should be a JSON serialization of a PaymentRequest.
Note that because you are using the StandaloneMockMvcBuilder, you might need to set the HttpMessageConverter instances yourself, ie. a MappingJackson2HttpMessageConverter to serialize and deserialize JSON.
Note that the BindingResult parameter should come immediately after the parameter to which it's related. Like so
#RequestMapping(value = "/requestpayment", method = RequestMethod.POST, headers="Accept=application/json")
#ResponseBody
public ResponseEntity<PaymentResult> handleRequestPayment(#Valid #RequestBody PaymentRequest paymentRequest, BindingResult result, HttpServletRequest request, HttpServletResponse response) throws Exception{
Don't forget the #Valid.
Note that this
requestPaymentController.setLoginService(mockLoginService);
requestPaymentController.handleRequestPayment(mockPaymentRequest, mockHttpServletRequest, mockHttpServletResponse, mockBindingResult);
is completely unrelated to the MockMvc test you are doing.
In my case, as sprint mvc w/ jackson (jackson-mapper-asl, v-1.9.10) deserialization requires JSON parser. And jackson requires a default constructor for http request message deserialization, if there's no default constructor, jackson will have a problem w/ reflection and throws HttpMessageNotReadableException exception.
This is to say, all the classes/sub-classes which used as Request body, (in this case) requires a default constructor. This costed me a few moments after I tried adding custom converter and other suggestions I got in stackoverflow in vain.
Or you can add Custom Deserializer or Mixin annotation to avoid adding default constructor hierachically everywhere. as described here: http://blogs.jbisht.com/blogs/2016/09/12/Deserialize-json-with-Java-parameterized-constructor. Check this if you're interested.
Seems duplicated here > Spring HttpMessageNotReadableException.
Make sure of the following:
return object implements Serializable
#ResponseBody annotation used on the controller method
On your unit test
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {....})
#WebMvcTest
#AutoConfigureMockMvc
Probably too late to answer but just in case someone is still looking at this page.
As #Sotirios Delimanolis mentions, the problem is due to a bad request - a '#RequestBody' is specified in the parameter but never supplied in the request body. So, if you add that to request using 'content(someRequestString)' as below, it should work.
PaymentRequest paymentRequest = new PaymentRequest(...);
String requestBody = new ObjectMapper().valueToTree(paymentRequest).toString();
mockMvc.perform(post("/requestpayment")
.content(requestBody)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value("SUCCESS"))
.andExpect(jsonPath("$.paymentAmount", is(20)));
jsonPath may be used to verify the attributes on the response. In the above example, say PaymentResponse has attributes status and paymentAmount in the json response. These parts can be verified easily.
You may run into errors like -
NoClassDefFoundError: com/jayway/jsonpath/Predicate
while using jsonPath. So, make sure it is added to classpath explicitly as it is an optional dependency in spring-test and will not be available transitively. If using maven, do this:
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.4.0</version>
<scope>test</scope>
</dependency>

Resources