How to get access HttpServletRequest in constructor of resource-parentclass? - java

I filter the BasicAuth of my REST-services and store the username in the HttpServletRequest
Related Code of the AuthFilter
public class AuthFilter implements ContainerRequestFilter {
#Context
public transient HttpServletRequest servletRequest;
#Override
public ContainerRequest filter(ContainerRequest containerRequest) throws WebApplicationException {
// all the auth filter actions...
// Set data
servletRequest.setAttribute("auth_username", username);
return containerRequest;
}
}
I then have a resource parent class (called BaseResource with only some general attributes) and specific classes which extends it
example specific class
#Path("/Plant")
public class PlantResource extends BaseResource {
private List<Plant> plantlist = new LinkedList<Plant>();
#GET
#Path("/GetPlantById/plantid/{plantid}")
#Produces("application/json")
public String getPlantById(#PathParam("plantid") String plantid, #Context HttpServletRequest hsr) {
String username = (String)hsr.getAttribute("auth_username");
// do something
}
}
As you can see I handle the HttpServletRequest via "#Context HttpServletRequest hsr" to the function (as described in there: Get HttpServletRequest in Jax Rs / Appfuse application?) . This works fine and I can access the data correctly!
What I want now to do is to access this Data in the constructor of the parent class, so I don't have to do it in every function of my specified resources, but in a single place
My try:
public class BaseResource {
#Context protected HttpServletRequest hsr; // Also tried private and public:
/* ... */
public BaseResource() {
String username = (String)hsr.getAttribute("auth_username"); // line 96
System.out.println("constructur of BaseResource" + username);
}
}
But this ends up in:
Aug 05, 2013 3:40:18 PM com.sun.jersey.spi.container.ContainerResponse mapMappableContainerException
Schwerwiegend: The RuntimeException could not be mapped to a response, re-throwing to the HTTP container
java.lang.NullPointerException
at de.unibonn.sdb.mobilehelper.resources.BaseResource.<init>(BaseResource.java:96)
It looks like the HttpServletRequest isn't set there. So how can I access it in the constructor of my parent class?

Fields of BaseResource are injected after an instance is created, so you can't refer to them in the constructor itself. Either create a property method in your BaseResource:
public class BaseResource {
#Context
protected HttpServletRequest hsr;
/* ... */
protected String getUsername() {
return (String)hsr.getAttribute("auth_username");
}
}
or create a hierarchy like:
public class BaseResource {
protected HttpServletRequest hsr;
/* ... */
public BaseResource(HttpServletRequest hsr) {
this.hsr = hsr;
String username = (String)hsr.getAttribute("auth_username");
System.out.println("constructur of BaseResource" + username);
}
}
and
#Path("/Plant")
public class PlantResource extends BaseResource {
private List<Plant> plantlist = new LinkedList<Plant>();
public PlantResource(#Context HttpServletRequest hsr) {
super(hsr);
}
#GET
#Path("/GetPlantById/plantid/{plantid}")
#Produces("application/json")
public String getPlantById(#PathParam("plantid") String plantid) {
String username = (String)hsr.getAttribute("auth_username");
// do something
}
}

You will have to pass it up via a function. The JAX-RS annotations like #Context aren't available in the parent, as you noted. They are also not inherited down. Additionally, you cannot do it at construction time since #Context references are not guaranteed to be available during construction (depends on how container creates the resources).

Related

Custom error message containing parameter names when validation fails

I would like my API to return errorMessage when the request lacks of required parameters. For example let's say there is a method:
#GET
#Path("/{foo}")
public Response doSth(#PathParam("foo") String foo, #NotNull #QueryParam("bar") String bar, #NotNull #QueryParam("baz") String baz)
where #NotNull is from package javax.validation.constraints.
I wrote an exception mapper which looks like this:
#Provider
public class Mapper extends ExceptionMapper<ConstraintViolationException> {
#Override
public Response toResponse(ConstraintViolationException) {
Iterator<ConstraintViolation<?>> it= exception.getConstraintViolations().iterator();
StringBuilder sb = new StringBuilder();
while(it.hasNext()) {
ConstraintViolation<?> next = it.next();
sb.append(next.getPropertyPath().toString()).append(" is null");
}
// create errorMessage entity and return it with apropriate status
}
but next.getPropertyPath().toString() returns string in format method_name.arg_no, f.e. fooBar.arg1 is null
I'd like to receive output fooBar.baz is null or simply baz is null.
My solution was to include -parameters parameter for javac but to no avail.
Probably I could somehow achieve it with the use of filters:
public class Filter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) {
UriInfo uriInfo = requestContext.getUriInfo();
UriRoutingContext routingContext = (UriRoutingContext) uriInfo;
Throwable mappedThrowable = routingContext.getMappedThrowable();
if (mappedThrowable != null) {
Method resourceMethod = routingContext.getResourceMethod();
Parameter[] parameters = resourceMethod.getParameters();
// somehow transfer these parameters to exceptionMapper (?)
}
}
}
The only problem with the above idea is that ExeptionMapper is executed first, then the filter is executed. Also I have no idea how could I possibly transfer errorMessage between ExceptionMapper and Filter. Maybe there is another way?
You can inject ResourceInfo into the exception mapper to get the resource method.
#Provider
public class Mapper extends ExceptionMapper<ConstraintViolationException> {
#Context
private ResourceInfo resourceInfo;
#Override
public Response toResponse(ConstraintViolationException ex) {
Method resourceMethod = resourceInfo.getResourceMethod();
Parameter[] parameters = resourceMethod.getParameters();
}
}

