How to validate XML input not-null property in Java REST service? - java

I am running the service under TomEE.
The model is very simple:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Card {
#XmlElement(required = true, nillable = false)
private String cardNumber;
public Card() {
//no-op
}
public Card(final String s) {
cardNumber = s;
}
public String getCardNumber() {
return cardNumber;
}
public void setCardNumber(String cardNumber) {
this.cardNumber = cardNumber;
}
}
I followed this example
https://docs.oracle.com/javaee/7/tutorial/jaxrs-advanced008.htm
The service is also very simple like:
#Consumes(APPLICATION_XML)
#Produces(APPLICATION_XML)
public class MyService {
#POST
#Path("status")
public String queryStatus(Card card) {
// do something
}
}
If my input is wrongly formatted, it will have a proper exception. But it doesn't seem to be able to validate empty card number or null.
For example, when I have
"<card></card>"
or
"<card><cardNumber> </cardNumber></card>"
(with an empty string), the service still goes through, with the "cardNumber" property being null or empty.
Well, I could do something in the setter to throw out an exception. But I was hoping JavaEE automatically handle this kind of this if I put the annotation on the property.
So what am I missing here?
Thank you for any tips!

With Bean Validation (http://beanvalidation.org/) Java EE offers a standard way to validate objects. It is also integrated with JAX RS.
So you can use annotations like #NotNull in your Card class. In your Service just say that you want a #Valid Card.
An example can be found here: https://jaxenter.com/integrating-bean-validation-with-jax-rs-2-106887.html

Related

No primary or default constructor found for interface java.util.List Rest API Spring boot

I am passing a request body to a POST request on postman similar to this:
"name":"Mars",
"artifacts":[
{
"elements":[
{
"name":"carbon",
"amount":0.5,
"measurement":"g"
}
],
"typeName":"typeA"
},
{
"elements":[
{
"name":"hydrogen",
"amount":0.2,
"measurement":"g"
}
],
"typeName":"typeB"
}
]
The create method in the rest controller looks like this.
#RequestMapping("/create")
public Planet create(#RequestBody Planet data) {
Planet mars = planetService.create(data.getName(),data.getArtifacts());
return mars;
Planet and all its nested objects have a default constructor such as:
public Planet() {}
However, I am not able to create a new planet object because of lack of a default constructor. Please help!
EDIT:
Planet class
public class Planet {
#JsonProperty("name")
private String name;
#Field("artifacts")
private List<Artifact> artifacts;
public Planet() {}
public Planet(String name, List<Artifact> artifacts)
{
this.name = name;
this.artifacts = artifacts;
}
//setters and getters
}
Artifact class:
public class Artifact() {
#Field("elements")
private List<Element> elements;
#JsonProperty("typeName")
private String typeName;
public Artifact() {}
public Artifact(String typeName, List<Element> elements)
{
this.typeName = typeName;
this.elements = elements;
}
}
Element class:
public class Element() {
#JsonProperty("elementName")
private String name;
#JsonProperty("amount")
private double amount;
#JsonProperty("measurement")
private String measurement;
public Element() {}
public Element(String name, double amount, String measurement)
{
//assignments
}
}
I had that the same error when I forgot the #RequestBody before the parameter
#RequestMapping("/create")
public Planet create(#RequestBody Planet data) {
I don't understand what is the issue you are facing, but i can see an error straight away so guessing that is the issue you are facing, i am going to give you a solution.
Create a class which matches your json data structure like this :
Class PlanetData {
private String name;
private List<Planet> artifacts;
public PlanetData(String name, List<Planet> artifacts){
name = name;
artifacts = artifacts;
}
// include rest of getters and setters here.
}
Then your controller should look like this. Basically you needed to put #RequestBody to all the parameters you want to recieve from request JSON. Earlier you only put #RequestBody to name parameter not artifact parameter and since Request Body can be consumed only once, so you need a wrapper class to recieve the complete request body using single #RequestBody annotation.
#RequestMapping("/create")
public String create(#RequestBody PlanetData data) {
Planet mars = planetService.create(data.getName(),data.getArtifacts());
return mars.toString();
}
Edit : Looking at the Planet class, it also needs some modification
public class Planet {
private String typeName; // key in json should match variable name for proper deserialization or you need to use some jackson annotation to map your json key to your variable name.
private List<Element> elements;
public Planet() {}
public Planet(String typeName, List<Element> elements)
{
this.typeName = typeName;
this.elements = elements;
}
//setters and getters. Remember to change your setters and getter from name to typeName.
}
Hope this solves your issue.
This answer too might help someone.
When you are using spring framework for your API development, you may accidently import a wrong library for RequestBody and RequestHeader annotations.
In my case, I accidently imported library,
io.swagger.v3.oas.annotations.parameters.RequestBody
This could arise the above issue.
Please ensure that, you are using the correct library which is
org.springframework.web.bind.annotation.RequestBody
I guess, it’s trying to call new List() which has no constructor. Try using ArrayList in your signatures.
If it works this way, you have found the error. Then rethink your concept of calling methods, since you would usually want to avoid using implementations of List in method signatures
Make sure your request type is not of type GET
If so it is better not to send data as request body.
you should write as below:
...
public String create(#RequestBody JSONObject requestParams) {
String name=requestParams.getString("name");
List<Planet> planetArtifacts=requestParams.getJSONArray("artifacts").toJavaList(Planet.Class);
...

Jersey. Nice way of hiding some of a POJO's fields in a response for a REST request

Let's assume the following example.
The POJO class:
#XmlRootElement
public class User {
private String id;
private String email;
private String password;
private String firstName;
private String lastName;
// getters and setters
}
The resource class:
#Path("user")
public class UserResource {
private UserRepository userRepository = new UserRepositoryStub();
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces({MediaType.APPLICATION_XML,MediaType.APPLICATION_JSON})
public User createUser(User user) {
return userRepository.create(user);
}
#GET
#Path("{objectId}")
#Produces({MediaType.APPLICATION_XML,MediaType.APPLICATION_JSON})
public Response getManagedObject(#PathParam("objectId") String objectId) {
if (objectId == null) {
return Response.status(Response.Status.BAD_REQUEST).build();
}
User user = userRepository.findUser(objectId);
if (user == null) {
return Response.status(Response.Status.NOT_FOUND).build();
}
// Possible, but seems that not nice solution
// user.setPassword(null);
return Response.ok().entity(user).build();
}
}
In this simple example I want that the GET request {url}/user/12345 doesn't return password field. I've commented one solution I don't like.
In general while working on the API I want to have configurations of visibility of POJO's fields for every request. Is there an elegant way of achieving that?
Create a TransferObject i.e TO or DTO which holds the fields that you want the user to show in JSON response. You can use #JsonIgnore on the field and the JSON parser wont parse that field and thus wont be included in response.
the general practice is to have a service layer in between. you then have a dto object that is an io object for the outside world that is converted to your resource/entity/repository/whatever object. you need to provide conversion/mapper/whatever between those 2 types of objects and you don't set the password when going in dto to resource direction. same thing is usually done for ids in rest interfaces. you don't want anyone to update a resource and by providing an id in the input object to update a different object. this is how things are usually done even though it means extra code, this is usually trivial. can be simplified using a config using Dozer framework or something similar.
from a design point of view resource/persistence layer should only contain atomic operations. what happens if you need to do several of those for a single resource? you'd have to put it in a single method in the resource class. this way you'll be mixing the rest/io logic with what should be in the service layer. easier to make a mistake and harder to write isolated unit tests for
Assuming that you want the POST method (un-marshaling) to include the password -
but not the GET method (marshaling) - and you are using JAXB, you can write an XmlAdapter.
Its primer use is to convert between mappable and unmappable classes, but it can do the trick here.
public class PasswordAdapter extends XmlAdapter<String, String> {
#Override
public String unmarshal(String v) throws Exception {
return v;
}
#Override
public String marshal(String v) throws Exception {
return "***";
}
}
Then specify that adapter for the password property:
class User {
//...
#XmlJavaTypeAdapter(PasswordAdapter.class);
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}

Conditional property serialization with Jackson and Jersey

I'm unsuccessfully trying to conditionally and dynamically pick which property to serialize to respond to each request with Jersey (using Jackson). The idea behind this is to securely access to properties of objects within a REST API.
I have several objects that I return in API calls that should show/hide fields depending in the user who is authenticated.
For example, lets say I have an object Car
public class Car implements Serializable {
private Long id;
private String VIN;
private String color;
...
}
Lets say that if an user with the ROLE_ADMIN is authenticated, all properties should be returned, but if there isn't a logged user only the first two need to be shown.
I was thinking on building something that's annotation based. Something like:
public class Car implements Serializable {
private Long id;
private String VIN;
#Secured({AccessRole.ROLE_ADMIN})
private String color;
...
}
In this case, the color property should only be returned if the access role of the requesting user matches the ones passed via the annotation.
But I'm unable to get a hook on where should I implement this logic.
What I'm trying to implement is a sort of #JsonIgnore but that's conditional and dynamic. All solutions I found so far are static.
Is this even possible?
Jersey has support for Entity Filtering. Aside from general filtering, it also supports Role-based Entity Filtering using (javax.annotation.security) annotations.
So you can use the #RolesAllowed, #PermitAll, and #DenyAll annotations on the domain model properties
public static class Model {
private String secured;
#RolesAllowed({"ADMIN"})
public String getSecured() { return this.secured; }
}
To make this work though, you need to have set the SecurityContext inside of a request filter. Jersey will look up the SecurityContext to validate the roles. You can read more about it in this post (Note: the entity filtering is separate from any real authorization that is mentioned in that post. But the post does explain about the SecurityContext).
Basically you will have something like (notice the last line where you set the SecurityContext).
#PreMatching
public static class SimpleAuthFilter implements ContainerRequestFilter {
private static final Map<String, User> userStore = new ConcurrentHashMap<>();
static {
userStore.put("peeskillet", new User("peeskillet", Arrays.asList("ADMIN", "USER")));
userStore.put("paulski", new User("paulski", Arrays.asList("USER")));
}
#Override
public void filter(ContainerRequestContext request) throws IOException {
final String authHeader = request.getHeaderString("Authorization");
final String username = authHeader.split("=")[1];
final User user = userStore.get(username);
if (user == null) {
throw new NotAuthorizedException("No good.");
}
request.setSecurityContext(new SimpleSecurityContext(user));
}
}
Where the SimpleSecurityContext is just a class of your own, where you need to override the isUserInRole method and check if the user has the role
private static class SimpleSecurityContext implements SecurityContext {
private final User user;
SimpleSecurityContext(User user) {
this.user = user;
}
#Override
public Principal getUserPrincipal() {
return new Principal() {
#Override
public String getName() {
return user.getUsername();
}
};
}
#Override
public boolean isUserInRole(String role) {
return user.getRoles().contains(role);
}
#Override
public boolean isSecure() {
return false;
}
#Override
public String getAuthenticationScheme() {
return "simple";
}
}
That's pretty much it. You will also need to register the SecurityEntityFilteringFeature with the application to make it all work.
See a complete test case in this Gist
You can register a custom MessageBodyWriter https://jersey.java.net/documentation/latest/user-guide.html#d0e6951
The MessageBodyWriter will use your custom logic to decide what to write.
It can be done with #JsonView as #dnault suggested.
http://www.baeldung.com/jackson-json-view-annotation
Your MessageBodyWriter will hold a jackson mapper and you will apply the writerWithView with the matching view class as described in the above link.
EDIT: see this one - Jackson Json serialization: exclude property respect to the role of the logged user

How to validate Integer or long in spring mvc

class employee{
...
private long phone;
...
}
I want to validate phone number using spring jsr303 validator, In my Controller I am using #valid. I am successfully validating entered value is number or string by using generic typeMismatch placing in error message property file.
But I want to validate entered number format is correct or not.(#pattern for string only)
How to achieve this one,please suggest me.
Normally phone numbers are String and you can validate by using #Pattern, but if you want to validate any fields you can do like this.
Custom annotation Javax validator
#javax.validation.Constraint(validatedBy = { PhoneNumberConstraintValidator.class })
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
#Retention(RUNTIME)
public #interface ValidPhoneNumber {
}
public class PhoneNumberConstraintValidator implements ConstraintValidator<ValidPhoneNumber, Long> {
#Override
public void initialize(final ValidPhoneNumber constraintAnnotation) {
// nop
}
#Override
public boolean isValid(final Long value, final ConstraintValidatorContext context) {
//your custom validation logic
}
}
class employee{
...
private long phone;
#ValidPhoneNumber
public Long getPhone() { return phone; }
...
}
OR simpler if you have hibernate validator, you can just add this method in your entity class.
#org.hibernate.validator.AssertTrue
public boolean validatePhoneNumber() { }

Spring CGLIB AopProxy interferes with Jersey resource method parameter validation

Stack is Spring Boot w/ Jetty/Jersey. Here's the resource method in question:
#GET
#Path("campaignTargets")
#Produces(MediaType.APPLICATION_JSON)
#Transactional(readOnly=true)
public List<CampaignTargetOutputDTO> getCampaignTargets(
#PathParam("businessUnitId") Integer id,
#QueryParam("name") String name,
#Pattern(regexp = DATE_VALIDATION_PATTERN) #QueryParam("startDate") String startDate,
#Pattern(regexp = DATE_VALIDATION_PATTERN) #QueryParam("endDate") String endDate,
#Pattern(regexp = INTEGER_CSV_VALIDATION_PATTERN) #QueryParam("targetTypeIds") String targetTypeIds,
#Pattern(regexp = ALPHANUM_CSV_VALIDATION_PATTERN) #QueryParam("statuses") String statuses) {
return ResourceUtil.entityOr404(campaignService.getAdvertiserCampaignTargets(id, name, startDate, endDate, targetTypeIds, statuses));
}
When Jersey intercepts the call to this method to perform the validation, it doesn't (always) get this method. The reason I know this is because I have taken the advice of the Jersey documentation and created the following ValidationConfig:
#Provider
public class ValidationConfigurationContextResolver implements
ContextResolver<ValidationConfig> {
#Context
private ResourceContext resourceContext;
#Override
public ValidationConfig getContext(Class<?> type) {
final ValidationConfig config = new ValidationConfig();
config.constraintValidatorFactory(
resourceContext.getResource(InjectingConstraintValidatorFactory.class));
config.parameterNameProvider(new CustomParameterNameProvider());
return config;
}
private static class CustomParameterNameProvider extends DefaultParameterNameProvider {
private static final Pattern PROXY_CLASS_PATTERN = Pattern.compile("(.*?)\\$\\$EnhancerBySpringCGLIB\\$\\$.*$");
public CustomParameterNameProvider() {
}
#Override
public List<String> getParameterNames(Method method) {
/*
* Since we don't have a full object here, there's no good way to tell if the method we are receiving
* is from a proxy or the resource object itself. Proxy objects have a class containing the string
* $$EnhancerBySpringCGLIB$$ followed by some random digits. These proxies don't have the same annotations
* on their method params as their targets, so they can actually interfere with this parameter naming.
*/
String className = method.getDeclaringClass().getName();
Matcher m = PROXY_CLASS_PATTERN.matcher(className);
if(m.matches()) {
try {
return getParameterNames(method.getDeclaringClass().getSuperclass().
getMethod(method.getName(), method.getParameterTypes()));
} catch (Exception e) {
return super.getParameterNames(method);
}
}
Annotation[][] annotationsByParam = method.getParameterAnnotations();
List<String> paramNames = new ArrayList<>(annotationsByParam.length);
for(Annotation[] annotations : annotationsByParam) {
String name = getParamName(annotations);
if(name == null) {
name = "arg" + (paramNames.size() + 1);
}
paramNames.add(name);
}
return paramNames;
}
private String getParamName(Annotation[] annotations) {
for(Annotation annotation : annotations) {
if(annotation.annotationType() == QueryParam.class) {
return ((QueryParam) annotation).value();
} else if(annotation.annotationType() == PathParam.class) {
return ((PathParam) annotation).value();
}
}
return null;
}
}
}
My main problem with this solution is that it requires a paragraph of comment to (hopefully) prevent future confusion. Otherwise it seems to work. Without this, I get uninformative parameter names like arg1 and so on, which I'd like to avoid. Another big problem with this solution is that it relies too heavily on the implementation of Aop proxying in Spring. The pattern may change and break this code at some point in the future and I may not be here to explain this code when the comment fails to illuminate its purpose. The weirdest thing about this is that it seems to be intermittent. Sometimes the parameter names are good and sometimes they're not. Any advice is appreciated.
It turns out this happens as a result of running the server from eclipse. I haven't quite figured out why, but running the server from the command line fixes the problem. If anyone can figure out why eclipse does this and how to turn off whatever "feature" of eclipse is causing this, I will upvote/accept your answer. For now the answer is, don't run the service in eclipse.

Categories

Resources