Is it possible to access the Request object in a REST method under JAX-RS?
I just found out
#Context Request request;
On JAX-RS you must annotate a Request parameter with #Context:
#GET
public Response foo(#Context Request request) {
}
Optionally you can also inject:
UriInfo
HttpHeaders
SecurityContext
HttpServletRequest
To elaborate on #dfa's answer for alternatives, I find this to be simpler than specifying the variable on each resource method signature:
public class MyResource {
#Context
private HttpServletRequest httpRequest;
#GET
public Response foo() {
httpRequest.getContentType(); //or whatever else you want to do with it
}
}
Related
Depends on request body content I need to redirect http requests to URL_1 or URL_2.
I started controller implementation:
#RestController
public class RouteController {
#Autowired
private RestTemplate restTemplate;
#RequestMapping(value = "/**")
public HttpServletResponse route(HttpServletRequest request) {
String body = IOUtils.toString(request.getReader());
if(isFirstServer(body)) {
//send request to URL_1 and get response
} else {
//send request to URL_2 and get response
}
}
}
Request might be GET or POST ot PUT or PATCH etc.
Could you help me to write that code?
I've asked a somehow similar question a while ago. Plea see Server side redirect for REST call for more context.
The best way (to my current understanding) you could achieve this is by manually invoking the desired endpoints from your initial endpoint.
#RestController
public class RouteController {
#Value("${firstUrl}")
private String firstUrl;
#Value("${secondUrl}")
private String secondUrl;
#Autowired
private RestTemplate restTemplate;
#RequestMapping(value = "/**")
public void route(HttpServletRequest request) {
String body = IOUtils.toString(request.getReader());
if(isFirstServer(body)) {
restTemplate.exchange(firstUrl,
getHttpMethod(request),
getHttpEntity(request),
getResponseClass(request),
getParams(params));
} else {
restTemplate.exchange(secondUrl,
getHttpMethod(request),
getHttpEntity(request),
getResponseClass(request),
getParams(params))
}
}
}
Example implementation for getHttpMethod:
public HttpMethod getHttpMethod(HttpServletRequest request) {
return HttpMethod.valueOf(request.getMethod());
}
Similar implementations for getHttpEntity, getResponseClass and getParams. They are used for converting the data from the HttpServletRequest request to the types required by the exchange method.
There seem to be a lot of better ways of doing this for a Spring MVC app, but I guess that it does not apply to your context.
Another way you could achieve this would be defining your own REST client and adding the routing logic there.
I have currently trying to learn the basics of Java REST, using JAX-RS.
Within the UserService class (near bottom) of this example there is both an #GET AND #PUT method with the same #path annotation:
#GET
#Path("/users")
#Produces(MediaType.APPLICATION_XML)
public List<User> getUsers() {
return userDao.getAllUsers();
}
and
#PUT
#Path("/users")
#Produces(MediaType.APPLICATION_XML)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public String createUser(#FormParam("id") int id,
#FormParam("name") String name,
#FormParam("profession") String profession,
#Context HttpServletResponse servletResponse) throws IOException {
User user = new User(id, name, profession);
int result = userDao.addUser(user);
if(result == 1) {
return SUCCESS_RESULT;
}
return FAILURE_RESULT;
}
How does the Program know which method to invoke, considering that they are both point at the same #path ?
Resource classes have methods that are invoked when specific HTTP method requests are made, referred to as resource methods. In order to create Java methods that will be invoked with specific HTTP methods, a regular Java method must be implemented and annotated with one of the JAX-RS #HttpMethod annotated annotations (namely, #GET, #POST, #PUT, and #DELETE).
For more info take a look at this example1 and example2
JAX-RS evaluates the HTTP method of the request and then calls the appropriate Java method in UserService.
I have a RESTEasy web server with lot of methods. I want implement logback to track all requests and responses, but I don't want add log.info() to every methods.
Maybe there's way to catch requests and responses in one place and log it. Maybe something like a filter on HTTP request process chain on RESTEasy.
#Path("/rest")
#Produces("application/json")
public class CounterRestService {
//Don't want use log in controler every method to track requests and responces
static final Logger log = LoggerFactory.getLogger(CounterRestService.class);
#POST
#Path("/create")
public CounterResponce create(#QueryParam("name") String name) {
log.info("create "+name)
try {
CounterService.getInstance().put(name);
log.info("responce data"); // <- :((
return new CounterResponce();
} catch (Exception e){
log.info("responce error data"); // <- :((
return new CounterResponce("error", e.getMessage());
}
}
#POST
#Path("/insert")
public CounterResponce create(Counter counter) {
try {
CounterService.getInstance().put(counter);
return new CounterResponce();
} catch (Exception e){
return new CounterResponce("error", e.getMessage());
}
}
...
}
You can create filters and easily bind them to the endpoints you need to log, keeping your endpoints lean and focused on the business logic.
Defining a name binding annotation
To bind filters to your REST endpoints, JAX-RS provides the meta-annotation #NameBinding and it can be used as following:
#NameBinding
#Retention(RUNTIME)
#Target({TYPE, METHOD})
public #interface Logged { }
Logging the HTTP request
The #Logged annotation will be used to decorate a filter class, which implements ContainerRequestFilter, allowing you to handle the request:
#Logged
#Provider
public class RequestLoggingFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// Use the ContainerRequestContext to extract information from the HTTP request
// Information such as the URI, headers and HTTP entity are available
}
}
The #Provider annotation marks an implementation of an extension interface that should be discoverable by JAX-RS runtime during a provider scanning phase.
The ContainerRequestContext helps you to extract information from the HTTP request.
Here are methods from the ContainerRequestContext API to get information from the HTTP request that can be useful for your logs:
ContainerRequestContext#getMethod(): Get the HTTP method from the request.
ContainerRequestContext#getUriInfo(): Get URI information from the HTTP request.
ContainerRequestContext#getHeaders(): Get the headers from the HTTP request.
ContainerRequestContext#getMediaType(): Get the media type of the entity.
ContainerRequestContext#getEntityStream(): Get the entity input stream.
Logging the HTTP response
For logging the response, consider implementing a ContainerResponseFilter:
#Logged
#Provider
public class ResponseLoggingFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext) throws IOException {
// Use the ContainerRequestContext to extract information from the HTTP request
// Use the ContainerResponseContext to extract information from the HTTP response
}
}
The ContainerResponseContext helps you to extract information from the HTTP response.
Here are some methods from the ContainerResponseContext API to get information from the HTTP response that can be useful for your logs:
ContainerResponseContext#getStatus(): Get the status code from the HTTP response.
ContainerResponseContext#getHeaders(): Get the headers from the HTTP response.
ContainerResponseContext#getEntityStream(): Get the entity output stream.
Binding the filters to your endpoints
To bind the filter to your endpoints methods or classes, annotate them with the #Logged annotation defined above. For the methods and/or classes which are annotated, the filters will be executed:
#Path("/")
public class MyEndpoint {
#GET
#Path("{id}")
#Produces("application/json")
public Response myMethod(#PathParam("id") Long id) {
// This method is not annotated with #Logged
// The logging filters won't be executed when invoking this method
...
}
#DELETE
#Logged
#Path("{id}")
#Produces("application/json")
public Response myLoggedMethod(#PathParam("id") Long id) {
// This method is annotated with #Logged
// The request logging filter will be executed before invoking this method
// The response logging filter will be executed before invoking this method
...
}
}
In the example above, the logging filters will be executed only for myLoggedMethod(Long) because it's annotated with #Logged.
Additional information
Besides the methods available in ContainerRequestContext and ContainerResponseFilter interfaces, you can inject ResourceInfo in your filters using #Context:
#Context
ResourceInfo resourceInfo;
It can be used to get the Method and the Class which match with the requested URL:
Class<?> resourceClass = resourceInfo.getResourceClass();
Method resourceMethod = resourceInfo.getResourceMethod();
HttpServletRequest and HttpServletResponse are also available for injection:
#Context
HttpServletRequest httpServletRequest;
#Context
HttpServletResponse httpServletResponse;
Refer to this answer for the types that can be injected with #Context.
Try Interceptors(not just vanilla EJB interceptors you can use CDI with that).
They are there for implementing Cross Cutting Concerns(aspects).
For others using Jersey and trying to solve same problem, there is org.glassfish.jersey.logging.LoggingFeature that can be used both on client or server. It logs request and response to java.util.logging.Logger.
Output can be bridged to slf4j with org.slf4j.bridge.SLF4JBridgeHandler if needed.
Consider this case:-
I am injecting HttpServletRequest in a Rest Service like
#Context
HttpServletRequest request;
And use it in a method like:-
#GET
#Path("/simple")
public Response handleSimple() {
System.out.println(request.getParameter("myname"));
return Response.status(200).entity("hello.....").build();
}
This works fine but when I try to send it through POST method and replace the #GET by #POST annotation, I get the parameter value null.
Please suggest me where I am mistaking.
You do not need to get your parameters etc out of the request. The JAX-RS impl. handles that for you.
You have to use the parameter annotations to map your parameters to method parameters. Casting converting etc. is done automaticly.
Here your method using three differnt ways to map your parameter:
// As Pathparameter
#POST
#Path("/simple/{myname}")
public Response handleSimple(#PathParam("myname") String myName) {
System.out.println(myName);
return Response.status(200).entity("hello.....").build();
}
// As query parameter
#POST
#Path("/simple")
public Response handleSimple(#QueryParam("myname") String myName) {
System.out.println(myName);
return Response.status(200).entity("hello.....").build();
}
// As form parameter
#POST
#Path("/simple")
public Response handleSimple(#FormParam("myname") String myName) {
System.out.println(myName);
return Response.status(200).entity("hello.....").build();
}
Documentation about JAX-RS Annotations from Jersey you can find here:
https://jersey.java.net/documentation/latest/jaxrs-resources.html
suppose i have some jax-rs resource class:
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public class ResourceA {
#GET
public Something get(#Context UriInfo uriInfo) {
if (...) {
//how to get to ResourceB ?
}
}
}
and i want to conditionally redirect the call to some other jax-rs resource:
public class ResourceB {
#GET
#Path("{identifier}")
public Other get(#PathParam("identifier")String someArg) {
}
}
how do i do this?
note that i dont want this to be visible to the client (so no http redirects) and generally the resource methods i want to redirect to dont share the same signature (they may have path params etc as in the example i gave).
im running jersey 2.6 under apache tomcat (its a spring app, if thats any help)
EDIT - im looking for a jax-rs equivalent of servlet forward. i dont want to do an extra http hop or worry abour instantiating resource classes myself
You can get it using ResourceContext as follows:
#Context
ResourceContext resourceContext;
This will inject the ResourceContext into your Resource. You then get the resource you want using:
ResourceB b = resourceContext.getResource(ResourceB.class);
The Javadoc for ResourceContext is here. You can find a similar question here
I'm not aware of any possibility to do this from a resource method, but if it fits your use case, what you could do is implement your redirect logic in a pre matching request filter, for example like so:
#Provider
#PreMatching
public class RedirectFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) {
UriInfo uriInfo = requestContext.getUriInfo();
String prefix = "/redirect";
String path = uriInfo.getRequestUri().getPath();
if (path.startsWith(prefix)) {
String newPath = path.substring(prefix.length());
URI newRequestURI = uriInfo.getBaseUriBuilder().path(newPath).build();
requestContext.setRequestUri(newRequestURI);
}
}
}
This will redirect every request to /redirect/some/resource to /some/resource (or whatever you pass to requestContext.setRequestUri()) internally, before the resource method has been matched to the request and is executed and without http redirects or an additional internal http request.