Sending a MultipartFile as a Request Param to REST server using Angular2 - spring

I need to upload a photo to the server which has been written using Spring Boot. For the front end which sends the request I use Angular2.
This is my API which accepts the HTTP request. It works fine. (I tested it using Postman)
#RequestMapping(method = RequestMethod.POST, value = "/tender/save-new/save-photo")
public ResponseEntity<?> uploadPhoto(#RequestParam("file") MultipartFile file){
if (file.isEmpty()) {
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setMessage("DEBUG: Attached file is empty");
return new ResponseEntity<ErrorResponse>(errorResponse, HttpStatus.NOT_FOUND);
}
String returnPath = null;
try {
// upload stuff
} catch (IOException e) {
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setMessage(e.getMessage());
return new ResponseEntity<ErrorResponse> (errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<String>(returnPath, HttpStatus.OK);
}
I am not sure how should I write the code in Angular2 to call the server.
Following is what I have come up with.
savePhoto(photoToSave: File) {
let formData: FormData = new FormData();
formData.append('file', photoToSave);
let savedPath = this._http
.post(this._endpointUrl + "tender/save-new/save-photo", formData)
.map(
res => {
return res.json();
}
)
.catch(handleError);
return savedPath;
}
As you can see, I append the 'file' parameter to the form data before sending it. Server method accepts the RequestParam as 'file'.
But, in the server log, I get following error.
org.springframework.web.multipart.MultipartException: Current request
is not a multipart request at
org.springframework.web.method.annotation.RequestParamMethodArgumentResolver.handleMissingValue(RequestParamMethodArgumentResolver.java:190)
Note that I haven't declared a CommonsMultipartResolver bean since SprinBoot implicitly handles it. (I have heard this. Please correct if I am wrong.)
I think that the error comes up because of a missing value in the request. What Spring is saying by handleMissingValue? What am I missing?

you need to specify that your controller is expecting multipart
#RequestMapping(method = RequestMethod.POST, value = "/tender/save-new/save-photo", consumes = {"multipart/form-data"})
public ResponseEntity<?> uploadPhoto(#RequestParam("file") MultipartFile file){
Also to solve the CORS problem you need to add the methods you are planning to use in your cors mapping, try something like this
#Configuration
public class WebMvcConfiguration {
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD");
}
};
}
}

