I have a spring-boot application without any controller classes.
How can I write exception handlers for this application. Exception handler classes annotated with #ControllerAdvice doesn't work.
If you are developing web applications, ErrroController is available.
#Controller
#RequestMapping("${server.error.path:${error.path:/error}}")
public class MyErrorController implements ErrorController {
private final ErrorAttributes errorAttributes;
public MyErrorController(final ErrorAttributes errorAttributes) {
this.errorAttributes = errorAttributes;
}
#Override
public String getErrorPath() {
return null;
}
#RequestMapping
public ResponseEntity<Map<String, Object>> error(final HttpServletRequest request) {
final WebRequest webRequest = new ServletWebRequest(request);
final Throwable th = errorAttributes.getError(webRequest);
// ...
// see also: BasicErrorController implementation
}
}
Related
I am new to SOAP, trying to run a sample SOAP client using Spring Boot
How the SOAP fault, Exceptions or Errors are handled while using WebServiceTemplate
public class CountryClient extends WebServiceGatewaySupport {
private static final Logger log = LoggerFactory.getLogger(CountryClient.class);
public GetCountryResponse getCountry(String country) {
GetCountryRequest request = new GetCountryRequest();
request.setName(country);
log.info("Requesting location for " + country);
GetCountryResponse response = (GetCountryResponse) getWebServiceTemplate()
.marshalSendAndReceive("http://localhost:8080/ws/countries", request,
new SoapActionCallback(
"http://spring.io/guides/gs-producing-web-service/GetCountryRequest"));
return response;
}
}
One way is writing your custom interceptor which implements Spring WS's ClientInterceptor interface. You should override handleFault method to handle SOAP faults with your custom logic.
public class MySoapClientInterceptor implements ClientInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(MySoapClientInterceptor.class);
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
return true;
}
#Override
public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
return true;
}
#Override
public boolean handleFault(MessageContext messageContext) throws WebServiceClientException {
LOGGER.info("intercepted a fault...");
SoapBody soapBody = getSoapBody(messageContext);
SoapFault soapFault = soapBody.getFault();
LOGGER.error(soapFault.getFaultStringOrReason());
throw new RuntimeException(String.format("Error occured while invoking SOAP service - %s ", soapFault.getFaultStringOrReason()));
}
#Override
public void afterCompletion(MessageContext messageContext, Exception ex) throws WebServiceClientException {
}
private SoapBody getSoapBody(MessageContext messageContext) {
SoapMessage soapMessage = (SoapMessage) messageContext.getResponse();
SoapEnvelope soapEnvelope = soapMessage.getEnvelope();
return soapEnvelope.getBody();
}
}
Then you need to register your custom Interceptor class as an interceptor at your SOAP client config class. At the bean definition of CountryClient at your Configuration class in your case.
#Configuration
public class SoapClientConfig {
#Value("${soap.server.url}")
public String soap_server_url;
#Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("com.example");
return marshaller;
}
#Bean
public SoapConnector soapConnector(Jaxb2Marshaller marshaller) {
SoapConnector client = new SoapConnector();
client.setDefaultUri(soap_server_url);
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
ClientInterceptor[] clientInterceptors = {new MySoapClientInterceptor()};
client.setInterceptors(clientInterceptors);
return client;
}
}
In Spring Webflux what is the prefered way of Exception Handling?
#RestControllerAdvice comes from Spring MVC whereas DefaultErrorAttributes comes from Spring Webflux.
However, in Spring Webflux someone could use #RestControllerAdvice. What would be the advantages/disadvantages?
#RestControllerAdvice
#RestControllerAdvice
public class ControllerAdvice
{
#ExceptionHandler(Throwable.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public Mono<Map<String, Object>> exceptions(Throwable e)
{
return Mono.just(Map.of("message", "bad"));
}
}
Extend DefaultErrorAttributes
#Component
public class ErrorAttributes extends DefaultErrorAttributes
{
#Override
public Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace)
{
var ex = getError(request);
var attributes = new LinkedHashMap<String, Object>();
attributes.put("status", HttpStatus.BAD_REQUEST.value());
attributes.put("message", "bad");
return attributes;
}
}
I want to stay in the reactive world, so I tend more towards DefaultErrorAttributes (which plays well with DefaultErrorWebExceptionHandler in Webflux). However, in #RestControllerAdvice I could also use Mono.just(...).
It is same. Like WebMvc.
#RestControllerAdvice
public class ControllerAdvice {
#ExceptionHandler(AnyException.class)
public Mono<EntityResponse<YourModel>> example(AnyException exception) {
return EntityResponse.fromObject(new YourModel()).status(HttpStatus.NOT_FOUND).build();
}
}
In Spring Webflux in case functional routes declaration, you can also implement your own ExceptionHandler instead of DefaultErrorWebExceptionHandler:
class SystemErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {
#Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable throwable) {
return super.handle(exchange, throwable)
// debug, process
.contextWrite(...);
}
#Override
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
// for all routs
return route(all(), this::renderErrorResponse);
}
private Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
Map<String, Object> error = getErrorAttributes(request, ErrorAttributeOptions.of());
Throwable t = this.getError(request);
// map exception on response
return ServerResponse.status(status).body(...);
}
}
Then use your implementation of AbstractErrorWebExceptionHandler in the spring configuration with #AutoConfigureBefore(WebFluxAutoConfiguration.class)
In my REST service app, I am planning to create a #ControllerAdvice class to catch controller thrown exceptions and return ResponseEntity objects according to the error type.
But I already have a #RestController class implementing the ErrorController interface to catch all exceptions.
Do these two interfere in any manner?
In which cases will ErrorController be called when #ControllerAdvice exists?
Edit:
The ErrorController code as requested
#RestController
public class ControllerCustomError implements ErrorController{
//error json object
public class ErrorJson {
public Integer status;
public String error;
public String message;
public String timeStamp;
public String trace;
public ErrorJson(int status, Map<String, Object> errorAttributes) {
this.status = status;
this.error = (String) errorAttributes.get("error");
this.message = (String) errorAttributes.get("message");
this.timeStamp = errorAttributes.get("timestamp").toString();
this.trace = (String) errorAttributes.get("trace");
}
}
private static final String PATH = "/error";
#Value("${hybus.error.stacktrace.include}")
private boolean includeStackTrace = false;
#Autowired
private ErrorAttributes errorAttributes;
#RequestMapping(value = PATH)
ErrorJson error(HttpServletRequest request, HttpServletResponse response) {
// Appropriate HTTP response code (e.g. 404 or 500) is automatically set by Spring.
// Here we just define response body.
return new ErrorJson(response.getStatus(), getErrorAttributes(request, includeStackTrace));
}
#Override
public String getErrorPath() {
return PATH;
}
private Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) {
RequestAttributes requestAttributes = new ServletRequestAttributes(request);
return errorAttributes.getErrorAttributes(requestAttributes, includeStackTrace);
}
}
An implementation of the ErrorController is used to provide a custom whitelabel error page.
A class annotated with #ControllerAdvise is used to add a global exception handling logic for the whole application. Thus, more than one controller in your application.
If in your application there is no mapping found for a request or page then spring will fallback to the 'whitelabel error page'. And in this case it will be the custom implementation of ErrorController
I would like to create own class that will transform HTTP request and initializes object from this HTTP request in my Spring MVC application. I can create object by defining parameters in method but I need to do mapping in my own way and do it manually.
How can I do it with my own implementation that will pass to Spring and it will use it seamlessly?
Update1
Solution that kindly provided Bohuslav Burghardt doesn't work:
HTTP Status 500 - Request processing failed; nested exception is
java.lang.IllegalStateException: An Errors/BindingResult argument is
expected to be declared immediately after the model attribute, the
#RequestBody or the #RequestPart arguments to which they apply: public
java.lang.String
cz.deriva.derivis.api.oauth2.provider.controllers.OAuthController.authorize(api.oauth2.provider.domain.AuthorizationRequest,org.springframework.ui.Model,org.springframework.validation.BindingResult,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
Maybe I should mention that I use own validator:
public class RequestValidator {
public boolean supports(Class clazz) {
return AuthorizationRequest.class.equals(clazz);
}
public void validate(Object obj, Errors e) {
AuthorizationRequest request = (AuthorizationRequest) obj;
if ("foobar".equals(request.getClientId())) {
e.reject("clientId", "nomatch");
}
}
}
and declaration of my method in controller (please not there is needed a validation - #Valid):
#RequestMapping(value = "/authorize", method = {RequestMethod.GET, RequestMethod.POST})
public String authorize(
#Valid AuthorizationRequest authorizationRequest,
BindingResult result
) {
}
I have two configurations classes in my application.
#Configuration
#EnableAutoConfiguration
#EnableWebMvc
#PropertySource("classpath:/jdbc.properties")
public class ApplicationConfig {
}
and
#Configuration
#EnableWebMvc
public class WebappConfig extends WebMvcConfigurerAdapter {
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new AuthorizationRequestArgumentResolver());
}
}
What is wrong?
Update 2
The problem is with param BindingResult result, when I remove it it works. But I need the result to process it when some errors occur.
If I understand your requirements correctly, you could implement custom HandlerMethodArgumentResolver for that purpose. See example below for implementation details:
Model object
public class AuthorizationRequestHolder {
#Valid
private AuthorizationRequest authorizationRequest;
private BindingResult bindingResult;
// Constructors, accessors omitted
}
Resolver
public class AuthorizationRequestMethodArgumentResolver implements HandlerMethodArgumentResolver {
#Override
public boolean supportsParameter(MethodParameter parameter) {
return AuthorizationRequestHolder.class.isAssignableFrom(parameter.getParameterType());
}
#Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
// Map the authorization request
AuthorizationRequest authRequest = mapFromServletRequest(request);
AuthorizationRequestHolder authRequestHolder = new AuthorizationRequestHolder(authRequest);
// Validate the request
if (parameter.hasParameterAnnotation(Valid.class)) {
WebDataBinder binder = binderFactory.createBinder(webRequest, authRequestHolder, parameter.getParameterName());
binder.validate();
authRequestHolder.setBindingResult(binder.getBindingResult());
}
return authRequestHolder;
}
}
Configuration
#Configuration
#EnableWebMvc
public class WebappConfig extends WebMvcConfigurerAdapter {
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new AuthorizationRequestMethodArgumentResolver());
}
}
Usage
#RequestMapping("/auth")
public void doSomething(#Valid AuthRequestHolder authRequestHolder) {
if (authRequestHolder.getBindingResult().hasErrors()) {
// Process errors
}
AuthorizationRequest authRequest = authRequestHolder.getAuthRequest();
// Do something with the authorization request
}
Edit: Updated answer with workaround to non-supported usage of #Valid with HandlerMethodArgumentResolver parameters.
I am using GWT (2.5) with RPC, Spring and Postgresql for my project. My issue is about HttpSession handling .
All queries to server are dispatched by Spring (DispatchServlet) to my GwtController (extends RemoteServiceServlet) .
The particular RemoteService is injected in the GwtController . It is easy to get the HttpSession inside the GwtContorller.
In example by getThreadLocalRequest().getSession() or just from request.getSession().
My question is how to get HttpSession object inside the RemoteService ?
public class GwtRpcController extends RemoteServiceServlet {
……………
private RemoteService remoteService;
private Class remoteServiceClass;
………………
public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception {
…………
}
public String processCall(String payload) throws SerializationException {
…………
}
public void setRemoteService(RemoteService remoteService) {
…………….
}
}
My Interface - DataService which implements RemoteService
public class DataServiceImpl implements DataService {
public Data getData(){
!!!!! Here I want to get HttpSession !!!!!
…………………………
}
}
You can maintain a ThreadLocal in your Servlet and store there your current Request, then expose your Request with a static method.
public class GwtRpcController extends RemoteServiceServlet {
static ThreadLocal<HttpServletRequest> perThreadRequest =
new ThreadLocal<HttpServletRequest>();
#Override
public String processCall(String payload) throws SerializationException {
try {
perThreadRequest.set(getThreadLocalRequest());
return super.processCall(payload);
} finally {
perThreadRequest.set(null);
}
}
public static HttpServletRequest getRequest() {
return perThreadRequest.get();
}
}
public class DataServiceImpl implements DataService {
public Data getData(){
HttpServletRequest request = GwtRpcController.getRequest();
HttpSession session = request.getSession();
}
}