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.
Related
Let's say I have this value object:
public class Latitude {
private Double value;
public Latitude(Double value) {
if (value < -180 || value > 180) {
throw RuntimeException("Invalid value for latitude");
}
this.value = value;
}
public Double getValue() {
return this.value;
}
}
And this DTO:
public class LatitudeDTO {
private Double value;
public Double getValue() {
return this.value;
}
}
I want to create a Parser, so I can use it in the application service in this way:
Latitude latitude = latitudeParser.fromDTO(latitudeDTO);
I'm considering two option for implementing this parser. Option #1:
public class LatitudeParser {
public Latitude fromDTO(LatitudeDTO latitudeDTO) throws DtoParserException {
try {
return new Latitude(latitudeDTO.getValue());
} catch(RuntimeException e) {
throw new DtoParserException(e);
}
}
}
Option #2:
public class LatitudeParser {
public Latitude fromDTO(LatitudeDTO latitudeDTO) throws DtoParserException {
if (value < -180 || value > 180) {
throw DtoParserException("Invalid value for latitude");
}
return new Latitude(latitudeDTO.getValue());
}
}
In both cases, DtoParserException is later mapped to 400 Bad Request, when it reaches the presentation layer.
Now, for me option #1 is better, because there's no repetition of the validation rule. However, I've heard that everything that is sent to the domain must be validated before and in this sense the exceptions thrown by the domain layer point out a bug in the application layer code. In this case I shoudn't use domain exceptions to inform the user of my application that the data inputted is invalid.
So, should the parser make its own validations or use the domain validation? It seems so weird to just replicate the validation in the parser.
everything that is sent to the domain must be validated before
I would personally rephrase this to "Everything that makes it into the domain layer is valid". I'm saying this because one reason to use value objects as parameters into the domain layer is that it removes the need for format validation logic in the domain layer. Compare the following to signatures:
public void RegisterUser(string firstName, string lastName, string phoneNumber, string email)
vs
public void RegisterUser(FullName name, PhoneNumber phoneNumber, Email email)
In the second scenario, you can be certain that all parameters are non null and with correctly formatted data.
So, when it says that "must be validated before", in this case, the constructor of the value object is already "before" getting into the business logic layer.
What I would say is that RuntimeException is not a very good name for that exception. Note that exceptions are part of the "Contract" of a class. In the case of the value object, the constructor, the public properties and the exceptions are part of its contract, so you'd probably have something like InvalidCoordinatesException or similar.
The reason I'm saying this is that the DTO parser logic probably belongs to the same layer that currently handles your DtoParserException, so maybe you don't need to translate the exception at all and you directly can handle the InvalidCoordinatesException (or a base ValueObjectValidationException). But that's for your consideration.
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)
I have a controller that has a few methods that get an optional of entity from service, checks if is present and proceeds with some other actions or redirects with message "Entity not found".
It looks like that:
#GetMapping("action")
public String method(#PathVariable Long id,
final RedirectAttributes redirectAttributes){
Optional<Entity> eOpt = entityService.findById(id);
if(eOpt.isEmpty()){
alertHandler.set(redirectAttributes, Status.ENTITY_NOT_FOUND);
return "redirect:/entity/list"
}
Entity e = eOpt.get();
// other actions that are using e
return "view-name";
}
The six lines repeat in a few methods and for different entities too. Is there a way to assign it to some private method? The only thing I came up with is using a private method like:
private Optional<Entity> getEntityOpt(Long id){
Optional<Entity> eOpt = entityService.findById(id);
if(eOpt.isEmpty()){
alertHandler.set(redirectAttributes, Status.ENTITY_NOT_FOUND);
}
return Optional.empty();
}
This only saves me one line in mapped methods, so I don't have to set up alert message. Otherwise I still have to check again if the Optional is empty to redirect it.
So I guess the question really is - can I set up the private method to either return entity or redirect like:
Entity e = getEntityOrRedirect(Long id);
or maybe you have different ways to handle that problem. Or maybe it is what it is and you have to repeat yourself...
You may treat empty Optional as an exceptional situation.
In that case you may provide your own RuntimeException containing path to redirect.
public class EntityNotFoundException extends RuntimeException {
private final String fallbackView;
public EntityNotFoundException(final String fallbackView) {
this.fallbackView = fallbackView;
}
public String getFallbackView() {
return fallbackView;
}
Then provide a method annotated with #ExceptionHandler to your controller class (or if the situation is common for multiple controllers then provide such method to class annotated with #ControllerAdvice). Your exception handler should catch just defined exception and do a redirect.
#ExceptionHandler(EntityNotFoundException.class)
public String redirectOnEntityNotFoundException(final EntityNotFoundException exception,
final RedirectAttributes redirectAttributes) {
alertHandler.set(redirectAttributes, Status.ENTITY_NOT_FOUND);
return exception.getFallbackView();
}
Finally you achieved some kind of getEntityOrRedirect. Now you may use the above setup as following:
#GetMapping("action")
public String method(#PathVariable Long id){
Entity e = entityService.findById(id)
.orElseThrow(() -> new EntityNotFoundException("redirect:/entity/list"));
// other actions that are using e
return "view-name";
}
Code not tested so apologize for typos in advance.
Note I believe it would work for Spring >= 4.3.5 as otherwise RedirectAttributes wouldn't be resolved for #ExceptionHandler (as stated here)
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.
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