How do I internationalize API errors in Spring Boot? - java

I was able to internationalize a controller web page request with
#RequestMapping(value="/image")
public String image(#RequestHeader(name=HEADER_CONSTANTS.ACCEPT_LANGUAGE, required=false) String al) {
In the parameter list. However I tried to put it in a ResponseEntityExceptionHandler and it didn't work.
#ControllerAdvice(basePackages="com.example.apis")
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(ApiException.class)
protected ResponseEntity<Object> handleApiException(ApiException ex, #RequestHeader(name=HEADER_CONSTANTS.ACCEPT_LANGUAGE, required=false) String al) {
2019-05-13 21:59:59.193 WARN 16052 --- [nio-8080-exec-1] .m.m.a.ExceptionHandlerExceptionResolver : Failed to invoke #ExceptionHandler method: protected org.springframework.http.ResponseEntity com.example.apis.RestExceptionHandler.handleApiException(com.example.apis.ApiException,java.lang.String)
java.lang.IllegalStateException: Could not resolve method parameter at index 1 in protected org.springframework.http.ResponseEntity com.example.apis.RestExceptionHandler.handleApiException(com.example.apis.ApiException,java.lang.String): No suitable resolver for argument 1 of type 'java.lang.String'
It works without the #RequestHeader parameter. How can I get the client's Accept-Language HTTP header in the exception handler for JSON API requests?
Spring Boot 1.5.20

If what you need is the locale of the current user, you should register a LocaleResolver and then get the locale from LocaleContextHolder.
#Bean
public LocaleResolver localeResolver() {
// Registering accept-header Locale Resolver with default Locale.US
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
localeResolver.setDefaultLocale(Locale.US);
return localeResolver;
}
Then you can get the locale in your exception handler by calling LocaleContextHolder.getLocale().

Documentation for #ExceptionHandler
It is invalid syntax, look at the rules of method signatures. You can use WebRequest to get the Header
#ExceptionHandler(ApiException.class)
protected ResponseEntity<Object> handleApiException(ApiException ex, WebRequest request) {
request.getHeader(HttpHeaders.ACCEPT_LANGUAGE);
}
Annotation for handling exceptions in specific handler classes and/or handler methods.
Handler methods which are annotated with this annotation are allowed to have very flexible signatures. They may have parameters of the following types, in arbitrary order:
An exception argument: declared as a general Exception or as a more specific exception. This also serves as a mapping hint if the annotation itself does not narrow the exception types through its value().
Request and/or response objects (typically from the Servlet API). You may choose any specific request/response type, e.g. ServletRequest / HttpServletRequest.
Session object: typically HttpSession. An argument of this type will enforce the presence of a corresponding session. As a consequence, such an argument will never be null. Note that session access may not be thread-safe, in particular in a Servlet environment: Consider switching the "synchronizeOnSession" flag to "true" if multiple requests are allowed to access a session concurrently.
WebRequest or NativeWebRequest. Allows for generic request parameter access as well as request/session attribute access, without ties to the native Servlet API.
Locale for the current request locale (determined by the most specific locale resolver available, i.e. the configured LocaleResolver in a Servlet environment).
InputStream / Reader for access to the request's content. This will be the raw InputStream/Reader as exposed by the Servlet API.
OutputStream / Writer for generating the response's content. This will be the raw OutputStream/Writer as exposed by the Servlet API.
Model as an alternative to returning a model map from the handler method. Note that the provided model is not pre-populated with regular model attributes and therefore always empty, as a convenience for preparing the model for an exception-specific view

I used the following based on Deadpool's answer. I got exceptions (org.springframework.context.NoSuchMessageException) if the language bundle did not contain the translation for a given message key. I wish it was possible to default to English if the .properties file does not contain the translation, in one line.
#ExceptionHandler(ApiException.class)
protected ResponseEntity<Object> handleApiException(ApiException ex, Locale locale) {
String defaultMessage = messages.getMessage(ex.errorMessage, ex.errorArgument, Locale.ENGLISH);
String localErrorMessage = messages.getMessage(ex.errorMessage, ex.errorArgument, defaultMessage, locale);

Related

Get request mapping object in spring interceptor, to get actual url string pattern

It might be hard to explain why, but I have this situation where I need to get the request url mapping string of currently requested url.
Like if I have a GET URL as "/Test/x/{number}"
I want to get "/Test/x/{number}" not "/Test/x/1"
can I get the actual declared url string in interceptor?
If this is possible how can I achieve this
You can implement a HanderInterceptor to intercept, pre or post, request and introspect the method being called.
public class LoggingMethodInterceptor implements HandlerInterceptor {
Logger log = LoggerFactory.getLogger(LoggingMethodInterceptor.class);
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod method = (HandlerMethod) handler;
GetMapping mapping = method.getMethodAnnotation(GetMapping.class);
log.info("URL is {}", Arrays.toString(mapping.value()));
return true;
}
}
This will output, URL is [/hello/{placeholder}]
Full example can be found here, https://github.com/Flaw101/spring-method-interceptor
You could add more logic to introspect only certain methods, certain types of requests etc. etc.
I think that you can get it with reflection and getting #RequestMapping anotations.
for example when you use
#RequestMapping(value = "/Test/x/{number}", method = RequestMethod.GET)
the value is what you are looking for if I got it right!
You only must find the controller class type.
Its possible I think but I didn't test it.
Check this:
In a Spring-mvc interceptor, how can I access to the handler controller method?
First it may be solved if the HandlerMethod was right but if you get cast error then you must get the controller class [I think].
When you get the controller class then you can looking for the method with according #RequestMapping annotation.
So
1- Find the controller class type
2- Search all methods with in the class by reflection
3- Check method annotations with specified url and specified method [GET / POST]
4- select the best candidate
If you have more than two URL parameter this method is not good!

Understanding REST APIs - What are Context and #Context?

I recently went through restful web services tutorial, but couldn't understand what a context is. Can someone explain what it it and also what #Context does?
JAX-RS provides the #Context annotation to inject 12 object instances related to the context of the HTTP request and they are:
SecurityContext - Security context instance for the current HTTP request
Request - Used for setting precondition request processing
Application, Configuration, and Providers -> Provide access to the JAX-RS application, configuration, and providers instances
ResourceContext - Resource contect aclass instances
ServletConfig - The ServletConfig instance instance
ServletContext - The ServletContext instance
HttpServletRequest - The HttpServletRequest instance for the current request
HttpServletResponse - The HttpServletResponse instance for the current request
HttpHeaders - Maintains the HTTP header keys and values
UriInfo - Query parameters and path variables from the URI called
It is a little confusing to have both an #Inject (or #Autowired in Spring) and #Context that does the same job, but it is hoped to bring more alignment to Java EE in the next edition. In the meantime, you will have to make do.
An interesting feature is that all of these instances can be injected as a field value or directly into the resource method.
An example of injection into the resource method parameter list:
#Path("/")
public class EndpointResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getAllHttpHeaders(final #Context HttpHeaders httpHeaders){
// Code here that uses httpHeaders
}
}
An example of injection into a field:
#Path("/")
public class EndpointResource {
private final #Context HttpHeaders httpHeaders;
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getAllHttpHeaders(){
// Code here that uses httpHeaders
}
}
If you want to know more, take a look at this series of articles answering the question What is #Conext in JAX-RS used for?
For an explanation about context in programming terms, have a look at this answer.
The JAX-RS API provides a #Context annotation. In general, such annotation can be used to obtain contextual Java types related to the request or response. Those types can be injected into classes managed by the JAX-RS runtime.
For example, to inject the HttpServletRequest in your resource method, you can do the following:
#GET
public Resonse foo(#Context HttpServletRequest request) {
...
}
Additional resources:
Types that can be injected with #Context
Jersey documentation about resources
context is a react Hook feature that helps to pass the data from one component to another without calling the props at each level ... it avoids prop drilling. by defining the provider in one context component and then you can call everywhere and every time when you need.
REST is an architectural style and one of the way to implement web-services. (Other is SOAP). There are many implementations of REST architecture and one of them in java is Jersey (https://jersey.java.net/)
#context is annotation in Jersey framework. It's a class from jax rs jar. (https://jersey.java.net/apidocs-javax.jax-rs/2.0.1/javax/ws/rs/core/Context.html)

How does Spring call these #RequestMappings

The source code for Spring OAuth2's AuthorizationEndpoint contains two redundant #RequestMapping annotations for the same /oauth/authorize endpoint. One of them specifies the POST method, while the other does not specify a method.
How are the two #RequestMapping annotations interpreted? Does the one that specifies POST exclusively handle all POST /oauth/authorize requests, and does the one that does not specify a method exclusively handle any non-POST requests to /oauth/authorize? Or do both methods overlap, with both methods being called for certain requests?
This is probably a Spring MVC question, though the source code on GitHub uses Spring MVC to define what is Spring OAuth2.
Though the complete source code is available on GitHub at the link that the top of this OP, the headers for the two relevant methods are summarized here as follows:
#RequestMapping(value = "/oauth/authorize")
public ModelAndView authorize(Map<String, Object> model, #RequestParam Map<String, String> parameters,
SessionStatus sessionStatus, Principal principal) {
//other stuff
}
#RequestMapping(value = "/oauth/authorize", method = RequestMethod.POST, params = OAuth2Utils.USER_OAUTH_APPROVAL)
public View approveOrDeny(#RequestParam Map<String, String> approvalParameters, Map<String, ?> model,
SessionStatus sessionStatus, Principal principal) {
//other stuff
}
This is already explained in the official documentation: if you provide the values for the method field, they'll be used to narrow down the mapping. In other words: Spring MVC will use these hints to find the most precise match for each request.
It's also easy to build a simple proof-of-concept application that demonstrates it in practice:
#RequestMapping("/foo")
#ResponseBody
public String hello() {
return "hello, default";
}
#RequestMapping(value="/foo", method = RequestMethod.GET)
#ResponseBody
public String helloGet() {
return "hello, GET";
}
Hitting /foo with a GET request, for instance using Postman, will return "hello, GET". All other supported HTTP methods (POST, PUT, DELETE, etc.) will result in "hello, default".
The default method used by Spring request mapping is GET, so if you specify a request mapping with only #RequestMapping annotation, Spring will route all GET requests for the value of the annotation to this method.
To use any other method you basically need to say the method in the annotation. like #RequestMapping(method = RequestMethod.POST)
So for your example the first method will only handle the GET requests, while the other will handle the POST requests exclusively.
Usually GET in OAuth is used for normal interpretations, while the POST is used to authenticate un-authenticated users using the param passed to the method, which in this case is OAuth2Utils.USER_OAUTH_APPROVAL.
How are the two #RequestMapping annotations interpreted?
First of, from http://javatechig.com/java/spring/how-spring-controller-request-mapping-works-in-spring-mvc the default is interpreted as a GET. This is the first distinction. Second the paramaters of both methods are slightly different where method 1 requests a Map<String, String> and the other method Map<String, ?>. So even if both methods were GET, it would still make the distinction on parameter level.
Does the one that specifies POST exclusively handle all POST
/oauth/authorize requests, and does the one that does not specify a
method exclusively handle any non-POST requests to /oauth/authorize?
Or do both methods overlap, with both methods being called for certain
requests?
The POST exclusively handles post and nothing else. The other method only handles GET requests. They never overlap. As is java's law and Spring is still bound by the rules of the java overlords =)

Adding parameters to #ExceptionHandler for MethodArgumentNotValidException in Spring

I have a Spring controller that validates incoming requests with hibernate validator.
When the request is invalid, MethodArgumentNotValidException is thrown by the validator. Would it be possible to add additional class as an argument to handler method for the exception?
This is what i have:
#RequestMapping(value = "/...", method = RequestMethod.POST)
#ResponseBody
public Response handleCustomObject(#Valid #RequestBody CustomObject obj) {
//..
}
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseBody
public Response handleInvalidRequest(MethodArgumentNotValidException e) {
return getMissingMandatoryParametersResponse(e);
}
}
And i would need something like example bellow, however this doesn't work:
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseBody
public Response handleInvalidRequest(MethodArgumentNotValidException e, CustomObject obj) {
// do something with CustomObject
}
If you want to do something with the object which failed the validation in the exception handler, you can retrieve it from BindingResult like so:
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseBody
public Response handleInvalidRequest(MethodArgumentNotValidException e) {
CustomObject ce = (CustomObject) e.getBindingResult().getTarget();
// do something with CustomObject
}
You can also take a look at Spring JavaDoc for #ExceptionHandler annotation to see the list of supported exception handler method argument types:
Handler methods which are annotated with this annotation are allowed
to have very flexible signatures. They may have arguments of the
following types, in arbitrary order:
An exception argument: declared as a general Exception or as a more specific exception. This also serves as a mapping hint if the
annotation itself does not narrow the exception types through its
value().
Request and/or response objects (Servlet API or Portlet API). You may choose any specific request/response type, e.g. ServletRequest /
HttpServletRequest or PortletRequest / ActionRequest / RenderRequest.
Note that in the Portlet case, an explicitly declared action/render
argument is also used for mapping specific request types onto a
handler method (in case of no other information given that
differentiates between action and render requests).
Session object (Servlet API or Portlet API): either HttpSession or PortletSession. An argument of this type will enforce the presence of
a corresponding session. As a consequence, such an argument will never
be null. Note that session access may not be thread-safe, in
particular in a Servlet environment: Consider switching the
"synchronizeOnSession" flag to "true" if multiple requests are allowed
to access a session concurrently.
WebRequest or NativeWebRequest. Allows for generic request parameter access as well as request/session attribute access, without
ties to the native Servlet/Portlet API.
Locale for the current request locale (determined by the most specific locale resolver available, i.e. the configured LocaleResolver
in a Servlet environment and the portal locale in a Portlet
environment).
InputStream / Reader for access to the request's content. This will be the raw InputStream/Reader as exposed by the Servlet/Portlet
API.
OutputStream / Writer for generating the response's content. This will be the raw OutputStream/Writer as exposed by the Servlet/Portlet
API.

HttpSession as a parameter in the controller

I am trying to understand how to sending HttpSession as a parameter in the spring controller works.
I have a jsp which does a post request on clicking the submit button. In the controller, reading the sessions as follows
In the controller:
public ModelAndView viewEditFundClass(HttpServletRequest request,HttpServletResponse response,Model model){
HttpSession session = (HttpSession)request.getSession();
java.util.Date startDate = sesseion.getAttribute("startDate");
However, when I just change the controller to the following, I am still able to access the session
public ModelAndView viewEditFundClass(HttpServletRequest request,HttpServletResponse response, HttpSession session,Model model)
I would like to know how this is done in Spring, ie how did the post request pass the HttpSession as a parameter? will this session be valid?
Assuming you're using Spring 3+ #Controller and #RequestMapping handler methods, Spring defines a default set of supported argument types for your handlers
Session object (Servlet API): of type HttpSession. An argument of
this type enforces the presence of a corresponding session. As a
consequence, such an argument is never null.
Spring uses the strategy pattern to accomplish this, using the interface HandlerMethodArgumentResolver. It checks the parameter types of your handler methods and, for each type, tries to find a HandlerMethodArgumentResolver that will be able to resolve an argument for it.
For HttpSession, that implementation is ServletRequestMethodArgumentResolver.

Categories

Resources