I've built a REST Service with Spring Boot. In one request method I'm validating the incoming request vs. an Object that I have annotated for validation (#NotNull etc.) using Hibernate Validator. The code for the REST endpoint looks like this:
#GetMapping(value = "/parameter-dates")
public ResponseEntity getParameterDates(ParameterDateRequest parameterDateRequest) {
Set<ConstraintViolation<ParameterDateRequest>> inputErrors = validator.validate(parameterDateRequest);
if (!inputErrors.isEmpty()) {
objectValidationErrorMessages = new ArrayList<>();
for (ConstraintViolation<ParameterDateRequest> constraintViolation : inputErrors) {
objectValidationErrorMessage = new ObjectValidationErrorMessage();
log.info("Error for user: " + loggedInUser.getUserEmail() +
" field: " + constraintViolation.getPropertyPath() + " with value: " + parameterDate.getParameterDateUnadjusted() +
" has error: " + constraintViolation.getMessage());
objectValidationErrorMessage.setFieldWithError(constraintViolation.getPropertyPath().toString());
objectValidationErrorMessage.setErrorMessage(constraintViolation.getMessage());
objectValidationErrorMessages.add(objectValidationErrorMessage);
}
return (new ResponseEntity(objectValidationErrorMessages, headers, HttpStatus.BAD_REQUEST));
}
//The rest of the code that is used when there is not validation errors
I would like to move the creation of the validation error messages to a metod of it's own like this:
public List<ObjectValidationErrorMessage> getErrorMessages(Class<?> clazz, Object model) {
List<ObjectValidationErrorMessage> objectValidationErrorMessages = new ArrayList<>();
Set<ConstraintViolation<?>> inputErrors = validator.validate(model);
if (!inputErrors.isEmpty()) {
for (ConstraintViolation<?> constraintViolation : inputErrors) {
objectValidationErrorMessage = new ObjectValidationErrorMessage();
objectValidationErrorMessage.setFieldWithError(constraintViolation.getPropertyPath().toString());
objectValidationErrorMessage.setErrorMessage(constraintViolation.getMessage());
objectValidationErrorMessages.add(objectValidationErrorMessage);
}
}
return objectValidationErrorMessages;
}
With the code as it is now I get the following error Unknow class: validateClass for Set<ConstraintViolation<validatedClass>>. How do I pass a Class name, in this case ParameterDateRequest as an argument to a method?
Update:
I manged to pass the Class as Class<?> clazz. I also realised that I need to pass the Object as an Object but I get this error:
validate (T, Class<?>...) in Validator cannot be applied
to (java.lang.Object)
reason: Incompatible equality constraint: ? and T
You can generify your method:
public <T> List<ObjectValidationErrorMessage> getErrorMessages(Class<T> clazz, T model) {
This will ensure that your Class<?> token and your Object have matching types.
Then pass the arguments as such:
validator.validate(model, clazz)
Note I could be misreading the error (I couldn't find the javadocs for the validator), so it's also possible that validator has a class-level generic (e.g. Validator<MyType>). If that's the case, then the T/Object you pass to validator#validate must match the type of the Validator, but the second parameter seems to accept any classtype (Class<?>). You also wouldn't need to generify the method as well, you would just have to match the type for the validator.
Related
I'm trying to test a method handleRequest() which is the handler code when an AWS lambda is triggered. The method is as follows:
#Override
public Response handleRequest(Object input, Context context) {
final Request request =
OBJECT_MAPPER.readValue(OBJECT_MAPPER.writeValueAsBytes(input),Request.class);
// some code that uses request
.
.
}
Input to AWS lambda function that is working fine when tested from AWS console -
{"fieldA": "fieldAValue",
"fieldB": "fieldBValue",
"fieldC": "fieldCValue",
"fieldList": [
{
"subFieldA": "subFieldAValue",
"subFieldB": "subFieldBValue",
"subFieldC": "subFieldCValue",
"subFieldD": "subFieldDValue",
"subFieldE": "subFieldEValue"
}
]
}
My test method -
#Test
public void test_handleRequest() {
String testInput =
"{\"fieldA\":\"fieldAValue\",\"fieldB\":\"fieldBValue\",+
"\"fieldC\":\"fieldCValue\"," +
"\"fieldList\":[" +
"{\"subFieldA\":\"subFieldAValue\", " +
"\"subFieldB\":\"subFieldBValue\", " +
"\"subFieldC\":\"subFieldCValue\", " +
"\"subFieldD\":\"subFieldDValue\", " +
"\"subFieldE\":\"subFieldEValue\"}]}";
handleRequest(testInput, null);
// code to assert that the actual and expected values are equal.
.
.
.
}
Error when the test method is executed -
Cannot construct instance of 'model.Request'
(although at least one Creator exists): no String-argument constructor/factory method to
deserialize from String value ('{"fieldA":"fieldAValue","fieldB":"fieldBValue",
"fieldC":"fieldCValue","fieldList":[{"subFieldA":"subFieldAValue",
"subFieldB":"subFieldBValue", "subFieldC":"subFieldCValue",
"subFieldD":"subFieldDValue", "subFieldE":"subFieldEValue"}]}') at [Source: (byte[])""
The test method is failing with the error that the given input string cannot be mapped to Request class. I got expected result when the AWS lambda is triggered with the same input via AWS console, so I'm pretty sure that the code is perfectly fine.
Is this the right way to build the testInput? Any other alternative way to build testInput so that it can be correctly mapped to Request class when testing?
Converting the json string to Object type using readValue() before passing it to the method under test resolved the error.
#Test
public void test_handleRequest() {
String testInput =
"{\"fieldA\":\"fieldAValue\",\"fieldB\":\"fieldBValue\",+
"\"fieldC\":\"fieldCValue\"," +
"\"fieldList\":[" +
"{\"subFieldA\":\"subFieldAValue\", " +
"\"subFieldB\":\"subFieldBValue\", " +
"\"subFieldC\":\"subFieldCValue\", " +
"\"subFieldD\":\"subFieldDValue\", " +
"\"subFieldE\":\"subFieldEValue\"}]}";
Object input = new ObjectMapper().readValue(testInput, Object.class);
handleRequest(input, null);
// code to assert that the actual and expected values are equal.
.
.
.
}
I am new to lambda and trying to create a function, where i can consume both SNSEvent and simple json as payload in my requestHandler method. How can i do it in Java? should i take my input as Object type?
public class LogEvent implements RequestHandler<SNSEvent, Object> {
public Object handleRequest(SNSEvent request, Context context){
String timeStamp = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss").format(Calendar.getInstance().getTime());
context.getLogger().log("Invocation started: " + timeStamp);
context.getLogger().log(request.getRecords().get(0).getSNS().getMessage());
timeStamp = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss").format(Calendar.getInstance().getTime());
context.getLogger().log("Invocation completed: " + timeStamp);
return null;
}
}
this works fine. but if i want the flexibility of passing simple Json like below
{
"req": "test"
}
from aws console lambda test section. to manually trigger few tests without sending actual SNSEvent Object. how should i modify my code.
Note: above mentioned code and test are not exactly what i have but providing any suggestion on given code itself will be helpful.
I am trying to pass a String array from my typescript
tmp : Array<string> = [];
So I have a function which takes in this array as a parameter input
passValues(test : Array<string>) {
........
// some method to call post method from service
}
So in service
public passingOfValues( test : Array<string> ) : Observable<Array<string>> {
let headers = new Headers({ 'Content-Type': 'application/json'} );
let options = new RequestOptions({ headers: headers);
let response = this.http.post(this.basePath + this.modulePath + '/getArrayValue', {'test' : test }, options)
.map(this.extractData)
.catch(this.handleError);
return response;
}
But I am getting errors such as System property [org.owasp.esapi.devteam] is not set
And I read on other posts that I have to stringify the array before passing to backend.
Is there a reason why I need to stringify / also can I just pass the raw array?
EDIT 1 :
including backend controller codes
public ResponseEntity<?> getArrayValues( ArrayList<String> test ) {
logger.debug("### Test if array has a size ###" + test.size());
}
Apparently size already shows 0 from here.
EDIT 2 :
While debugging, i realised that the SQL at the back is receiving
say
HOME CHARACTER(20 OCTETS)
does this make any difference?
Like passing of string into octets or do I have to do some conversion?
Sorry if I have alot of questions am also working hard on debugging and learning more about it!
Most of the developers like JSON data as request and it's good practice in RESTful apis. why?
JSON format is {key1: value1, key2: value 2,....}
You are passing
this.http.post(this.basePath + this.modulePath + '/getArrayValue',{'test' : YOUR_ACTUAL_ARRAY})
form the front-end. The httpClient.post(url,body,options?) has url and body as mandatory. How can you get it in back-end? Since you have body only,
public ResponseEntity<?> getArrayValues(#RequestBody List<String> test) {
// codes
}
Key of passed parameter from front-end test and variable which
listens in back-end should be in same name. Otherwise
#RequestBody("KEY_NAME") List<String> any_variable
As you asked from comment, you may have two key value pairs. Eg : { "test" : value1, "tmp": value2}. Assume value1 and value2 both are String array.
this.http.post(this.basePath + this.modulePath + '/getArrayValue',{'myJson' : YOUR_JSON})
There are lot of way(Eg : Gson,ObjectMapper etc). I use another way.
Create a class called TestTmpConverter
class TestTmpConverter{
List<String> test;
List<String> tmp;
//No-argument constructors & Argument constructors
//Getters
}
In controller
public ResponseEntity<?> getArrayValues(#RequestBody List<TestTmpConverter> myJson ) {
List<TestTmpConverter> test=myJson.getTest();
List<TestTmpConverter> tmp=myJson.getTmp();
// Do your work
}
I only showed one way.There are a lot of way to pass data to back-end like #RequestParam, #PathVariable etc. I feel now you get something how you can pass the data.
For your client put your data directly on POST's body:
public passingOfValues( test : Array<string> ) : Observable<Array<string>> {
let headers = new Headers({ 'Content-Type': 'application/json'} );
let options = new RequestOptions({ headers: headers);
let response = this.http.post(this.basePath + this.modulePath + '/getArrayValue',
test, options)
.map(this.extractData)
.catch(this.handleError);
return response;
}
On your REST service use the #RequestBody annotation:
public ResponseEntity<?> getArrayValues(#RequestBody String[] test ) {
logger.debug("### Test if array has a size ###" + test.size());
}
So the class I'm doing JSR-303 bean validation on has two fields with the same pattern constraint applied to each:
#Column(name="test_suite_revision")
#XmlElement(name="test_suite_revision")
#NotNull
#Pattern(regexp = "\\d\\d-\\d\\d-\\d\\d\\d\\d", message = "value must be of the form xx-xx-xxxx")
private String revisionTestSuite;
#Column(name="test_revision")
#XmlElement(name="test_revision")
#NotNull
#Pattern(regexp = "\\d\\d-\\d\\d-\\d\\d\\d\\d", message = "value must be of the form xx-xx-xxxx")
private String revisionTest;
IMPORTANT - this class is NOT a form-backing class in a classic Spring MVC webapp but an entity class that lives at the base of a web service. So the validation is occuring in the service.
Now the web client that consumes the web service is a Spring MVC and does have a form-backing bean which ties to a jsp with places to put error messages.
So suppose a user enters an incorrectly-formatted string into one of the two fields. I can trap for it with this pretty standard code snippet
Set<ConstraintViolation<TestCase>> violations = validator.validate( permit);
if( !violations.isEmpty()) {
logger.debug( "basic validation FAILED with " + violations.size() + " errors");
Iterator<ConstraintViolation<TestCase>> iter = violations.iterator();
while( iter.hasNext()) {
ConstraintViolation<TestCase> cv = iter.next();
logger.debug( "invalidValue:" + cv.getInvalidValue());
logger.debug( "message:" + cv.getMessage());
ConstraintDescriptor<?> cd = cv.getConstraintDescriptor();
Map<String, Object> mapp = cd.getAttributes();
for( String keey : mapp.keySet()) {
logger.debug("mapp key:" + keey + ":" + mapp.get(keey));
}
which writes out
basic validation FAILED with 1 errors
invalidValue:050607
message:value must be of the form xx-xx-xxxx
mapp key:message:value must be of the form xx-xx-xxxx
mapp key:payload:[Ljava.lang.Class;#1367702
mapp key:flags:[Ljavax.validation.constraints.Pattern$Flag;#bf5210
mapp key:groups:[Ljava.lang.Class;#a49be5
mapp key:regexp:\d\d-\d\d-\d\d\d\d
Here's the rub: How does one figure out WHICH field failed validation? I can't seem to find a way to extract the field name , "revisionTest" or "revisionTestSuite"
from the ConstraintViolation object nor the ConstraintDescritpor object.
the getValidationAppliesTo() method newly available in version 1.1.0.Final of javax.validation-api seems promising but so far this method throws an AbstractMethodError during runtime. Ugh.
TIA,
Still-learning Steve
See ConstraintViolation#getPropertyPath method:
/**
* #return the property path to the value from {#code rootBean}
*/
Path getPropertyPath();
Path.Node#getName will give you the property name. For field names in nested beans, you have walk the path.
I am making the following AJAX request:
$.post('/route', {
arg1 : 'foo',
arg2 : 'bar'
});
Through the route:
POST /route controllers.Test.readPost()
How do I access these POST variables in the method of my controller?
public static Result readPost() {
return TODO; // read post variables
}
I cannot find a simple way of doing this in the documentation. It only states how to get values from JSON requests.
Use DynamicForm
public static Result getValues(){
DynamicForm requestData = form().bindFromRequest();
String name = requestData.get("name");
String desg = requestData.get("desg");
// etc
return ok("You sent: " + name + ", " + desg);
}
There is also other possibility to construct AJAX query and pass arguments via javascriptRoutes: https://stackoverflow.com/a/11133586/1066240
Of course it will pass the params via URL so it's not suitable for every value, but in many places it will be goot enough for sending POST requests with AJAX. Of course javascriptRoutes create the request with type set in routes file.
BTW: it was better if you wrote which version you are using.
you can use GET with an ajaxRequest. more information can be found here http://www.javascriptkit.com/dhtmltutors/ajaxgetpost.shtml
var mygetrequest=new ajaxRequest()
mygetrequest.onreadystatechange=function(){
if (mygetrequest.readyState==4){
if (mygetrequest.status==200 || window.location.href.indexOf("http")==-1){
document.getElementById("result").innerHTML=mygetrequest.responseText
}
else{
alert("An error has occured making the request")
}
}
}
var namevalue=encodeURIComponent(document.getElementById("name").value)
var agevalue=encodeURIComponent(document.getElementById("age").value)
mygetrequest.open("GET", "basicform.php?name="+namevalue+"&age="+agevalue, true)
mygetrequest.send(null)