Jersey filter in Dropwizard to set some global FreeMarker variables

I'm reading https://jersey.github.io/documentation/latest/filters-and-interceptors.html and http://www.dropwizard.io/1.1.4/docs/manual/core.html#jersey-filters to try and make this:
#CookieParam("User-Data") userData: String,
#HeaderParam("User-Agent") userAgent: String,
Not needed in each and every resource GET method of my web app. userData is json data from a cookie with fields like "name" and "id" and userAgent is the full User-Agent string from the header. For each view I pass in:
AppUser.getName(userData), AppUser.isMobile(userAgent)
The getName function parses the json and returns just the name field and the isMobile function returns a true boolean if the string "mobile" is found.
I use this in each view of the app in FreeMarker to display the user's name and to change some layout stuff if mobile is true.
Is there a way to make this less repetitive? I'd rather use a BeforeFilter to just set this automatically each time.
Sounds like something you can just do in a ContainerResponseFilter, which gets called after the return of the view resource/controller. Assuming you are returning a Viewable, you get the Viewable from the ContainerRequestContext#getEntity, get the model from it, and add the extra information to the model.
#Provider
#UserInModel
public class UserInModelFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext request,
ContainerResponseContext response) throws IOException {
Cookie cookie = request.getCookies().get("User-Data");
String header = request.getHeaderString("User-Agent");
String username = AppUser.getName(cookie.getValue());
boolean isMobile = AppUser.isMobile(header);
Viewable returnViewable = (Viewable) response.getEntity();
Map<String, Object> model = (Map<String, Object>) returnViewable.getModel();
model.put("username", username);
model.put("isMobile", isMobile);
}
}
The #UserInModel annotation is a custom Name Binding annotation, which is used to determine which resource classes or methods should go through this filter. Since you don't want all endpoints to go through this filter, just annotate the methods or classes you want.
#NameBinding
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE, ElementType.METHOD})
public #interface UserInModel {
}
#Path("/")
public class IndexController {
#GET
#UserInModel
#Produces(MediaType.TEXT_HTML)
public Viewable home() {
Map<String, Object> model = new HashMap<>();
return new Viewable("/index", model);
}
}
With Dropwizard, all you need to do is register the filter.
env.jersey().register(UserInModelFilter.class);
If you want to do some preprocessing of the cookie and header before the resource method is called, you can do that in a ContainerRequestFilter, which can also be name bound. And instead of recalculating the AppUser.xxx method in the response filter, you can also just set a property on the ContainerRequestContext#setProperty that you can later retrieve from the same context (getProperty) in the response filter.
UPDATE
The above answer assumes you are using Jersey's MVC support, hence the use of Viewable. If you are using Dropwizard's view support, then it's not much different. You may want to create an abstract class as a parent for all the view classes, that way you can just cast to the abstract type when retrieving the entity from the filter.
public class AbstractView extends View {
private String userName;
private boolean isMobile;
protected AbstractView(String templateName) {
super(templateName);
}
public String getUserName() { return userName; }
public void setUserName(String userName) { this.userName = userName; }
public boolean isMobile() { return isMobile; }
public void setIsMobile(boolean mobile) { isMobile = mobile; }
}
public class PersonView extends AbstractView {
private final Person person;
public PersonView(Person person) {
super("person.ftl");
this.person = person;
}
public Person getPerson() {
return this.person;
}
}
In the filter
#Provider
#UserInModel
public class UserInModelFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext request,
ContainerResponseContext response) throws IOException {
Cookie cookie = request.getCookies().get("User-Data");
String header = request.getHeaderString("User-Agent");
String username = AppUser.getName(cookie.getValue());
boolean isMobile = AppUser.isMobile(header);
AbstractView returnViewable = (AbstractView) response.getEntity();
returnViewable.setUserName(username);
returnViewable.setIsMobile(isMobile);
}
}
Tested resource class for completeness
#Path("person")
public class PersonController {
#GET
#UserInModel
#Produces(MediaType.TEXT_HTML)
public PersonView person() {
Person person = new Person("peeskillet#fake.com");
return new PersonView(person);
}
}