i had the same problem, this is the solution i am using right now:
for the back end using spring boot:
#RequestMapping(value="/save", method = RequestMethod.POST)
public ResponseEntity<?> saveTemp(#RequestParam("file") MultipartFile file) throws Exception{
String nomFichier=file.getOriginalFilename();
try {
byte[] bytes = file.getBytes();
File fi=new File(tempRepo+nomFichier);
fi.getParentFile().mkdirs();
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream((new FileOutputStream(fi)));
bufferedOutputStream.write(bytes);
bufferedOutputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(HttpStatus.OK);
}
for the front end using Angular2:
public save(file:any){
const formData = new FormData();
formData.append("file", file);
return this._http
.post('http://localhost:8081/save', formData)
.catch(this._errorhandler);
}

Related

no suitable HttpMessageConverter found for response type [class java.lang.Object] and content type [multipart/form-data;boundary=XXX;charset=UTF-8]

I am doing multipart formdata post call from UI to upload a file.
From UI, the call goes to component A controller which you can consider it as a common component or bus which just read the stream pass the stream to other component B controller through rest call which uploads the file and sends back metadata about uploaded file in json format.
Issue: The call successfully goes till component B controller, file got uploaded but when the response of component B reaches component A, I am getting below error
"message" : "Could not extract response: no suitable HttpMessageConverter found for response type [class java.lang.Object] and content type [multipart/form-data;boundary=HWhdmg6KNJw_kaP4wWnLyoAb9htc8StF4;charset=UTF-8]"
Can someone help pls?
Components A code
#PostMapping(value = "/v3/documents",consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity uploadMultipartDocument(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse) {
ResponseEntity<?> responseEntity = null;
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
headers.set(HznAppsHeaderConstant.DOCUMENT_TYPE, HznAppsUtil.getHeaderFromCustomHeaderParameterProvider(customHeaderParameterProvider, HznAppsHeaderConstant.DOCUMENT_TYPE));
InputStream stream =null;
MultiValueMap<String, Object> params= new LinkedMultiValueMap<String, Object>();
try{
ServletFileUpload upload = new ServletFileUpload();
FileItemIterator iterStream = upload.getItemIterator(req);
while (iterStream.hasNext()){
FileItemStream item = iterStream.next();
stream = item.openStream();
if (!item.isFormField() && item.getFieldName().equalsIgnoreCase("file")) {
InputStreamResource inputStream = new InputStreamResource(stream) {
#Override
public String getFilename() {
return item.getName();
}
#Override
public long contentLength() {
return -1;
}
};
params.add("file", inputStream);
break;
}
}
final HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<MultiValueMap<String, Object>>(params, headers);
restTemplate.getInterceptors().clear();
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);
restTemplate.setRequestFactory(requestFactory);
responseEntity = restTemplate.exchange("Component B URL", HttpMethod.POST, httpEntity, Object.class);
} catch (Exception e) {
e.printStackTrace();
}
return responseEntity;
}
Component B code
#RequestMapping(value = "/api/v3/documents", produces = {"application/json"}, consumes = {"multipart/form-data"}, method = RequestMethod.POST)
public ResponseEntity uploadMultipartDocument(HttpServletRequest request) {
ResponseDocument responseDocument = null;
try {
responseDocument = documentsService.uploadMultiPartDocument(request,documentType); // Doing Soap call to upload document and its works fine in this method
} catch (Exception e) {
e.printStackTrace();
}
return new ResponseEntity<>(responseDocument, HttpStatus.OK);
}
Thanks

Spring Resttemplate exception handling

