How to fix RestTemplate NullPointerException in spring boot? - java

#Service
public class RequestSender {
private static final Logger logger = LoggerFactory.getLogger(RequestSender.class);
#Autowired
private RestTemplate restTemplate;
public MbsFtResponseData sendJsonDataToMBS(final MBSTransactionData transactionData) {
String mbsUrl = MBSConfigConstants.mbsUrl;
try {
logger.info("Sending request method Is Initiated");
HttpEntity<MBSTransactionData> httpEntity = new HttpEntity<>(transactionData);
ResponseEntity<MbsFtResponseData> response = restTemplate.exchange(mbsUrl, HttpMethod.POST, httpEntity,
MbsFtResponseData.class);
if (response != null) {
HttpStatus status = response.getStatusCode();
if (status.is2xxSuccessful()) {
logger.info("Response getting back is succeded with the status code {}", status.value());
return response.getBody();
} else {
logger.error("ERROR Response getting back is with the status code {}", status.value());
throw new BBPSMBSException("Error is while connecting to mBS server", status.value());
}
} else {
logger.error("Null value::::::::::::response is null");
}
} catch (Exception e) {
e.printStackTrace();
logger.error("ERROR :::{}:: is occered ", e.getCause());
}
return new MbsFtResponseData("Server Not responding or Busy", 500, "FAILED");
}
}
java.lang.NullPointerException at com.npst.bbps.middleware.mbs.RequestSender.sendJsonDataToMBS(RequestSender.java:26) at com.npst.bbps.middleware.mbs.MbsftServiceImpl.mbsFundTransfer(MbsftServiceImpl.java:27) at com.npst.bbps.middleware.controller.ThirdPartyIntegrationCtrl.initiateRefund(ThirdPartyIntegrationCtrl.java:64) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

