On the server I am handling exceptions globally using #ControllerAdvice
CODE:
#ControllerAdvice
#EnableWebMvc
public class GlobalExeptionHandler
{
public static final String DEFAULT_ERROR_VIEW = "error";
#ExceptionHandler(value = {Exception.class,RuntimeException.class})
public ModelAndView defaultErrorHandler (HttpServletRequest request, Exception e)
{
ModelAndView mav = new ModelAndView(DEFAULT_ERROR_VIEW);
mav.addObject("datetime", new Date());
mav.addObject("exception", e);
mav.addObject("url", request.getRequestURL());
return mav;
}
#ExceptionHandler(value = {UnableToSaveException.class})
public ModelAndView saveExceptionHandler (HttpServletRequest request, UnableToSaveException e)
{
ModelAndView mav = new ModelAndView(DEFAULT_ERROR_VIEW);
mav.addObject("errCode", e.getErrCode());
mav.addObject("errMsg", e.getErrMsg());
return mav;
}
}
I know that using ExtJS on client I can catch exception using this code:
Ext.Ajax.on('requestexception', function(conn, response, options, eOpts)
{
...
}
I want to rise dialog with error Message which I want to get from ModelAndView which is returned in spring, but I do not know how to get it. Can someone tell me how, or say if I doing something wrong?
Related
I am trying to customize my body error message.
My springboot version is 2.1.5.RELEASE
I want this:
{
"This should be application specific"
}
but I'm receiving this:
{
"timestamp": "2019-05-24T15:47:10.872+0000",
"status": 500,
"error": "Internal Server Error",
"message": "Not Found (404)",
"path": "/github/gifojhuinh4w5"
}
My exception class is:
#ControllerAdvice
public class AppExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(Exception.class)
protected ResponseEntity<Object> handleConflict(Exception ex, WebRequest request) {
String bodyOfResponse = "This should be application specific";
return handleExceptionInternal(ex, bodyOfResponse,
new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR, request);
}
}
My class where exception is captured
#Controller
#EnableAutoConfiguration
public class GitHub {
#RequestMapping(value ="/github/{usuario}", produces = "application/json; charset=UTF-8")
#ResponseBody
public ResponseEntity<Object> quantidadeRepositorios(#PathVariable(value = "usuario")String usuario) throws IOException {
HashMap<String, Integer> map = new HashMap<>();
RepositoryService service = new RepositoryService();
GitHubClient client = new GitHubClient();
Gson gson = new Gson();
client.setOAuth2Token("key");
map.put("Total",service.getRepositories(usuario).size()); // exception captured here
return new ResponseEntity<>(gson.toJson(map), HttpStatus.OK);
}
}
When exception is caught by ExceptionHandler, build a response entity and return it as below.
Create a ErrorResponseDTO object and set message to it.
public class ErrorResponseDTO {
private String errorMessage;
}
In exception handler, return that dto object.
#ExceptionHandler(Exception.class)
protected ResponseEntity<Object> handleConflict(Exception ex, WebRequest request) {
ErrorResponseDTO errorDTO = new ErrorResponseDTO();
errorDTO.setErrorMessage("This should be application specific");
return new ResponseEntity<>(errorDTO, HttpStatus.INTERNAL_SERVER_ERROR);
}
This will give you the payload, you are looking for.
{
"This should be application specific"
}
I am using spring mvc, to handle excpetion i use global exception handler
#ControllerAdvice
public class GlobalControllerExceptionHandler {
#ResponseStatus(value = HttpStatus.CONFLICT, reason = "Data integrity violation")
#ExceptionHandler({DataIntegrityViolationException.class})
public #ResponseBody AdminResponse handleConflict(DataIntegrityViolationException ex,HttpServletResponse httpServletResponse) {
AdminResponse error = new AdminResponse ();
httpServletResponse.setStatus(HttpStatus.CONFLICT.value());
error.setStatus(Status.FAILURE);
error.setErrorDescription(ex.getMessage());
return error;
}
as i know, the annotation #ResponseStatus(value = HttpStatus.CONFLICT will change the repose status code into HttpStatus.CONFLICT, but that is not happen.
when i created dummy exception and annotated this dummy exception with #ResponseStatus then throw this new exception, the GlobalControllerExceptionHandler catches and handle the exception and also changes the response status code.
how can i change the response status code without creating new Exception, i just need to catch DataIntegrityViolationException
You take to two way.
1. use #ResponseBody and return custom JSON String.
#ExceptionHandler(value = { HttpClientErrorException.class, HTTPException.class })
public #ResponseBody String checkHTTPException(HttpServletRequest req, Exception exception,
HttpServletResponse resp) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
CommonExceptionModel model = new CommonExceptionModel();
model.setMessage("400 Bad Request");
model.setCode(HttpStatus.BAD_REQUEST.toString());
String commonExceptionString = mapper.writeValueAsString(model);
return commonExceptionString;
}
2. use ResponseEntity and exception
Return ResponseEntity.
ResponseEntity.status(exception.getStatusCode()).headers(exception.getResponseHeaders())
.body(exception.getResponseBodyAsString());
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");
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
...
}
I'm new to Spring MVC. I'm getting errors on the following (not sure yet what;s the full scope of info requierd to assist me):
Working fine:
#RequestMapping(value = "startpage.do")
public ModelAndView startpage(HttpServletRequest req, HttpServletResponse res) {
.
.
ModelAndView mv = new ModelAndView("startpage");
mv.getModelMap().addAttribute("loginPage", loginPage);
return mv;
But failing:
#RequestMapping(value = "somecontroller.do")
public ModelAndView ftcontroller(HttpServletRequest req, HttpServletResponse res, ModelAndView mav) {.. ...
ModelAndView mv = new ModelAndView("startpage");
mv.getModelMap().addAttribute("loginPage", loginPage);
return mav;
As you can see, same code, different request mapping. Could it be that this is consuing the MVC somehow to get confused?
The error I'm getting is:
java.lang.NullPointerException
at jsp_servlet._web_45_inf._jsp.__somecontroller._jspService(__ftcontroller.java:103)
at weblogic.servlet.jsp.JspBase.service(JspBase.java:34)
at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:227)
at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:125)
at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:292)
Truncated. see log file for complete stacktrace
The biz logic is a login screen "startpage.do" when Login is submitted, it can fail (in which case, startup.do should be retuend once again, in other case, a differn page should be returned.
if there is better way to implement this, I'll be happy to hear that.
(It should be noted that I'm trying to plug-in Spring MVC into an existing project, so I'm trying to make as little as possible changes, and hence not using spring:form etc..)
More info:
The JSP startpage.jsp (resolved from "startpage") has a form with target="somecontroller.do".
Full controller code:
class
{
public static void main(String[] args)
{
#RequestMapping(value = "startpage.do")
public ModelAndView startpage(HttpServletRequest req, HttpServletResponse res) {
System.out.println(">>>>>>HomeController: Passing through (Get Type)...");
LoginPage loginPage = new LoginPage();
ModelAndView mv = new ModelAndView("startpage");
mv.getModelMap().addAttribute("loginPage", loginPage);
return mv;
}
#RequestMapping(value = "somecontroller.do")
public ModelAndView ftcontroller(HttpServletRequest req, HttpServletResponse res)
throws Exception {
// Parsing for login request;
String sUsername = req.getParameter(USER_ID);
String sUserPassword = req.getParameter(PASSWORD);
AbstractResponseDataComponent returnedResponse = new LoginCommand().login(sUsername, sUserPassword);
String returnedView = GlobalConstants.EMPTY_STRING;
JstlView view = new JstlView();
Map model = new HashMap();
if (returnedResponse.isSuccessful())
{
view.setUrl("somecontroller");
model.put("loginResponse", (LoginResponse) returnedResponse);
} else
{
view.setUrl("startpage");
model.put("loginPage", (LoginPage) returnedResponse);
}
return new ModelAndView(view, model);
}
}
}
BTW: when using Spring 3.0 make your method signature more clean:
instead of
public ModelAndView ftcontroller(HttpServletRequest req, HttpServletResponse res)
throws Exception {
String sUsername = req.getParameter(USER_ID);
String sUserPassword = req.getParameter(PASSWORD);
...
do it in the spring 3.0 way:
public ModelAndView ftcontroller(
#RequestParam(USER_ID) String sUsername,
#RequestParam(PASSWORD) String sUserPassword)
throws Exception {
...
For your second question:
if there is better way to implement
this, I'll be happy to hear that.
Are you looking for something like this:
#RequestMapping(value = "startpage.do")
public String startpage() {
...
if (loginFailed) {
return "redirect:startpage.do");
} else {
return "redirect:somecontroller.do");
}
}
(I prefere redirects, because I assume the that login methods has some sideeffects.)
I belive the cause of your exception is the JstlView.
Try not to use the JstlView direct, instad pass the view name as String.
final String viewName;
Map model = new HashMap();
if (returnedResponse.isSuccessful())
{
viewName = "somecontroller";
model.put("loginResponse", (LoginResponse) returnedResponse);
} else
{
viewName = "startpage";
model.put("loginPage", (LoginPage) returnedResponse);
}
return new ModelAndView(viewName, model);