Below is the code snippet; basically, I am trying to propagate the exception when the error code is anything other than 200.
ResponseEntity<Object> response = restTemplate.exchange(url.toString().replace("{version}", version),
HttpMethod.POST, entity, Object.class);
if(response.getStatusCode().value()!= 200){
logger.debug("Encountered Error while Calling API");
throw new ApplicationException();
}
However in the case of a 500 response from the server I am getting the exception
org.springframework.web.client.HttpServerErrorException: 500 Internal Server Error
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:94) ~[spring-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
Do I really need to wrap the rest template exchange method in try? What would then be the purpose of codes?
You want to create a class that implements ResponseErrorHandler and then use an instance of it to set the error handling of your rest template:
public class MyErrorHandler implements ResponseErrorHandler {
#Override
public void handleError(ClientHttpResponse response) throws IOException {
// your error handling here
}
#Override
public boolean hasError(ClientHttpResponse response) throws IOException {
...
}
}
[...]
public static void main(String args[]) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new MyErrorHandler());
}
Also, Spring has the class DefaultResponseErrorHandler, which you can extend instead of implementing the interface, in case you only want to override the handleError method.
public class MyErrorHandler extends DefaultResponseErrorHandler {
#Override
public void handleError(ClientHttpResponse response) throws IOException {
// your error handling here
}
}
Take a look at its source code to have an idea of how Spring handles HTTP errors.
Spring cleverly treats http error codes as exceptions, and assumes that your exception handling code has the context to handle the error. To get exchange to function as you would expect it, do this:
try {
return restTemplate.exchange(url, httpMethod, httpEntity, String.class);
} catch(HttpStatusCodeException e) {
return ResponseEntity.status(e.getRawStatusCode()).headers(e.getResponseHeaders())
.body(e.getResponseBodyAsString());
}
This will return all the expected results from the response.
You should catch a HttpStatusCodeException exception:
try {
restTemplate.exchange(...);
} catch (HttpStatusCodeException exception) {
int statusCode = exception.getStatusCode().value();
...
}
Another solution is the one described here at the end of this post by "enlian":
http://springinpractice.com/2013/10/07/handling-json-error-object-responses-with-springs-resttemplate
try{
restTemplate.exchange(...)
} catch(HttpStatusCodeException e){
String errorpayload = e.getResponseBodyAsString();
//do whatever you want
} catch(RestClientException e){
//no response payload, tell the user sth else
}
Spring abstracts you from the very very very large list of http status code. That is the idea of the exceptions. Take a look into org.springframework.web.client.RestClientException hierarchy:
You have a bunch of classes to map the most common situations when dealing with http responses. The http codes list is really large, you won't want write code to handle each situation. But for example, take a look into the HttpClientErrorException sub-hierarchy. You have a single exception to map any 4xx kind of error. If you need to go deep, then you can. But with just catching HttpClientErrorException, you can handle any situation where bad data was provided to the service.
The DefaultResponseErrorHandler is really simple and solid. If the response status code is not from the family of 2xx, it just returns true for the hasError method.
I have handled this as below:
try {
response = restTemplate.postForEntity(requestUrl, new HttpEntity<>(requestBody, headers), String.class);
} catch (HttpStatusCodeException ex) {
response = new ResponseEntity<String>(ex.getResponseBodyAsString(), ex.getResponseHeaders(), ex.getStatusCode());
}
A very simple solution can be:
try {
requestEntity = RequestEntity
.get(new URI("user String"));
return restTemplate.exchange(requestEntity, String.class);
} catch (RestClientResponseException e) {
return ResponseEntity.status(e.getRawStatusCode()).body(e.getResponseBodyAsString());
}
If you use pooling (http client factory) or load balancing (eureka) mechanism with your RestTemplate, you will not have the luxury of creating a new RestTemplate per class. If you are calling more than one service you cannot use setErrorHandler because if would be globally used for all your requests.
In this case, catching the HttpStatusCodeException seems to be the better option.
The only other option you have is to define multiple RestTemplate instances using the #Qualifier annotation.
Also - but this is my own taste - I like my error handling snuggled tightly to my calls.
The code of exchange is below:
public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables) throws RestClientException
Exception RestClientException has HttpClientErrorException and HttpStatusCodeException exception.
So in RestTemplete there may occure HttpClientErrorException and HttpStatusCodeException exception.
In exception object you can get exact error message using this way: exception.getResponseBodyAsString()
Here is the example code:
public Object callToRestService(HttpMethod httpMethod, String url, Object requestObject, Class<?> responseObject) {
printLog( "Url : " + url);
printLog( "callToRestService Request : " + new GsonBuilder().setPrettyPrinting().create().toJson(requestObject));
try {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Object> entity = new HttpEntity<>(requestObject, requestHeaders);
long start = System.currentTimeMillis();
ResponseEntity<?> responseEntity = restTemplate.exchange(url, httpMethod, entity, responseObject);
printLog( "callToRestService Status : " + responseEntity.getStatusCodeValue());
printLog( "callToRestService Body : " + new GsonBuilder().setPrettyPrinting().create().toJson(responseEntity.getBody()));
long elapsedTime = System.currentTimeMillis() - start;
printLog( "callToRestService Execution time: " + elapsedTime + " Milliseconds)");
if (responseEntity.getStatusCodeValue() == 200 && responseEntity.getBody() != null) {
return responseEntity.getBody();
}
} catch (HttpClientErrorException exception) {
printLog( "callToRestService Error :" + exception.getResponseBodyAsString());
//Handle exception here
}catch (HttpStatusCodeException exception) {
printLog( "callToRestService Error :" + exception.getResponseBodyAsString());
//Handle exception here
}
return null;
}
Here is the code description:
In this method you have to pass request and response class. This method will automatically parse response as requested object.
First of All you have to add message converter.
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
Then you have to add requestHeader.
Here is the code:
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Object> entity = new HttpEntity<>(requestObject, requestHeaders);
Finally, you have to call exchange method:
ResponseEntity<?> responseEntity = restTemplate.exchange(url, httpMethod, entity, responseObject);
For prety printing i used Gson library.
here is the gradle : compile 'com.google.code.gson:gson:2.4'
You can just call the bellow code to get response:
ResponseObject response=new RestExample().callToRestService(HttpMethod.POST,"URL_HERE",new RequestObject(),ResponseObject.class);
Here is the full working code:
import com.google.gson.GsonBuilder;
import org.springframework.http.*;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestTemplate;
public class RestExample {
public RestExample() {
}
public Object callToRestService(HttpMethod httpMethod, String url, Object requestObject, Class<?> responseObject) {
printLog( "Url : " + url);
printLog( "callToRestService Request : " + new GsonBuilder().setPrettyPrinting().create().toJson(requestObject));
try {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Object> entity = new HttpEntity<>(requestObject, requestHeaders);
long start = System.currentTimeMillis();
ResponseEntity<?> responseEntity = restTemplate.exchange(url, httpMethod, entity, responseObject);
printLog( "callToRestService Status : " + responseEntity.getStatusCodeValue());
printLog( "callToRestService Body : " + new GsonBuilder().setPrettyPrinting().create().toJson(responseEntity.getBody()));
long elapsedTime = System.currentTimeMillis() - start;
printLog( "callToRestService Execution time: " + elapsedTime + " Milliseconds)");
if (responseEntity.getStatusCodeValue() == 200 && responseEntity.getBody() != null) {
return responseEntity.getBody();
}
} catch (HttpClientErrorException exception) {
printLog( "callToRestService Error :" + exception.getResponseBodyAsString());
//Handle exception here
}catch (HttpStatusCodeException exception) {
printLog( "callToRestService Error :" + exception.getResponseBodyAsString());
//Handle exception here
}
return null;
}
private void printLog(String message){
System.out.println(message);
}
}
Thanks :)
To extedend #carcaret answer a bit....
Consider your response errors are returned by json message. For example the API may return 204 as status code error and a json message as error list. In this case you need to define which messages should spring consider as error and how to consume them.
As a sample your API may return some thing like this, if error happens:
{ "errorCode":"TSC100" , "errorMessage":"The foo bar error happend" , "requestTime" : "202112827733" .... }
To consume above json and throw a custom exception, you can do as below:
First define a class for mapping error ro object
//just to map the json to object
public class ServiceErrorResponse implements Serializable {
//setter and getters
private Object errorMessage;
private String errorCode;
private String requestTime;
}
Now define the error handler:
public class ServiceResponseErrorHandler implements ResponseErrorHandler {
private List<HttpMessageConverter<?>> messageConverters;
#Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return (response.getStatusCode().is4xxClientError() ||
response.getStatusCode().is5xxServerError());
}
#Override
public void handleError(ClientHttpResponse response) throws IOException {
#SuppressWarnings({ "unchecked", "rawtypes" })
HttpMessageConverterExtractor<ServiceErrorResponse> errorMessageExtractor =
new HttpMessageConverterExtractor(ServiceErrorResponse.class, messageConverters);
ServiceErrorResponse errorObject = errorMessageExtractor.extractData(response);
throw new ResponseEntityErrorException(
ResponseEntity.status(response.getRawStatusCode())
.headers(response.getHeaders())
.body(errorObject)
);
}
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
this.messageConverters = messageConverters;
}
}
The custom Exception will be:
public class ResponseEntityErrorException extends RuntimeException {
private ResponseEntity<ServiceErrorResponse> serviceErrorResponseResponse;
public ResponseEntityErrorException(ResponseEntity<ServiceErrorResponse> serviceErrorResponseResponse) {
this.serviceErrorResponseResponse = serviceErrorResponseResponse;
}
public ResponseEntity<ServiceErrorResponse> getServiceErrorResponseResponse() {
return serviceErrorResponseResponse;
}
}
To use it:
RestTemplateResponseErrorHandler errorHandler = new
RestTemplateResponseErrorHandler();
//pass the messageConverters to errror handler and let it convert json to object
errorHandler.setMessageConverters(restTemplate.getMessageConverters());
restTemplate.setErrorHandler(errorHandler);
This is how to handle exceptions in Rest Template
try {
return restTemplate.exchange("URL", HttpMethod.POST, entity, String.class);
}
catch (HttpStatusCodeException e)
{
return ResponseEntity.status(e.getRawStatusCode()).headers(e.getResponseHeaders())
.body(e.getResponseBodyAsString());
}
Here is my POST method with HTTPS which returns a response body for any type of bad responses.
public String postHTTPSRequest(String url,String requestJson)
{
//SSL Context
CloseableHttpClient httpClient = HttpClients.custom().setSSLHostnameVerifier(new NoopHostnameVerifier()).build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
//Initiate REST Template
RestTemplate restTemplate = new RestTemplate(requestFactory);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
//Send the Request and get the response.
HttpEntity<String> entity = new HttpEntity<String>(requestJson,headers);
ResponseEntity<String> response;
String stringResponse = "";
try {
response = restTemplate.postForEntity(url, entity, String.class);
stringResponse = response.getBody();
}
catch (HttpClientErrorException e)
{
stringResponse = e.getResponseBodyAsString();
}
return stringResponse;
}
I fixed it by overriding the hasError method from DefaultResponseErrorHandler class:
public class BadRequestSafeRestTemplateErrorHandler extends DefaultResponseErrorHandler
{
#Override
protected boolean hasError(HttpStatus statusCode)
{
if(statusCode == HttpStatus.BAD_REQUEST)
{
return false;
}
return statusCode.isError();
}
}
And you need to set this handler for restemplate bean:
#Bean
protected RestTemplate restTemplate(RestTemplateBuilder builder)
{
return builder.errorHandler(new BadRequestSafeRestTemplateErrorHandler()).build();
}
Read about global exception handling in global exception handler add the below method. this will work.
#ExceptionHandler( {HttpClientErrorException.class, HttpStatusCodeException.class, HttpServerErrorException.class})
#ResponseBody
public ResponseEntity<Object> httpClientErrorException(HttpStatusCodeException e) throws IOException {
BodyBuilder bodyBuilder = ResponseEntity.status(e.getRawStatusCode()).header("X-Backend-Status", String.valueOf(e.getRawStatusCode()));
if (e.getResponseHeaders().getContentType() != null) {
bodyBuilder.contentType(e.getResponseHeaders().getContentType());
}
return bodyBuilder.body(e.getResponseBodyAsString());
}
There is also an option to use TestRestTemplate. It is very useful for integration and E2E tests, when you need to validate all status codes manually (for example in negative test-cases).
TestRestTemplate is fault-tolerant. This means that 4xx and 5xx do not result in an exception being thrown and can instead be detected via the response entity and its status code.
Try using #ControllerAdvice. This allows you to handle the exception only once and have all 'custom' handled exceptions in one place.
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/ControllerAdvice.html
example
#ControllerAdvice
public class MyExceptionHandler extends ResponseEntityExceptionHandler{
#ExceptionHandler(MyException.class)
protected ResponseEntity<Object> handleMyException(){
MyException exception,
WebRequest webRequest) {
return handleExceptionInternal(
exception,
exception.getMessage(),
exception.getResponseHeaders(),
exception.getStatusCode(),
webRequest);
}

How to get response object from POST request?

It is a Spring application, I'm trying to make a POST request and get back a response.
At server side I have this
#CrossOrigin
#RequestMapping(value = "/test", method = RequestMethod.POST)
public
#ResponseBody
Test testmethod(#RequestBody Test test) {
test.setValue("test");
return test;
}
At client side I have the post method which should return the Test object. I use JSON for ecoding.
public Object post(String url1, Test test) throws IOException, ClassNotFoundException {
ObjectMapper mapper = new ObjectMapper();
String jsonInString = mapper.writeValueAsString(login);
try {
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpPost postRequest = new HttpPost(url1);
StringEntity input = new StringEntity(jsonInString);
input.setContentType("application/json");
postRequest.setEntity(input);
HttpResponse response = httpClient.execute(postRequest);
//read the object from the response, how to do that?
//responseObject = ?????
httpClient.getConnectionManager().shutdown();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return responseObject;
}
And
Test s = new Test;
Test s=(Test)post("http://localhost:8081/basic-web-app/test",s);
My problem is that i don't know hot to get the Test object from the response. Please help. Thanks!
You can try to use:
String responseAsString = EntityUtils.toString(response.getEntity());

Throw exception from spring controller which producces xml MediaType

I have the the following controller with one RequestMapping which produces an xml MediaType.
#RestController
#RequestMapping("/api")
public class ArticleResource {
#RequestMapping(value = "/xml/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_XML_VALUE)
public ResponseEntity<byte[]> getXml(#PathVariable(value = "id") String id,
final HttpServletRequest request,
final HttpServletResponse response) {
InputStream inputStream = null;
try {
inputStream = new FileInputStream(path + id + ".xml");
} catch (FileNotFoundException e) {
throw new BadRequestException("No such xml exists");
}
try {
return new ResponseEntity<byte[]>(IOUtils.toByteArray(inputStream), HttpStatus.OK);
} catch (IOException e) {
e.printStackTrace();
}
return new ResponseEntity<byte[]>(HttpStatus.NOT_FOUND);
}
}
The BadRequestException implementation is the following:
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public class BadRequestException extends RuntimeException {
public BadRequestException(String message) {
super(message);
}
}
It works fine when the xml exists, but when the xml cannot be found I have a 406 error code. I suppose the problem occurs because it expects a xml media type and instead a RuntimeException is returned. How can I tackle with this issue?
Do you have an Accept: header in your HTTP request? Your error handler will just return an HTTP error code (response status) so it causes a 406 Not Acceptable on the client side if the client expects XML.
If this is the case you can return an XML response entity from the error handler and update your signature to reflect that it produces XML. Or you can try removing the Accepts from your request.
I solved my issue by returning the following:
String returnString = "XML file don't exists";
return new ResponseEntity<byte[]>(IOUtils.toByteArray(
new ByteArrayInputStream(returnString.getBytes())), HttpStatus.NOT_FOUND);

Paypal IPN verification failed because character encoding

I have a problem with Paypal IPN verification in my Spring boot server. I'm not sure where is the problem, if at the server's side or in the other hand, it's Paypal's fault. I already selected UTF-8 as enconding in my profile page.
The main problem it's IPN with UTF-8 characters, which are making the verification fail I guess.
If I have no CharacterEncodingFilter in my Spring and Spring security server, IPN verification works fine. BUT makes other things (forms, for example) not showing with UTF-8 encoding, so this is an unacceptable solution.
I find strange when I'm printing the IPN (with no CharacterEnconding, so payment gets Verified) the response I get (among other things):
charset=UTF-8
address_name=Adrián
payment_status=Completed
So Paypal says that IPN it's UTF-8 but that's what I'm not receiving.
The server's encoding it's working fine adding CharacterEncodingFilter before Spring Security filter chain:
#Order(1)
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
#Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
FilterRegistration.Dynamic characterEncodingFilter = servletContext
.addFilter("characterEncodingFilter", new CharacterEncodingFilter());
characterEncodingFilter.setInitParameter("encoding", "UTF-8");
characterEncodingFilter.setInitParameter("forceEncoding", "false");
characterEncodingFilter.addMappingForUrlPatterns(null, false, "/*");
insertFilters(servletContext, new MultipartFilter());
}
}
And now, Paypal's IPN printing show params well encoded:
charset=UTF-8
first_name=Adrián
payment_status=Completed
but Paypal's response is INVALID.
This is my Controller that handles Paypal IPN's post:
#RequestMapping(value = "paypalok", method = RequestMethod.POST)
public void processIPN(HttpServletRequest request) {
String PAY_PAL_DEBUG = "https://www.sandbox.paypal.com/cgi-bin/webscr";
String CONTENT_TYPE = "Content-Type";
String MIME_APP_URLENC = "application/x-www-form-urlencoded";
String PARAM_NAME_CMD = "cmd";
String PARAM_VAL_CMD = "_notify-validate";
String PAYMENT_COMPLETED = "Completed";
String paymentStatus = "";
// Create client for Http communication
HttpClient httpClient = HttpClientBuilder.create().build();
// Request configuration can be overridden at the request level.
// They will take precedence over the one set at the client level.
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(40000).setConnectTimeout(40000)
.setConnectionRequestTimeout(40000).build();
HttpPost httppost = new HttpPost(PAY_PAL_DEBUG);
httppost.setHeader(CONTENT_TYPE, MIME_APP_URLENC);
try {
Map<String, String> params = new HashMap<String, String>();
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
nameValuePairs.add(new BasicNameValuePair(PARAM_NAME_CMD, PARAM_VAL_CMD));
// Process the parameters
Enumeration<String> names = request.getParameterNames();
while (names.hasMoreElements()) {
// String param = names.nextElement();
// String value = request.getParameter(param);
String param = new String (names.nextElement().getBytes ("iso-8859-1"), "UTF-8");
String value = new String (request.getParameter(param).getBytes ("iso-8859-1"), "UTF-8");
nameValuePairs.add(new BasicNameValuePair(param, value));
params.put(param, value);
System.out.println(param + "=" + value);
// Get the payment status
if (param.equalsIgnoreCase("payment_status")) paymentStatus = value;
}
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
if (verifyResponse(httpClient.execute(httppost))) {
// if (paymentStatus.equalsIgnoreCase(PAYMENT_COMPLETED)) do...
return "elovendo/pricing/paymentOk";
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return "redirect:/error";
} catch (ClientProtocolException e) {
e.printStackTrace();
return "redirect:/error";
} catch (IOException e) {
e.printStackTrace();
return "redirect:/error";
}
}
private boolean verifyResponse(HttpResponse response) throws IllegalStateException, IOException {
String RESP_VERIFIED = "VERIFIED";
InputStream is = response.getEntity().getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String responseText = reader.readLine();
is.close();
System.out.println("RESPONSE : " + responseText);
return responseText.equals(RESP_VERIFIED);
}
I have uri encoding with:
#Configuration
public class WebAppConfiguration {
/** HTTPS and Paging error **/
#Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
factory.setUriEncoding("UTF-8");
}
}
Resuming, if I send the characters UTF-8 encoded Paypal verification fails, even when it shouldn't come bad-encoded. If I send them bad-encoded, Paypal's response it's ok.
I can't send the IPN's response bad-encoded using CharacterEncodingFilter, can't I?
I'm don't really know what's going on.
Thank you!
Well, I actually don't know why Paypal is sending data wrong encoded, but a simply workaround manages that.
Just overriding CharacterEncodingFilter like this:
public class CharacterEncodingFilter extends org.springframework.web.filter.CharacterEncodingFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if (request != null && !request.getRequestURI().contains("paypalcheck")) {
super.doFilterInternal(request, response, filterChain);
}
else {
filterChain.doFilter(request, response);
}
}
}
making reference to the controller URL that listens the Paypal's IPN and telling the Filter that don't encode the data.
And also, making sure that the filter is before Spring Security chain:
public class SecurityConf extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
filter.setForceEncoding(true);
http.addFilterBefore(filter, WebAsyncManagerIntegrationFilter.class);
}
}

Resources