RestTemplate bean is not found in the BeanFactory, because you wouldn't configured.
You have to define a bean like below in the configuration file.
#Configuration
public class Config {
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

Related

How to make unitest simulation with mockServer, for external local server

I need to test this method.
public List<HotelInfo> getQueryHotelInfoViaURL(int id) {
private RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8081/hotels?id=" + id;
try {
HotelInfo[] hotelInfoArray = restTemplate.getForObject(new URI(url), HotelInfo[].class);
return Arrays.asList(hotelInfoArray);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
My test is not working:
mvc.perform(get("/offers?arrival=LWO&persons=3&los=2")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().is(200));
I need to simulate fetching url:
http://localhost:8081/hotels?id=
with response Object:
[{"id":1,"hotelName":"SunshineHotel","slogan":"Be where the good life is","hotelImageURL":"https://cdn-cyclingtips.pressidium.com/wp-content/uploads/2018/02/cycling-Switzerland-Andermatt-14.jpg"}]
How i can do this?

How to catch `Request processing failed` for client param error in spring boot?

I register a DateTimeFormatterRegistrar in my class implements WebMvcConfigurer like this:
#Override
public void addFormatters(FormatterRegistry registry) {
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
registrar.setUseIsoFormat(true);
registrar.registerFormatters(registry);
}
In the rest controller, i try to parse client GET params to an object:
#GetMapping("/api/url/path")
public APIResponse getPersonAttendList(#Valid SampleVO vo){}
SampleVO include field LocalDateTime time. If client offered wrong format of time param, the binding will be failed. Server will return 500, and print some log like this:
>ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] 175 - Servlet.service() for
servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed;
nested exception is java.time.format.DateTimeParseException
My question is, how to catch this exception, and return 400 to the client? It seems ControllerAdvice is not working.
In my project I am using #RestControllerAdvice to handle such cases, an example would be,
#RestControllerAdvice
public class MyCustomExceptionsHandler {
#ExceptionHandler({HttpMessageNotReadableException.class})
public ResponseEntity handleException(HttpMessageNotReadableException httpMessageNotReadableException) {
//return the response entity you need here with the correct error
}
}
This is something that is working for me.
You should be able to catch this with a "try{}, catch(){}, and finally{}". You would try the code and then catch the exception, then return a 400 error to the client.
try {
//your code
} catch (java.time.format.DateTimeParseException e) {
//Return 400 here
} finally {
//Other code here
}
I'm sorry. I made a mistake here. First, i design to return client all response like
#Getter
#Setter
#AllArgsConstructor
public class APIResponse {
private Integer status;
private String msg;
private Object data;
#NotNull
public static APIResponse fromData(Object data) {
return new APIResponse(0, "", data);
}
#NotNull
public static APIResponse fromError(#NotNull BaseError err) {
return new APIResponse(err.getStatus(), err.getMsg(), null);
}
#NotNull
public static APIResponse fromError(#NotNull BaseError err, Object data) {
return new APIResponse(err.getStatus(), err.getMsg(), data);
}
#NotNull
public static APIResponse fromEmpty() {
return new APIResponse(0, "", null);
}
}
I made a global error catch like this:
#RestControllerAdvice
#Slf4j
public class ErrorWrapper {
private String getErrMsg(BindingResult bindingResult) {
StringBuilder stringBuilder = new StringBuilder();
for (FieldError error : bindingResult.getFieldErrors()) {
stringBuilder.append(error.getDefaultMessage()).append(";");
}
String msg = stringBuilder.toString();
log.debug("valid error:{}", msg);
return msg;
}
/**
* Bean validation error
* #see javax.validation.Valid
* #see org.springframework.validation.Validator
* #see org.springframework.validation.DataBinder
*/
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public APIResponse paramNotValidHandler(MethodArgumentNotValidException e) {
BindingResult bindingResult = e.getBindingResult();
return APIResponse.fromError(new ParamError(getErrMsg(bindingResult)));
}
#ExceptionHandler(BindException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public APIResponse paramBindErrorHandler(BindException e) {
BindingResult bindingResult = e.getBindingResult();
return APIResponse.fromError(new ParamError(getErrMsg(bindingResult)));
}
#ExceptionHandler(MethodArgumentTypeMismatchException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
private APIResponse paramConvertErrorHandler(MethodArgumentTypeMismatchException e) {
log.debug("valid error:", e);
return APIResponse.fromError(new ParamError("argument error:%s", e.getCause().getMessage()));
}
#ExceptionHandler(ServletRequestBindingException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
private APIResponse paramBindErrorHandler(ServletRequestBindingException e) {
return APIResponse.fromError(new ParamError("param bind error"));
}
#ExceptionHandler(InvalidPropertyException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public APIResponse invalidProperty(InvalidPropertyException e) {
return APIResponse.fromError(new ParamError(e.getPropertyName() + " format error"));
}
#ExceptionHandler(PermissionError.class)
#ResponseStatus(HttpStatus.FORBIDDEN)
private APIResponse permissionErrorHandler(PermissionError e) {
log.debug("not allowed:", e);
return APIResponse.fromError(e);
}
#ExceptionHandler(HttpRequestMethodNotSupportedException.class)
#ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
private APIResponse methodError() {
return APIResponse.fromError(new ClientError("HTTP Method error"));
}
#ExceptionHandler(MaxUploadSizeExceededException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
private APIResponse fileTooLarge() {
return APIResponse.fromError(new ClientError("file is too big"));
}
#ExceptionHandler(MissingServletRequestPartException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
private APIResponse badRequest(MissingServletRequestPartException e) {
return APIResponse.fromError(new ClientError("file not exist"));
}
#ExceptionHandler(HttpMessageConversionException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
private APIResponse badRequest(HttpMessageConversionException e) {
return APIResponse.fromError(new ClientError("can't parse"));
}
#ExceptionHandler(ClientAbortException.class)
private APIResponse clientAbortHandler(ClientAbortException e) {
return null;
}
#ExceptionHandler(ClientError.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
private APIResponse clientErrorHandler(ClientError e) {
log.debug("bad request:", e);
return APIResponse.fromError(e);
}
#ExceptionHandler(ServerError.class)
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
private APIResponse serverErrorHandler(ServerError e) {
log.error("server error:" + e.getMsg(), e);
return APIResponse.fromError(e);
}
#ExceptionHandler(DataIntegrityViolationException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
private APIResponse duplicateKeyError(DataIntegrityViolationException e) {
log.debug("duplicate source:", e);
return APIResponse.fromError(new ClientError("db unqiue key error"));
}
#ExceptionHandler(Exception.class)
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
private APIResponse unknownErrorHandler(Exception e) {
String tips = "unknown error:";
if (e.getCause() != null) {
tips += e.getCause().getMessage();
} else if (e.getMessage() != null) {
tips += e.getMessage();
} else {
tips += e.toString();
}
log.error(tips, e);
return APIResponse.fromError(new ServerError());
}
So, if no catcher matched above, the last one will return 500.
There are so many exceptions for spring boot, i don't know how to catch them all and return 400 without missing any one.
If you want to catch all spring's exceptions in your controller advice then you need to catch Exception.class and then check the class name of the exception.
#Slf4j
#RestControllerAdvice(basePackages = "com.your.package.name")
public class ErrorWrapper {
#ExceptionHandler(Exception.class)
public APIResponse handleException(Exception ex, WebRequest request) {
if (ex.getClass().getCanonicalName().startsWith("org.springframework.")) {
return APIResponse.fromError(new ClientError(ex.getMessage()));
}
return APIResponse.fromError(new ServerError());
}
}
Surround this statement(DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar()) in try catch block .
try{
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
} catch (Exception e) {
e.printStacktrace();
}

Test upload endpoint through postman

I am trying to upload a file to my server using an endpoint exposed through spring. However when I try to test the api through postman, I get Current request is not a multipart request error. I went through this question MultipartException: Current request is not a multipart request but still couldn't fix this. Please Help. Thanks in advance.
Here is my controller:
#RestController
#RequestMapping
public class UploadController {
#Autowired
StorageService storageService;
List<String> files = new ArrayList<String>();
#PostMapping("/post")
public ResponseEntity<String> handleFileUpload(#RequestParam("file") MultipartFile file) {
String message = "";
try {
storageService.store(file);
files.add(file.getOriginalFilename());
message = "You successfully uploaded " + file.getOriginalFilename() + "!";
return ResponseEntity.status(HttpStatus.OK).body(message);
} catch (Exception e) {
message = "FAIL to upload " + file.getOriginalFilename() + "!";
return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(message);
}
}
#GetMapping("/getallfiles")
public ResponseEntity<List<String>> getListFiles(Model model) {
List<String> fileNames = files
.stream().map(fileName -> MvcUriComponentsBuilder
.fromMethodName(UploadController.class, "getFile", fileName).build().toString())
.collect(Collectors.toList());
return ResponseEntity.ok().body(fileNames);
}
#GetMapping("/files/{filename:.+}")
#ResponseBody
public ResponseEntity<Resource> getFile(#PathVariable String filename) {
Resource file = storageService.loadFile(filename);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"")
.body(file);
}
}
My Service:
#Service
public class StorageService {
Logger log = LoggerFactory.getLogger(this.getClass().getName());
private final Path rootLocation = Paths.get("upload-dir");
public void store(MultipartFile file) {
try {
Files.copy(file.getInputStream(), this.rootLocation.resolve(file.getOriginalFilename()));
} catch (Exception e) {
throw new RuntimeException("FAIL!");
}
}
public Resource loadFile(String filename) {
try {
Path file = rootLocation.resolve(filename);
Resource resource = new UrlResource(file.toUri());
if (resource.exists() || resource.isReadable()) {
return resource;
} else {
throw new RuntimeException("FAIL!");
}
} catch (MalformedURLException e) {
throw new RuntimeException("FAIL!");
}
}
public void deleteAll() {
FileSystemUtils.deleteRecursively(rootLocation.toFile());
}
public void init() {
try {
Files.createDirectory(rootLocation);
} catch (IOException e) {
throw new RuntimeException("Could not initialize storage!");
}
}
}
As you can see below I am sending file as form Data and no headers are being set
see below in the image, and add key value as file
Your Controller expects a request parameter "file":
#RequestParam("file") MultipartFile file
You have to set the key "file" in postman, where the value is your file (last screenshot).
Try adding in your request header Content-Type: multipart/form-data (as far as I see in postman it is missing)

How Receieve Validation Errors In Java Android Spring

I am sending API requests to a backend API using Spring in Android (Java). My question is how to receive validation errors to the error handler at ex 400 bad request response. Here is my code:
class RestTask extends AsyncTask<String,Void,ResponseEntity<ExpectedReturn>>
{
protected ResponseEntity<ExpectedReturn> doInBackground(String... uri)
{
try{
final String url = uri[0];
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(subscriber.getErrorHandler());
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
// set authentication tokens:
ResponseEntity<ExpectedReturn> response = restTemplate.exchange(url,callMethod,httpEntity, expectedReturnClass);
return response;
}catch(Exception e)
{
System.out.println(e.getMessage());
}
return null;
}
#Override
protected void onPostExecute(ResponseEntity<ExpectedReturn> result) {
if(result !=null && result.getBody() !=null)
{
subscriber.getSubscriber().onComplete(result.getBody(),result.getStatusCode());
}
}
}
My question is, if the post data fails validation (is incorrect), the API will return as JSON error object with errors, ex:
In case of a validation error, the error handler is called with a ClientHttpResponse object as a parameter. Calling the response.getBody() returns an InputStream. My question is, is there any way of receiving an object mapped from the JSON error response (as shown above) to the error handler, or perhaps converting the input stream to something readable (like a hashmap) so I can display the errors returned by the API (ex: "Name is required" etc...)?
I've tested your code and in case of a 400 bad request the catch block receives an instance of HttpClientErrorException which has a method to get the error body as String:
private class HttpRequestTask extends AsyncTask<Void, Void, String> {
#Override
protected String doInBackground(Void... params) {
try {
final String url = "https://reqres.in/api/login";
RestTemplate restTemplate = new RestTemplate();
//Same result with restTemplate.exchange() too
return restTemplate.postForObject(url, "{\n" +
" \"email\": \"peter#klaven\"\n" +
"}", String.class);
} catch (Exception e) {
Log.e(TAG, e.getMessage());
if (e instanceof HttpClientErrorException) {
String responseBodyAsString = ((HttpClientErrorException) e).getResponseBodyAsString();
Log.e(TAG, "Validation error" + responseBodyAsString);
//You can parse this with gson or jackson here
return responseBodyAsString;
}
}
return null;
}
#Override
protected void onPostExecute(String result) {
Log.d(TAG, "onPostExecute() called with: result = [" + result + "]");
}
}
Which prints in:
W/RestTemplate: POST request for "https://reqres.in/api/login" resulted in
400 (Bad Request); invoking error handler
E/MainActivity: 400 Bad Request
E/MainActivity: Validation error{"error":"Missing email or username"}
D/MainActivity: onPostExecute() called with: result = [{"error":"Missing email or username"}]
If you want to use the none default error handler and set your custom error handler you can get the error message as string this way:
restTemplate.setErrorHandler(new ResponseErrorHandler() {
#Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return response.getStatusCode().is4xxClientError();
}
#Override
public void handleError(ClientHttpResponse response) throws IOException {
String errorResponse = new String(getResponseBody(response), getCharset(response).name());
Log.e(TAG, "handleError: called with: " + errorResponse);
}
});
private byte[] getResponseBody(ClientHttpResponse response) {
try {
InputStream responseBody = response.getBody();
if (responseBody != null) {
return FileCopyUtils.copyToByteArray(responseBody);
}
} catch (IOException ex) {
// ignore
}
return new byte[0];
}
private Charset getCharset(ClientHttpResponse response) {
HttpHeaders headers = response.getHeaders();
MediaType contentType = headers.getContentType();
return contentType != null ? contentType.getCharSet() : Charset.defaultCharset();
}
Then you can use Jackson or Gson to parse the error response as below:
new Gson().fromJson(responseBodyAsString, ExpectedResponse.class);
Note I've just did the same thing as implemented in DefaultResponseErrorHandler
Edit:
The whole AsyncTask and Spring Android APIs are so outdated, Here is the same example with Retrofit:
api.login(new BodyModel("peter#klaven"))
.enqueue(new Callback<ExpectedModel>() {
#Override
public void onResponse(#NonNull Call<ExpectedModel> call, #NonNull Response<ExpectedModel> response) {
if (response.isSuccessful()) {
//Do what you got to do
} else {
Converter<ResponseBody, ErrorModel> converter = MainActivity.this.retrofit.responseBodyConverter(ErrorModel.class, new Annotation[0]);
ErrorModel errorModel = null;
try {
errorModel = converter.convert(response.errorBody());
Toast.makeText(MainActivity.this, errorModel.toString(), Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
#Override
public void onFailure(#NonNull Call<ExpectedModel> call, #NonNull Throwable t) {
t.printStackTrace();
}
})
You can find the full gist in my github repo

how to write Junit Test case for Rest call?

Can anyone suggest me how to write JUnit for the below class:
#Controller
#RequestMapping(value = "/cutdata", consumes = "TEXT/XML")
public class CustController
{
Logger LOG = Logger.getLogger(CustController.class);
#Autowired
CustService custService;
#Autowired
MarCusService marCustService;
#Resource(name = "CustValidator")
private CusValidator validator;
#Resource(name = "cmsSiteService")
private CMSSiteService cmsSiteService;
protected CMSSiteService getCmsSiteService()
{
return cmsSiteService;
}
#RequestMapping(value = "/methodcall", method = RequestMethod.PUT)
public #ResponseBody ResponseEntity<?> methodCall(#RequestBody final CustDTO data)
throws WebServicesException
{
String statusCode = null;
try {
if (data.getGroup() != null && !data.getGroup().equals(String.valueOf(GroupEnum.ALL))) {
validator.validate(data);
}
} catch (WebServicesException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.OK);
}
try
{
final CMSSiteModel site = cmsSiteService.getCurrentSite();
String currentSiteId=site.getUid() !=null ? site.getUid():"";
if(StringUtils.contains(currentSiteId,Config.getParameter("****.siteuid")))
{
statusCode = marCustService.processData(data);
}
else
{
statusCode = custService.processData1(data);
}
final String[] message = statusCode.split(":");
final String code = message[0];
final String statusMessage = message[1];
if (code.equalsIgnoreCase("200"))
{
return new ResponseEntity<>(statusMessage, HttpStatus.CREATED);
}
else if (code.equalsIgnoreCase("400"))
{
return new ResponseEntity<>(statusMessage, HttpStatus.BAD_REQUEST);
}
}
catch (final Exception e)
{
LOG.error("log ::" + e);
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
I'm new in writing JUnit Test case, i need help like how to write or how to start JUnit.
Basically, you need to make use of the Spring context to test Controller classes.
One example would be something like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
#WebAppConfiguration
public class CsrfShowcaseTests {
#Autowired
private WebApplicationContext context;
private MockMvc mvc;
#Before
public void setup() {
mvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
#Test
public void shouldTestMethodCall() {
mockMvc.perform(put("/methodcall"))
.andExpect(status.isOk());
}
}
From this test you can expand the testing to whatever your flows are.
If you need more references, you can check Spring's documentation here.

Categories