Access the HttpRequest with Restfulie? - java

This seems like a straightforward question, but I can't find it in the Restfulie documentation nor is Google coming up with an example.
I've got a Resource defined, the method is getting invoked, but I need to get to the query parameters on the URL that was used, which presumably means getting to the HttpRequest. Anyone know how you do that with Restfulie?
#Resource
public class Subscribers
{
private final Result result;
public Subscribers(Result result ){
this.result = result;
}
#Get
#Path("/subscribers")
public void get() {
// Need to get at the query parameters here...
result.use( json() ).from( "You got me" ).serialize();
}
}

Try this way
#Get
#Path("/subscribers")
public void get(#QueryParam("name") String name) {
}
your have to append the keys and values to the request URL. also you need to encode the values.
http://mydomain/subscribers?name=abcde

Related

How do I send an object to a GET endpoint that takes in a Java object (Angular)?

There is an object like this (more than one field, but just one for this ex)
public class MyObject{
private String objString;
public String getObjString(){
return objString;
}
public void setObjString(String str){
this.objString = str;
}
}
and a wrapper for it to take multiple
public class MyObjects{
private List<MyObject> objects;
... getters and setters for list...
}
and an endpoint like this
#GetMapping("/sample")
public ResponseEntity<String> sampleEndpoint(MyObjects objs){...}
What should I be doing to send it properly? I have tried making a mock model of it and sending it over as a request param but that is not working. I am able to hit the endpoint in the browser if I do a url like
'/sample?objs%5B0%5D.objString=test&objs%5B1%5D.objString=test1'
but am not sure what the best method to do this in angular is.

Jersey Conditional Pathing

