Jersey - Determine "#Produces" using Context? - java

I'm new to Jersey, and want to determine the #Produces type in other contexts, so I can use it during error handling cases.
For example, I have the following method that produces json:
#Path("test-json")
#Produces(MediaType.APPLICATION_JSON)
#GET
public Object getTestJson(#Context HttpServletRequest req, #Context HttpServletResponse res) throws Exception
{
throw new RuntimeException("POST submitted without CSRF token! ");
}
Later on, in a global exception handler, I'd like to get the #Produces media type.
I've tried doing this with something like the following, but getMediaType() is returning null (note that this is simplified, but headers is not null in all of my tests, just getMediaType() is null).
public class someClass
{
#Context
HttpHeaders headers;
public Response convertExceptionToResponse(T exception)
{
MediaType mediaType = headers.getMediaType();
// At this point, I thought media type would be
// MediaType.APPLICATION_JSON
// for the above 'getTestJson' method, but it's null.
}
}
How can I do this?

JAX-RS
Inject ResourceInfo and invoke getResourceMethod() which will return Java Method. Then you can simple retrieve declared annotations. The problem here is that with this approach you need to do a lot of coding in case #Produces is not located directly on a method but somewhere in the hierarchy.
Jersey 2
Inject ExtendedUriInfo
#Context
private ExtendedUriInfo uriInfo;
and look for matched ResourceMethod (getMatchedResourceMethod()). Then simply get list of producible media types (getProducedTypes()).

Related

How to get content type of the response in JAX-RS ExceptionMapper

I have a resource:
#GET
#Path("/print-order")
#Produces("application/pdf")
public byte[] printOrder(#QueryParam("id") Long orderId) {
return ...;
}
...which can throw an error that is relevant to the user and must be displayed as a HTML page. So I implemented an ExceptionMapper but I don't know how to get the value of #Produces("application/pdf") annotation of the called resource.
#Provider
public class CustomExceptionMapper implements ExceptionMapper<CustomException> {
#Override
public Response toResponse(CustomException exception) {
if (contentType = "application/pdf")
... html respone
else
... entity response
}
}
I'm using JAX-RS 1.x (jsr311) with Jersy 1.12 implementation but would love to have implementation independent solution.
You can inject different context objects into the ExceptionMapper to get more info on the request it handles. It's convenient to determine what content type the client expects based on HTTP's Accept header (learn more here).
Below is the example on how you can make ExceptionMapper to respond with different formats based on Accept header specified (or not specified) by your APIs client.
#Provider
public class CustomExceptionMapper implements ExceptionMapper<CustomException> {
// Inject headers of the request being processed
#Context
private HttpHeaders headers;
// or even all the request details
#Context
private HttpServletRequest request;
#Override
public Response toResponse(CustomException exception) {
List<MediaType> acceptedTypes = headers.getAcceptableMediaTypes();
if (acceptedTypes.contains(MediaType.APPLICATION_JSON)) {
// respond with entity
} else {
// respond with HTML
}
}
}
You initial idea can be implemented, though. You can inject HttpServletRequest in your resource class and use setAttribute() method to store application/pdf string within the context of current request. It can be later obtained in ExceptionMapper using getAttribute() method. But I wouldn't recommend to do so unless absolutely necessary. It introduces not so obvious dependencies between components of your code.

Get original mapping value inside Spring controller method

Since I'm using the CQRS pattern, I'm trying to create a single controller method that accepts every POST call with a command in its request body and send it.
I'm almost there, but I can't get the path variables.
I created a custom HandlerMapping
#Bean
public HandlerMapping requestMappingHandlerMapping() throws NoSuchMethodException {
for (final UrlEnum urlEnumItem : UrlEnum.values()) {
requestMappingHandlerMapping.registerMapping(new RequestMappingInfo(urlEnumItem.getCommandName(),
new PatternsRequestCondition(urlEnumItem.getUrl()),
null,
null,
null,
null,
null,
null),
commandController,
commandController.getClass().getDeclaredMethod("commandHandler", HttpServletRequest.class)
);
}
return requestMappingHandlerMapping;
}
and this is my controller method signature
#RequestMapping(method = {RequestMethod.POST, RequestMethod.PUT}, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<Object> commandHandler(final HttpServletRequest request) throws Exception {
// controller code here
}
If the url path is something like /api/test it works, but with something like /api/test/{idEntity} I don't have any PathVariable available in the request.
I tried everything like
String originalUrl = (String) request.getAttribute(
HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
which returns the valued url (i.e. /api/test/1234), not the template, or adding
#PathVariable Map<String, Object> parameters
as a parameter in the method, which is empty.
Debugging the request object it seems there isn't anything useful to identify the path variables.
Maybe I should interrogate the HandlerMapping, but I can't have access to it in the controller method.
Is there a way to extract the pathVariables in the controller method?
It was an error in the configuration. I shouldn't have added the RequestMapping annotation to the controller method because it overrode my configuration.
Now I have
#RestController
public class CommandController extends AbstractController {
private final MappingJackson2JsonView mappingJackson2JsonView = new MappingJackson2JsonView();
#Override
protected ModelAndView handleRequestInternal(final HttpServletRequest request, final HttpServletResponse response) throws Exception {
// controller code here
return new ModelAndView(mappingJackson2JsonView);
}
}

REST Handling a wrong Parameter and required parameter

My requirement is to get the json response with customized error message when a required #RequestParam is not sent to the request handler or invalid parameter(required is int but user is passing string) is sent to the request handler.
currently I am trying to use the #Exceptionhandler mechanism to handle these exceptions. But the respective exception handler methods not getting invoked.
Please see the code snippet:
#Controller
#RequestMapping("api/v1/getDetails")
public class Abc {
#RequestMapping
#ResponseBody
public Envelope<Object> retrieveTransactions(#RequestParam(required = false) Integer a,
#RequestParam int b, #RequestParam(required = false) boolean c,
HttpServletRequest req) {`
//implementation goes here
}
#ExceptionHandler(MissingServletRequestParameterException.class)
#ResponseBody
public Envelope<Object> missingParameterExceptionHandler(Exception exception,
HttpServletRequest request) {
Envelope<Object> envelope = null;
//error implementation
return envelope;
}
#ExceptionHandler(TypeMismatchException.class)
#ResponseBody
public Envelope<Object> typeMismatchExpcetionHandler(Exception exception, HttpServletRequest request) {
Envelope<Object> envelope = null;
//error implementation
return envelope;
}
Do I need to configure anything extra for exception handler? can anyone tell me where I am doing the wrong.
Consider identifying the parameter name in the RequestParameter annotation.
For example
#RequestParam(value="blammy", required=false)
I've never bothered figuring out how to handle type mismatch,
instead I've found it easier to accept all parameters as String and perform all verification myself (including type).
Also,
If you are accepting the HttpServletRequest as a parameter to your handler,
then there is no need to use #RequestParam annotations,
just get the parameter values directly from the request.
Finally,
consider org.springframework.web.context.request.WebRequest
or org.springframework.web.context.request.NativeWebRequest
instead of HttpServletRequest.
Have you tried to use MethodArgumentNotValidException or HttpMessageNotReadableException instead on your handlers?
And put required = true on your #RequestParam declaration to catch missing params exceptions
#RequestParam(required = true)

Use the annotations inside the method

Is there a way to use the value of the annotation inside the same method that it has been declared ?
#GET
#Produces({MediaType.APPLICATION_XML})
#Path(CONSTANTS.PATH1)
public MyModel getInfo(
#PathParam(CONSTANTS.ID) String id,
#Context HttpServletRequest request,
#Context HttpServletResponse response) {
...
}
In the above example, is it possible to use the value of #Path(CONSTANTS.PATH1) inside the method? I can directly use the value of CONSTANTS.PATH1, but if it possible to get it from annotations itself ?

how send result back in Spring 2.x

I have a method which returns true or false based on some parameters. So I make an ajax call (using Ext.ajax.request). In spring 2.x version how do I send back the result?
So for my controller I extend BaseSimpleCommandController and override the method
ModelAndView doExecute(HttpServletRequest request, HttpServletResponse response, Object command, BindException errors)
I want to know what would i need to do to send back just a boolean value. I am little confused as what needs to be done. I now i have to send back a ModelAndView type object but nor sure i should i embed a single boolean value in to this object.
EDIT: the BaseSimpleCommandController is specific to my project which in turn extends AbstractCommandController from spring. sorry for the confusion.
If you just want to return "true" or "false" there is no need to use models, command controllers, etc. Simply inject HttpServletResponse and send the data directly:
public void handle(HttpServletResponse response) {
boolean flag = //...
response.getWriter().print(flag);
}
Request params can be mapped via annotation in the method parameter, or through the request's parameter map
Method Parameter:
#RequestMapping(method=RequestMethod.GET)
public void someCall(#RequestParam(value="param1") String paramName)
...
Where param1 would be the get parameter param1. If you don't provide a value for the annotation, it tries to bind to the name of the parameter in the method name (paramName in this case).
Parameter Map
#RequestMapping(method=RequestMethod.GET)
public void someCall(HttpServletRequest request)
{
Map<String, String[]> paramMap = request.getParameterMap();
}
Hope this helps!

Categories

Resources