Default return error for white label erorr - spring

I have this controller which is open when user is redirected.
#Controller()
public class RedirectController {
#RequestMapping(method = RequestMethod.GET, value = "/redirect")
#ResponseBody
public String handleRedirectMessage(HttpServletRequest request) throws Exception {
......
}
}
But when the match is not found I get Whitelabel Error Page. Is there some way to set default handler which is by default open when path is not correct?

You need add following property:
server.error.whitelabel.enabled=false
And implement org.springframework.boot.web.servlet.error.ErrorController interface and overrides its getErrorPath() to return a custom path to call when an error occurred:
#Controller
public class MyErrorController implements ErrorController {
#RequestMapping("/error")
public String handleError() {
//do something like logging
return "error";
}
#Override
public String getErrorPath() {
return "/error";
}
}
After that add error page for path "/error" Thymeleaf error.html.
Also you could add pager for different errors:
#RequestMapping("/error")
public String handleError(HttpServletRequest request) {
Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
if (status != null) {
Integer statusCode = Integer.valueOf(status.toString());
if(statusCode == HttpStatus.NOT_FOUND.value()) {
return "error-404";
}
else if(statusCode == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
return "error-500";
}
}
return "error";
}
You can find here full code example
and description.

Related

What is the best way to return different types of ResponseEntity in Spring-Boot

