Spring MVC #ExceptionHandler method in controller invoking in success case - java

I'm trying to catch an error scenario but I'm getting an exception in all cases.
Below is my code snippet:
#RequestMapping(value = "/config/file/data", method = RequestMethod.GET)
#ResponseBody
#ExceptionHandler(RestClientException.class)
#ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Not Found")
public void restClientException()
{
//do nothing
}
public List<myfile> getAllmyfiles() throws RestClientException
{
return myfileService.getAllmyfiles();
}

try this :
#ExceptionHandler(RestClientException.class)
#ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Not Found")
public void restClientException()
{
//do nothing
}
#RequestMapping(value = "/config/file/data", method = RequestMethod.GET)
#ResponseBody
public List<myfile> getAllmyfiles() throws RestClientException
{
return myfileService.getAllmyfiles();
}
you shouldn't use a #RequestMapping with an #ExceptionHandler : the method annotated with #ExceptionHandler is automatically invoked when the specified exception is thrown

Related

How can a spring controller deal with application/octet-stream request?

I wrote a spring controller with following methods to deal with a callback http request,
#PostMapping ("/test")
public void notifyTranscodeResult(String paramStr){
...
}
#PostMapping ("/test2")
public void notifyTranscodeResult(#RequestBody ParamClass param){
...
}
but I get errors: Resolved exception caused by handler execution: org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/octet-stream' not supported
I can't change the callback http request because they are from other third-party services, how can I change my controller to correctly get the request params?
You need to define consumes attribute.
#PostMapping (path = "/test2", consumes = {MediaType.APPLICATION_OCTET_STREAM_VALUE})
Here is the implementation with Unit Test Case.
#PostMapping(value = "/upload",
consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public String demo(HttpServletRequest httpServletRequest) {
ServletInputStream inputStream;
try {
inputStream = httpServletRequest.getInputStream();
} catch (IOException e) {
throw new RuntimeException(e);
}
final List<String> list = new BufferedReader(new InputStreamReader(inputStream))
.lines().toList();
System.out.println(list);
return "Hello World";
}
Test Case
#Autowired
private MockMvc mockMvc;
#Test
public void shouldTestBinaryFileUpload() throws Exception {
mockMvc
.perform(MockMvcRequestBuilders
.post("/api/user/upload")
.content("Hello".getBytes())
.contentType(MediaType.APPLICATION_OCTET_STREAM))
.andExpect(MockMvcResultMatchers
.status()
.isOk())
.andExpect(MockMvcResultMatchers
.content()
.bytes("Hello World".getBytes()));
}

Spring MVC default #RequestMapping to catch unmatched URL

I have this Controller
#Controller
#RequestMapping(value = "/v1.0/user")
public class UserController {
#RequestMapping(value = "/findOne/{id}", method = RequestMethod.GET)
public #ResponseBody String findOne(#PathVariable("id") Integer id) {
log.info("findOne");
return "found URL";
}
}
Which will match the URL: http://localhost:8080/v1.0/user/findOne/4
But if my path varibale is not correct: http://localhost:8080/v1.0/user/findOne/4A
The I get nothing. Not even an error. It's as if Spring swallowed the URL.
I added
#RequestMapping(value = "/.*", method = RequestMethod.GET)
public #ResponseBody String redirectEverythingOtherThanTest(){
log.info("no url matched");
return "badly formed URL for Users";
}
Again I get nothing. What I'm trying to accomplish is for each Controller to have a unique message when the URL does not match.
In the end in each of my Controller classes I added
#ExceptionHandler(Exception.class)
public #ResponseBody String handleException(Exception e, HttpServletRequest request, HttpServletResponse response) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return e.getMessage();
}
And this will catch all exceptions and I can treat it as a catch all.

get modelandview object in another controller