jetty: store request-specific information

I am creating some sort of RESTful API with basic auth. To handle the auth information I added a custom ContainerRequestFilter. This works quite good, but I want to set global information like the "username" of the caller. How can I set global/request-specific information or properties and get them within a "Controller" method?
//some filter
public class AuthFilter implements ContainerRequestFilter{
//...
#Override
public void filter( ContainerRequestContext requestContext ) throws IOException {
requestContext.setProperty("username", "someusername");
}
//...
}
//example "route-handler"
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Event> getEvents() {
//HOW to get the username property?!
}
You can inject HttpServletRequest into your controller and use HttpServletRequest.getAttribute to retrieve the values you set in ContainerRequestContext.setProperty.
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Event> getEvents(#Context HttpServletRequest req) {
String username = (String) req.getAttribute("username");
...
}
I've used that on Glassfish/Jersey and it works fine so it should work in your environment.

add custom parameters to spring mvc controller

I have a custom dispatcher servlet which extends the default DispatcherServlet to do some validation for all requests.
Since I will get all the parameters from a request(getInputStream()->Map) to do some validation, I want to pass the params to controller or add the params to the context where I can get them again from the cotroller.
Now I just put all the params to a global Map, but I wonder if there are some simple ways.
public class CustomDispatcherServlet extends DispatcherServlet {
private static final long serialVersionUID = 7250693017796274410L;
#Override
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
doFilter(request, response);
super.doDispatch(request, response);
}
...
private void doFilter(HttpServletRequest request, HttpServletResponse response) {
WLNResponse<String> error = null;
try {
boolean isSignValid = checkSignValidity(request);
...
private boolean checkSignValidity(HttpServletRequest request) throws IOException {
// pass this params to controller or somewhere I can get from controller
Map<String, Object> params = WebUtils.readParams(request);
...
The way I would go at validating params in the controller itself. for instance
#Controller
public ControllerClass
{
#RequestMapping(value = "/someurl", method = RequestMethod.GET, params = {
"requestParam"})
public void someMethod(#RequestParam(value = "requestParam") String requestParam)
{
System.out.println("This is the value of the RequestParam requestParam " + requestParam);
}
}
This way you can do your validation within the controller.
The only thing this doesn't solve for is if the request being made is not resolved to a valid controller. For that I would use the annotation #controllerAdvice.
Currently, I simply use the request.setAttribute() to put params to the attributes and get it from the controller.....

java.lang.NoSuchMethodException with Constructor newInstance

I am currently working on some web dev project in Java, i have implemented a frontcontroller, which job is to instantiate new controllers, depending on the path.
So when the user is running ?q=user/login ex. the front controller should instatiate the UserController, that i am trying to do with this piece of code.
String q = request.getParameter("q");
try {
String[] page = q.split("/");
// Make first char upper, to match class name conventions.
page[0] = (page[0].substring(0, 1).toUpperCase() + page[0].substring(1).toLowerCase()).trim();
Class contDes = Class.forName("dk.elvar.rocks." + page[0]+ "Controller");
Constructor co = contDes.getConstructor();
co.newInstance(request, response, page);
This results in a
java.lang.NoSuchMethodException: dk.elvar.rocks.UserController.<init>()
at java.lang.Class.getConstructor0(Class.java:2706)
at java.lang.Class.getConstructor(Class.java:1657)
at dk.elvar.rocks.FrontController.doGet(FrontController.java:35)
I've tryed to look it up at google, and bugs as, declaring a constructor in loaded object, make the class public, is already there.
UserController:
public class UserController extends HttpServlet {
private final String USERNAME = "Martin";
private final String PASSWORD = "David";
private static final long serialVersionUID = 1L;
HttpServletRequest request;
HttpServletResponse response;
public UserController(HttpServletRequest request, HttpServletResponse response, String[] action) {
this.request = request;
this.response = response;
if(action[1].equalsIgnoreCase("login")) {
this.renderLoginAction();
}
if(action[1].equalsIgnoreCase("val-login")) {
this.validateLoginAction();
}
}
You probably get this exception because that class does not have a default constructor. You can get a constructor with parameters by passing them to the getConstructor method:
Constructor co = contDes.getConstructor(
HttpServletRequest.class,
HttpServletResponse.class,
String[].class);
co.newInstance(request, response, page);
Generally its not recommended to use other then default constructor, as web.xml or struts-config.xml uses to instantiate the servlet using reflection.
If you want to get instance of your class other than default constructor,
Class userController = Class.forName("<packages.>UserController");
Constructor userControllerConstructor = userController.class.getConstructor(HttpServletRequest.class, HttpServletResponse.class, String[].class);
UserController userControllerObj = (UserController)constructor.newInstance(request,response, action);

Categories

Resources