I would like to return two different response for a spring boot rest API.
I should not be using <?> wild card as i get the sonar issue "Generic wildcard types should not be used in return types"
My code:
#GetMapping(path = {"/v1/{type}"}, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> method(#PathVariable(value = "type") boolean type) {
boolean b = type;// some logic
if (b) {
Success result=new Success();
result.setSuccess("Yes");
return new ResponseEntity<>(result,HttpStatus.OK);
}
else {
Error result=new Error();
result.setError("No");
return new ResponseEntity<>(result,HttpStatus.CONFLICT); //appropriate error code
}
}
Any idea how to handle this situation.
Update:
public interface MyResponse{
public Success getSuccessObj();
public Error getErrorObj();
}
#Service
public class Success implements MyResponse {
public Error getErrorObj(){
return null;
}
public Success getSuccessObj(){
Success s=new Success();
return s;
}
#Service
public class Error implements MyResponse {
public Error getErrorObj(){
Error e=new Error();
return e;
}
public Success getSuccessObj(){
return null;
}
Not claiming to be "the best way", but one approach can be:
Introduce:
package com.my.package;
public interface MyResponseI { //if Error, Success (and others) have more "in common", you can also introduce an (abstract) class (with fields, methods, etc.)!
}
"Implement"/Extend:
public class Success implements com.my.package.MyResponseI { //everything else can stay}
as
public class Error implements com.my.package.MyResponseI { //everything else can stay}
Use as Response Type:
#...
public ResponseEntity<com.my.package.MyResponseI> ...
(on client side distinguish).
..and in "your domain" (error, success, ...), you are free to use any "tweaks" of a object oriented design.
Useful links/entries:
https://stackoverflow.blog/2020/03/02/best-practices-for-rest-api-design/
https://swagger.io/resources/articles/best-practices-in-api-design/
https://www.google.com/search?q=rest+api+design
, but also
https://www.google.com/search?q=object+oriented+design
and https://www.google.com/search?q=domain+driven+design
This should work
I tried the snippet below by myself and it worked for me:
#GetMapping("/testresponse/{id}")
public ResponseEntity<?> testMyResponse(#PathVariable("id") int id)
{
if(id==1)
return ResponseEntity.ok(new Success());
else return new ResponseEntity<>(new Error(), HttpStatus.CONFLICT);
}
public class Success {
private String msg = "Success";
public String getMsg() {
return msg;
}
}
public class Error {
private String msg = "Error";
public String getMsg() {
return msg;
}
}
EDIT: The solution as below doesn't work
You should also define an interface for both Success and Error classes. Let say the interface MyResponse
And then change your method declaration, it would look like this
public ResponseEntity<MyResponse> method(#PathVariable(value = "type") boolean type)
If so, the return statement, could be:
return new ResponseEntity<>(result, HttpStatus.OK);
Or
//for status 200 OK
return ResponseEntity.ok(result);

Spring redirect from ErrorController

I have a number of generic error pages which are used by multiple applications other than the one I have control of. I would like to configure the Spring Boot error controller to do a redirect to one of these pages. Unfortunately it's not working.
Eg.
#Controller
public class MyCustomErrorController implements ErrorController {
#GetMapping(value = "/error")
public String handleError(HttpServletRequest request) {
Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
if (status != null) {
Integer statusCode = Integer.valueOf(status.toString());
if (statusCode == HttpStatus.NOT_FOUND.value()) {
return "redirect:https://www.example.com/error-404";
}
}
return "redirect:https://www.example.com/error-500";
}
#Override
public String getErrorPath() {
return "/error";
}
}
If for example I purposefully mistype a URL I can see the response has the Location header with the 404 URL I am expecting but the browser doesn't actually redirect. Any ideas if it's possible to do a redirect from within a custom ErrorController?
Could this be because I'm trying to test this from localhost, and Strict-Transport-Security is ignoring the response Location header value (which is on a FQDN)?
How about adding HttpServletResponse to the method param and use it for redirection?
HttpServletResponse response;
response.sendRedirect("https://www.example.com/error-404");
Reference HERE
Add the following properties in application.proerties file
server.error.whitelabel.enabled=false
try this.
#Controller
public class MyCustomErrorController implements ErrorController {
#GetMapping(value = "/error")
public String handleError(HttpServletRequest request) {
Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
if (status != null) {
Integer statusCode = Integer.valueOf(status.toString());
if (statusCode == HttpStatus.NOT_FOUND.value()) {
return "redirect:/error-404"; //remove your https://www.example.com
}
}
return "redirect:/error-500";
}
#Override
public String getErrorPath() {
return "/error";
}
}
** EDIT **
change the url mapping and try again:
error-404 -> error/404
error-500 -> error/500
#Controller
public class MyCustomErrorController implements ErrorController {
#GetMapping(value = "/error")
public String handleError(HttpServletRequest request) {
Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
if (status != null) {
Integer statusCode = Integer.valueOf(status.toString());
if (statusCode == HttpStatus.NOT_FOUND.value()) {
return "redirect:/error/404"; //remove your https://www.example.com
}
}
return "redirect:/error/500";
}
#Override
public String getErrorPath() {
return "/error";
}
}
error/404
#GetMapping("/error/404")
error/500
#GetMapping("/error/500")

How to correctly handle exceptions from the service (spring boot rest)

When building a rest api using spring boot what is the best way to handle exceptions from the service level and pass them to the controller, so the client gets a custom json error message.
{
"message": "some error"
}
Endpoint from controller
#PostMapping("/login")
public String login(#RequestBody #Valid LoginDto loginDto) {
return gson.toJson(userService.login(loginDto.getUsername(), loginDto.getPassword()));
}
Service level code
public LoginResponseDto login(String username, String password) {
try {
//performs some checks
...
return new LoginResponseDto(token.get());
} catch (AuthenticationException e){
LOGGER.info("Log in failed for user {}", username);
}
return new LoginResponseDto("login failed");
}
LoginResponseDto class
String token;
String message;
public LoginResponseDto(String message) {
this.message = message;
}
Currently it is obviously returning the correctly message but not the correct status code, it will show status 200 with the error message in json.
You have some options:
1) Returning a message:
If you want to return a message something like this,
{
"message": "some error"
}
What you can do is:
Option 1: Create a custom POJO class for error message and return the reference to the object of that POJO class.
Something like this:
ErrorMessage.java
package org.example;
public class ErrorMessage {
private String message;
public ErrorMessage(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Request Handler Method in Controller:
#GetMapping("/login{?username, password}")
public ErrorMessage isUserAuthenticated(#RequestParam String username, #RequestParam String password) {
if (username.toLowerCase().contentEquals("root") && password.contentEquals("system")) {
return new ErrorMessage("authenticated");
}
return null;
}
Option 2: Create a Map and insert key-value pairs that you want to have in the message.
Like this:
#GetMapping("/login{?username, password}")
public Map<String, String> isUserAuthenticated(#RequestParam String username, #RequestParam String password) {
Map<String, String> message = new HashMap<>();
if (username.toLowerCase().contentEquals("root") && password.contentEquals("system")) {
message.put("message", "authenticated");
}
return message;
}
2) Returning an error status code (highly recommended by me):
You may use ResponseEntity for this purpose.
#GetMapping("/login{?username, password}")
public ResponseEntity<?> isUserAuthenticated(#RequestParam String username, #RequestParam String password) {
if (username.toLowerCase().contentEquals("root") && password.contentEquals("system")) {
return new ResponseEntity<>(HttpStatus.OK);
}
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}

Getting HTTP 404 error in Spring login

Error:
HTTP Status 404 – Not Found
Type Status Report
Message /DemoTask/
Description The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.
Apache Tomcat/7.0.90
Code:
import com.demo.bean.Login;
#Controller
public class LogController {
#RequestMapping({ "/login" })
public String showHome(#ModelAttribute("login") Login login) {
String name = login.getUname();
String password = login.getUpass();
if ((name.equals("hello")) && (password.equals("hello"))) {
return "redirect:admin.do";
}
return "login";
}
#RequestMapping({ "login" })
public String showLogin() {
return "login";
}
}
Ambigious Request Mapping for the uri "login".
Added request type to mapping as below
#Controller
public class LogController {
#RequestMapping(value = { "/login" }, method = RequestMethod.POST)
public String showHome(#ModelAttribute("login") Login login) {
String name = login.getUname();
String password = login.getUpass();
if ((name.equals("hello")) && (password.equals("hello"))) {
return "redirect:admin.do";
}
return "login";
}
#RequestMapping(value = { "login" }, method = RequestMethod.GET)
public String showLogin() {
return "login";
}
}

Return error from spring controller via ajax call

I am trying to develop a spring boot application involving sports, I can not see how to return my error after an ajax call in the error section instead of success, I wonder how I can recuperate all the returns coming from the controller of the Class error in the error section and not in the success section
N.B : Everything work fine in this code, only errors are returned in success part.
Class Error:
public class Error extends Exception{
public String code;
public String message;
}
Class Sport:
public class Sport {
public String id;
public String name;
}
Ajax Call
$.ajax({
type : "GET",
url : "/sports-actions",
data: {"id" : sportId},
contentType: "application/json",
dataType : 'json',
success: function (result) {
console.log(result);
},
error: function (e) {
console.log(e);
}
})
Spring Controller
#RestController
#RequestMapping("/sports-actions")
public class SportController {
#RequestMapping(method = RequestMethod.GET)
public Object deleteSport(#RequestParam("id") String id) {
return new Error(404, "id is not valid");
}
}
Edit
I extended my Error class from Exception, but I have error doing this
throw new Error(400 ,"id is not valid") // I get incompatibale type...
You can do following for testing purpose:
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<Object> deleteSport(#RequestParam("id") String id) {
if({if id exists}) {
return new ResponseEntity<Object>({your response object}, HttpStatus.OK);
} else {
//If the id doesn't exist.
return new ResponseEntity<Error>(new Error(),HttpStatus.BAD_REQUEST);
}
}
Best Practice
You should use #ControllerAdvice to handle exceptions using #ExceptionHandler on method level.
#ControllerAdvice
public class RestControllerAdvice {
#ExeptionHandler(NotFoundException.class)
public ResponseEntity<Error> handleNotFound(NotFoundException nfe) {
//LOG error
Error error = new Error();
error.setCode(HttpStatus.NOT_FOUND);
error.setMessage("ID not found OR Your custom message or e.getMessage()");
return new ResponseEntity<Error>(error, HttpStatus.NOT_FOUND);
}
}
Your controller method
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<Object> deleteSport(#RequestParam("id") String id) {
if({if id exists}) {
return new ResponseEntity<Object>({your response object}, HttpStatus.OK);
} else {
throw new NotFoundException("Id not found");
}
}
Above ControllerAdivce method will get invoked, if your NotFoundException is thrown during request processing. You can always customize the error.
Your current implementation of SportController will return HTTP status 200 which will never go into error: function (e) {. You need to throw an exception from the controller in order to get into error block.
#RestController
#RequestMapping("/sports-actions")
public class SportController {
#RequestMapping(method = RequestMethod.GET)
public Object deleteSport(#RequestParam("id") String id) throws Error {
throw new Error("Test exception block");
}
}

Resources