I am working to pass data from one controller to another.
I have one class that is annotated with #ControllerAdvice that is used to handle all exception of application.
I am processing exception and adding them to custom class then in ModelAndView I am adding that and passing to another controller using redirect.
And in that controller I want that added object but I don't have much idea about it how to get that object. I have tried some trick but did not get success.
Code:
ExceptionHandler class:
#ControllerAdvice
public class DefaultExceptionHandler {
#Autowired
private CPro cPro;
private static final Logger LOG = LoggerFactory.getLogger(DefaultExceptionHandler.class);
#RequestMapping(produces = {MediaType.APPLICATION_JSON_VALUE})
#ExceptionHandler(Exception.class)
#ResponseStatus(value = INTERNAL_SERVER_ERROR)
#ResponseBody
public ModelAndView handleException(Exception ex) {
ModelAndView modelAndView = new ModelAndView("redirect:/");
String exceptionType = ex.getClass().getSimpleName();
DefaultExceptionHandler.LOG.error("Internal Server Exception", ex);
ErrorResponse response = new ErrorResponse();
if (ex.getCause() != null) {
response.addSimpleError(exceptionType, ex.getCause().getMessage(), cPro.getProName());
} else {
response.addSimpleError(exceptionType, ex.getMessage(), cPro.getProName());
}
modelAndView.addObject("processingException", response);
return modelAndView;
}
}
my home controller:
#RequestMapping(value = "/", method = RequestMethod.GET)
public String getHomePage(#ModelAttribute("processingException") ErrorResponse errorResponse, Model model) {
// I want to get object data of processingException added in exception handler using ModelAndView
model.addAttribute("processingException", errorResponse.getError() == null ? null : errorResponse);
return "upscale"; //here upscale.html redirection
}
Does anyone have idea that how to get that object data in my controller ?
Thanks.
After a lot googling and searching various forums and article, I found some solution. I have combined data and code of various forums I have made my requirement fulfill.
We can use FlashMap for that. Just get context of request and add FlashMap and add other data to FlashMap as well.
Code:
#ControllerAdvice
public class DefaultExceptionHandler {
#Autowired
private CPro cPro;
private static final Logger LOG = LoggerFactory.getLogger(DefaultExceptionHandler.class);
#ExceptionHandler(Exception.class)
public String handleException(Exception ex, HttpServletRequest request) throws IOException {
DefaultExceptionHandler.LOG.error("Internal Server Exception", ex);
String exceptionType = ex.getClass().getSimpleName();
ErrorResponse response = new ErrorResponse();
if (ex.getCause() != null) {
response.addError(exceptionType, ex.getCause().getMessage(), cPro.getProName());
} else {
response.addError(exceptionType, ex.getMessage(), cPro.getProName());
}
FlashMap outputFlashMap = RequestContextUtils.getOutputFlashMap(request);
if (outputFlashMap != null) {
outputFlashMap.put("processingException", response);
}
return "redirect:/";
}
}
and other hand, in controller use ModelAttribute to get data that is sent from exception handler method.
code:
#RequestMapping(value = "/", method = RequestMethod.GET)
public String getHomePage(Model model, #ModelAttribute("processingException") Object processingException) {
if (processingException instanceof ErrorResponse) {
model.addAttribute("processingException", ((ErrorResponse) processingException).getError());
} else {
model.addAttribute("processingException", null);
}
return "upscale"; //here upscale.html redirection
}
After all bingo.. Done my work.
If anyone have still better idea on it then still welcome..
Thanks guys.
You could make a workaround like this:
public ModelAndView handleException(Exception ex, HttpServletRequest req) {
//...
ModelAndView modelAndView = new ModelAndView("forward:/");
//...
req.setAttribute("processingException", response);
Then in your Controller Method you have access to HttpServletRequest and get the Attribute (Object):
public String getHomePage(#ModelAttribute("processingException", HttpServletRequest req)
{
//....
req.getAttribute("processingException");

Testing thrown exception in controller

I want to perform a test on a controller method which throws an exception. The method is something like this:
#RequestMapping("/do")
public ResponseEntity doIt(#RequestBody Request request) throws Exception {
throw new NullPointerException();
}
When I try to test this method with following code part,
mockMvc.perform(post("/do")
.contentType(MediaType.APPLICATION_JSON)
.content(JSON.toJson(request)))
NestedServletException is thrown from Spring libraries. How can I test that NullPointerException is thrown instead of NestedServletException?
Our solution is rather a workaround: The exception is caught in advice and error body is returned as HTTP response. Here is how the mock works:
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller)
.setHandlerExceptionResolvers(withExceptionControllerAdvice())
.build();
private ExceptionHandlerExceptionResolver withExceptionControllerAdvice() {
final ExceptionHandlerExceptionResolver exceptionResolver = new ExceptionHandlerExceptionResolver() {
#Override
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(final HandlerMethod handlerMethod, final Exception exception) {
Method method = new ExceptionHandlerMethodResolver(TestAdvice.class).resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(new TestAdvice(), method);
}
return super.getExceptionHandlerMethod(handlerMethod, exception);
}
};
exceptionResolver.afterPropertiesSet();
return exceptionResolver;
}
Advice class:
#ControllerAdvice
public class TestAdvice {
#ExceptionHandler(Exception.class)
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Object exceptionHandler(Exception e) {
return new HttpEntity<>(e.getMessage());
}
}
After than, following test method passes successfully:
#Test
public void testException
mockMvc.perform(post("/exception/path"))
.andExpect(status().is5xxServerError())
.andExpect(content().string("Exception body"));
}
Easier way is to inject #ExceptionHandler into your Spring Test Context or it throws exception right in MockMvc.perform() just before .andExpect().
#ContextConfiguration(classes = { My_ExceptionHandler_AreHere.class })
#AutoConfigureMockMvc
public class Test {
#Autowired
private MockMvc mvc;
#Test
public void test() {
RequestBuilder requestBuilder = MockMvcRequestBuilders.post("/update")
.param("branchId", "13000")
.param("triggerId", "1");
MvcResult mvcResult = mvc.perform(requestBuilder)
.andExpect(MockMvcResultMatchers.status().is4xxClientError())
.andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(__ -> Assert.assertThat(
__.getResolvedException(),
CoreMatchers.instanceOf(SecurityException.class)))
.andReturn();
}
That way MvcResult.getResolvedException() holds #Controller's exception!
https://stackoverflow.com/a/61016827/173149

How to return 404 response status in Spring Boot #ResponseBody - method return type is Response?

I'm using Spring Boot with #ResponseBody based approach like the following:
#RequestMapping(value = VIDEO_DATA_PATH, method = RequestMethod.GET)
public #ResponseBody Response getData(#PathVariable(ID_PARAMETER) long id, HttpServletResponse res) {
Video video = null;
Response response = null;
video = videos.get(id - 1);
if (video == null) {
// TODO how to return 404 status
}
serveSomeVideo(video, res);
VideoSvcApi client = new RestAdapter.Builder()
.setEndpoint("http://localhost:8080").build().create(VideoSvcApi.class);
response = client.getData(video.getId());
return response;
}
public void serveSomeVideo(Video v, HttpServletResponse response) throws IOException {
if (videoDataMgr == null) {
videoDataMgr = VideoFileManager.get();
}
response.addHeader("Content-Type", v.getContentType());
videoDataMgr.copyVideoData(v, response.getOutputStream());
response.setStatus(200);
response.addHeader("Content-Type", v.getContentType());
}
I tried some typical approaches as:
res.setStatus(HttpStatus.NOT_FOUND.value());
new ResponseEntity(HttpStatus.BAD_REQUEST);
but I need to return Response.
How to return here 404 status code if video is null?
This is very simply done by throwing org.springframework.web.server.ResponseStatusException:
throw new ResponseStatusException(
HttpStatus.NOT_FOUND, "entity not found"
);
It's compatible with #ResponseBody and with any return value. Requires Spring 5+
Create a NotFoundException class with an #ResponseStatus(HttpStatus.NOT_FOUND) annotation and throw it from your controller.
#ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "video not found")
public class VideoNotFoundException extends RuntimeException {
}
Your original method can return ResponseEntity (doesn't change your method behavior):
#RequestMapping(value = VIDEO_DATA_PATH, method = RequestMethod.GET)
public ResponseEntity getData(#PathVariable(ID_PARAMETER) long id, HttpServletResponse res{
...
}
and return the following:
return new ResponseEntity(HttpStatus.NOT_FOUND);
You can just set responseStatus on res like this:
#RequestMapping(value = VIDEO_DATA_PATH, method = RequestMethod.GET)
public ResponseEntity getData(#PathVariable(ID_PARAMETER) long id,
HttpServletResponse res) {
...
res.setStatus(HttpServletResponse.SC_NOT_FOUND);
// or res.setStatus(404)
return null; // or build some response entity
...
}

Categories

Resources