I have a scenario where we support 2 different types of authenticated users (UserTypeA, UserTypeB), but they will never be used in the same server environment. Right now, we use 2 different url paths /path/usertypea/list vs /path/usertypeb/list. We would like to make them use the same path if possible, for example /path/list, and have an environment variable be the condition to know where to route the traffic. The parameters for each user type aren't exactly the same, there are some differences in how the data is organized. We're using Jersey.
I've tried a few things like Singleton classes: https://eclipse-ee4j.github.io/jersey.github.io/documentation/latest/user-guide.html#d0e2650 / https://stackoverflow.com/a/33585724/12183373 but it never routes the value, it just returns the name of the class instead of the JSON payload I'm expecting.
Here's some of the code:
#Path("/list")
public class GlobalSegmentServiceRouter {
#GET
#Produces("application/json")
public Class<?> findAll() {
boolean isUserTypeA = false;
if (isUserTypeA) {
return UserTypeAService.class;
} else {
return UserTypeBService.class;
}
}
}
Then I have 2 separate class files for the following:
#Singleton
public class UserTypeAService {
public List<String> findAll(/*Parameters for A*/) {
// Do work here for User Type A
}
}
#Singleton
public class UserTypeBService {
public List<String> findAll(/*Parameters for B*/) {
// Do work here for User Type B
}
}
When I try and hit the endpoint, this is the response I get:
"com.test.services.UserTypeAService"
Any suggestions on how to accomplish this?
add some flag for checking which kind of user is logged in to a custom principal impl. Then you can inject the current user and then call UserTypeAService.findAll or UserTypeBService.findAll in your method.
#GET
#Path("/path/list")
public String yourMethod(#Context SecurityContext securityContext)

How to fix Mass Assignment: Insecure Binder Configuration (API Abuse, Structural) in java

I have a Controller class with the below two methods for finding a doctors (context changed). Getting the
Mass Assignment: Insecure Binder Configuration (API Abuse, Structural) error on both methods.
#Controller
#RequestMapping(value = "/findDocSearch")
public class Controller {
#Autowired
private IFindDocService findDocService;
#RequestMapping(value = "/byName", method = RequestMethod.GET)
#ResponseBody
public List<FindDocDTO> findDocByName(FindDocBean bean) {
return findDocService.retrieveDocByName(bean.getName());
}
#RequestMapping(value = "/byLoc", method = RequestMethod.GET)
#ResponseBody
public List<FindDocDTO> findDocByLocation(FindDocBean bean) {
return findDocService.retrieveDocByZipCode(bean.getZipcode(),
bean.getDistance());
}
}
and my Bean is :
public class FindDocBean implements Serializable {
private static final long serialVersionUID = -1212xxxL;
private String name;
private String zipcode;
private int distance;
#Override
public String toString() {
return String.format("FindDocBean[name: %s, zipcode:%s, distance:%s]",
name, zipcode, distance);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getZipcode() {
return zipcode;
}
public void setZipcode(String zipcode) {
this.zipcode = zipcode;
}
public int getDistance() {
return distance;
}
public void setDistance(int distance) {
this.distance = distance;
}
As per all the suggestions found so far, they are suggesting to restrict the bean with required parameters only by something like below :
final String[] DISALLOWED_FIELDS = new String[]{"bean.name", "bean.zipcode", };
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.setDisallowedFields(DISALLOWED_FIELDS);
But my problem is all the 3 parameters of the bean will be used in either of the method supplied on Controller.
Can someone please suggest some solution for this. Thanks in advance.
InitBinder can be used for methods. You can try this.
#InitBinder("findDocByName")
public void initBinderByName(WebDataBinder binder) {
binder.setDisallowedFields(new String[]{"distance","zipcode"});
}
#InitBinder("findDocByLocation")
public void initBinderByZipCode(WebDataBinder binder) {
binder.setDisallowedFields(new String[]{"distance","name"});
}
i was facing same issue, then i added below code in same rest controller class:
#InitBinder
public void populateCustomerRequest(WebDataBinder binder) {
binder.setDisallowedFields(new String[]{});
}
now its working fine for me and mass assignment issue was fixed.
Simple question - how your mapper can instantionate the bean? Here is answer / example. You can pass that data by query parameter, or in header. However that would be strange. Better is to have that methods with #QueryParam providing location, or name. That way it will be easier to protect your application.
As a side note, query has limited length, so if your search form is big and strange, #POST can be good idea, and that way you can pass all the data. For this, simple example that would be overkill.
This looks like an unfortunate false positive. The rule behind this error is made to avoid that properties present in an object but not intended to be (unvalidated) user input are accidentally populated from a web request. An example would be a POST request creating a resource. If the request handler takes the full resource object and fills only missing properties an malicious user could populate fields that she shouldn't be able to edit.
This case however does not match the scheme. You just use the same mechanism to capture your different arguments. Additionally populated properties will not even be read. In
GET http://yourhost/findDocSearch/byName?name=Abuse&zipCode=11111
the additional zipCode would just be ignored. Therefore the assumed risk is not present here.
To fix the warning, you could mark it as a false positive (if this is possible inside your setup). If that is not possible you could also just map the query parameters to method arguments directly. As you only have limited parameters that should not harm too much. If this is also no option you probably need to figure out the exact algorithm your code analysis uses to figure out what checks it will recognize. Unfortunately most scanners are only able to discover a limited set of ways to do input validation.

Hibernate validation with strong typing in Jersey with Jackson

I am implementing a REST API using Jersey. I want to validate all of the inputs to my service (query params, path params, DTOs) and am looking into some options - one that looks like it does the job is Jersey Bean Validation. I also want to have everything in the service strongly typed - for example, instead of using String to represent all of the bits of data, where you'd have a function like this:
public Order getOrder(String customerId);
Instead define types for each bit of data (the benefit of this is to let the compiler catch incorrect data being passed to functions, being able to obfuscate the underlying value in the toString method for logging, knowing that the value is definitely valid if you have an instance and so on), so you end up with functions like this:
public Order getOrder(CustomerId customerId);
And types like this:
public class CustomerId {
private final String value;
public CustomerId(String value) {
this.value = validate(value);
}
public String getValue() {
return value;
}
private String validate(String value) {
// Do some validation here
}
}
The Jersey Bean Validation examples do not use strong types like above. For example:
#Path("/")
class MyResourceClass {
#POST
#Consumes("application/x-www-form-urlencoded")
public void registerUser(
#Pattern(regexp="[a-zA-Z -]{1,50}") #FormParam("name") String name) {
...
}
}
The build in validation is nice in that you get some features for free:
400 bad request exception returned on any validation error
Optionally include the validation error in the response
None of the code in your function gets executed if validation fails
However, there are a few problems:
You have to remember to include the annotations everywhere the data
can be input to your system, so it's hard to apply consistently
You may end up with different definitions of what is valid for a type
You don't get the strong typing benefits mentioned above
Does anyone know of a way to get all of these benefits. I tried defining a type like this:
public class CustomerId {
private final String value;
public CustomerId(String value) {
this.value = validate(value);
}
public String getValue() {
return value;
}
private String validate(String value) {
if (!Pattern.matches("[a-zA-Z -]{1,50}", value)) {
throw new ConstraintViolationException(new HashSet<ConstraintViolation<?>>());
}
return value;
}
}
But it seems the exception doesn't get handled the same way by Jersey, and the response code you get if the validation fails is 404 instead of 400.
Does anyone know of a way to get the best of both worlds?
This is from the spec, in regards to how errors are handle when constructing #XxxParams
... if the [..] is annotated with #MatrixParam, #QueryParam or #PathParam then an implementation MUST generate an instance of NotFoundException (404 status) that wraps the thrown exception and no entity; if the field or property is annotated with #HeaderParam or #CookieParam then an implementation MUST generate an instance of
BadRequestException (400 status) that wraps the thrown exception and no entity.
Though not listed here, #FormParam falls under the 400 bracket.
"Does anyone know of a way to get the best of both worlds?"
We can override this behavior by throwing a WebApplicationException. We could then create an ExceptionMapper for the exception, and then just delegate to the ExceptionMapper that normally handles ConstraintViolationException. I couldn't find any clear detail on this behavior. I mean you would expect that the ExceptionMapper should get called anyway, but it doesn't if it is isn't an instance of WebApplicationException. So you can make your exception extend WebApplicationException.
public static class MyException extends WebApplicationException {
private final ConstraintViolationException cve;
public MyException(ConstraintViolationException cve) {
this.cve = cve;
}
public ConstraintViolationException getConstraintViolationException() {
return cve;
}
}
Then create an ExceptionMapper for it. In the mapper, we simply delegate to the original mapper that handles ConstraintViolationException
public static class MyExceptionMapper implements ExceptionMapper<MyException> {
#Context
private Providers providers;
#Override
public Response toResponse(MyException exception) {
ExceptionMapper<ValidationException> mapper
= providers.getExceptionMapper(ValidationException.class);
return mapper.toResponse(exception.getConstraintViolationException());
}
}
Then you can just throw MyException. If you don't care for an error response body, and all you want is a 400 status, you can forget everything above and simply throw a BadRequestException. Or if you don't care for the response entity that the ConstraintViolationException mapper sends out, you can create your own response in the MyExceptionMapper, or create a Response inside the CustomerId class and pass it the BadRequestException constructor. So you have some options.
A headache from this approach I could see is that you need to create your own ConstraintViolation. That can get old really quick.
The other approach I could see is to use #BeanParam and #Valid
public static class CustomerId {
#FormParam("cust")
#Pattern(regexp="[a-zA-Z -]{1,50}")
private String value;
public void setValue(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
#POST
#Path("form")
#Consumes("application/x-www-form-urlencoded")
public String postForm(#BeanParam #Valid CustomerId custId) {
The problem with this approach is that your bean is now stuck with #FormParam and is not reusable with other #XxxParams.
So you have some trade-offs. Hope this gives you some good information to work with.
UPDATE
Oh and the last option I can think of, is similar to second one above, but you aren't tied to the #XxxParam in the bean
public static class CustomerId {
//#FormParam("cust")
#javax.validation.constraints.Pattern(regexp="[a-zA-Z -]{1,50}")
private String value;
public CustomerId(String value) {
//this.value = validate(value);
this.value = value;
}
...
}
#POST
#Path("form")
#Consumes("application/x-www-form-urlencoded")
public String postForm(#FormParam("cust") #Valid CustomerId custId) {
Think the last option might be the way to go, but you still need to remember to always annotate with #Valid, which sounds like something you were trying to avoid.

Restlet with many #Get in my server side

So I am new with restlet. I am creating a Android application that can communicate with a GAE server (with objectify DB)
I Did this very good tutorial to learn:
http://www.tutos-android.com/webservice-rest-android-appengine-restlet-objectify
It's working very well but do very little.
Onely 2 methods:
public interface UserControllerInterface {
#Put
void create(User user);
#Get
Container getAllUsers();
}
For my application its more complicated so I add many more methods:
public interface UserControllerInterface {
#Put
public void createUser(ObagooUser user);
#Put
public void createMessage(ObagooUser user, String message);
#Put
public void updateMessage(ObagooMessage message);
#Get
public List<ObagooUser> getAllUser();
#Get
public ObagooUser getUserById(String id);
#Get
public List<ObagooMessage> getAllMessage();
#Get
public List<ObagooMessage> getAllMessageFromSender(ObagooUser sender);
#Get
public ObagooMessage getFreeMessage(ObagooUser user);
}
Each of these mothds working server side (I tested with Junit).
Now I am coding the android part and I am having problems.
When I do a simple call to getAllMessage() I get an error:
java.lang.IllegalArgumentException: id cannot be zero
at com.google.appengine.api.datastore.KeyFactory.createKey(KeyFactory.java:44)
at com.googlecode.objectify.ObjectifyFactory.typedKeyToRawKey(ObjectifyFactory.java:269)
at com.googlecode.objectify.impl.ObjectifyImpl.find(ObjectifyImpl.java:159)
at com.googlecode.objectify.impl.ObjectifyImpl.find(ObjectifyImpl.java:183)
at com.obagoo.dao.ObagooUserDAO.getUserById(ObagooUserDAO.java:43)
at com.obagoo.controller.ObagooController.getUserById(ObagooController.java:47)
It's going in the wrong method (getUserById).
I put a break point in my getAllMessage and it's going in, but it is also going in other methods.
If I test many times, sometimes it's calling, createUser or another random method.
Do you see what I am doind wrong?
Adding the getAllMessage code:
public List<ObagooMessage> getAllMessage() {
// logger.debug("Getting all Obagoo Messages");
List<ObagooMessage> msg = new ArrayList<ObagooMessage>();
Objectify ofy = ObjectifyService.begin();
Query<ObagooMessage> q = ofy.query(ObagooMessage.class);
for (ObagooMessage u : q) {
msg.add(u);
}
return msg;
}
In the examples that I've seen, its always shown that you should separate the controller/resource handling the URI for the list resource from the single item (id/name based) resource. So you would have something like:
router.attach("/users", UsersController.class);
router.attach("/users/{id}", UserController.class
router.attach("/messages", MessagesController.class);
Notice the plural naming on the first class: UsersController, and singular naming on the the second class: UserController. The first class would handle cases where no id was being provided, such as a get of all users. Also, note when the id is provided in the URI, it can be automatically mapped into an id field on the class. So the Get method has no parameters on the method call.
As for handling a subset, then for messages from a specific user, that could be handled with query parameters. For instance when calling via a URI with /messages?sender=id, the MessagesController.class would use the following in the method handling the Get:
Form queryParams = getRequest().getResourceRef().getQueryAsForm();
String id = queryParams.getFirstValue("sender");
Hope that helps. I'm no expert, so anyone feel free to correct me.
As error says: you are creating a key with zero Id.
My gues is that your ObagoMessage Id field is long? You should make it Long. Primitive long Id values are not autogenerated, while object type Long are. See the docs.

Categories