Imagine the following scenario:
I send a request to a service (which uses Play framework) with the following parameters (parameter's name should be underscored by convention):
first_name=James&second_name=Parker
Moreover I have a model class in my codebase which looks like this.
public class User {
#Constraints.Required
private String firstName;
#Constraints.Required
private String secondName;
public String getFirstName() {
return firstName;
}
public String getSecondName() {
return secondName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setSecondName(String secondName) {
this.secondName = secondName;
}
}
All I want to do is to map parameter's names with the corresponding
field names. The following statement using Play Framework fails because
User object cannot be initialized with the given parameters of request.
Form<User> form = Form.form(User.class).bindFromRequest();
Read this first: https://www.playframework.com/documentation/2.1.1/JavaRouting
And then:
GET /myMethod/:firstName/:seccondName controllers.MyController.myMethod(firstName: String, seccondName: String)
and simple Method:
public myMethod(String firstName, String lastName) {
User u = new User();
u.setFirstName(firstName);
u.setSeccondName(seccondName);
}
Obviously if you use play framework 2.
You have underscores in the request parameter names, yet your class has the members in camelCase (e.g. first_name vs fistName [Missing a r here]).
Secondly it also seems like these class members are declared private AND you have no setter methods only getters (i.e. setFirstName).
In order to do the binding your class is instantiated and the values are set and with no way to do so it will fail.
Once you've fixed the above, you should be able to bind and then call form.hasErrors() to check if validation has failed.
Hope that helps.
Update:
To handle the mismatch between the request parameter names and the class member names you would probably have to manually set things up.
In your controller method you would do something like this:
Map<String, String[]> reqBody = request().body().asFormUrlEncoded()
Map<String, String[]> newReqBody = new HashMap<>();
for(Map.Entry<String, String[]> entry: body.entrySet()) {
newBody.put(underscoreToCamelCase(entry.key()), entry.value());
}
Form<User> form = Form.form(User.class).bindFromRequest(newReqBody);
Note that I'm using the overloaded version of bindFromRequest as seen here
You should then implement the underscoreToCamelCase method in a generic enough way to handle all your conventions (Perhaps you might have a situation where there are more than one underscores).
Related
In the below hashmap you can see, I have list of key param values for which I need to automate cases for multiple values without repeating the hashmap rather it would be and update.
How I am doing it:
1st test case
HashMap<String, String> queryParam = new HashMap<>();
queryParam.put("Name", Name);
queryParam.put("street","street" );
queryParam.put("city","city" );
queryParam.put("state", "state");
queryParam.put("postalCode","postalCode" );
queryParam.put("country", "country");
queryParam.put("email", "email");
queryParam.put("website","website" );
queryParam.put("phone", "phone");
Response response = request.auth().basic(uname, pwd).body(queryParam).contentType(APPLICATION_JSON)
.post().then().extract()
.response();
Now if you see the above hashmap, it has mandatory params, some optional and then each param has different validation. Now it terms to cover all the testcases with each keys, above haspmap is repeating and values or keys are changing. I would like to do this in better and efficient way of it.
Instead of using Map<K, V>, you should use Java POJO. Using constructor to setup default value, then using setter to change value. It's more efficient.
One more thing, you could apply Factory design pattern to build object with desired value for each test case.
Test example
#Test
void test1() {
QueryObject query = QueryObjectFactory.getDefaultValue();
Response res = given().contentType(ContentType.JSON)
.body(query)
.post("to_your_api_endpoint");
}
Factory class
public class QueryObjectFactory {
public static QueryObject getDefaultValue() {
QueryObject queryObject = new QueryObject();
queryObject.setName("name");
queryObject.setStreet("street");
queryObject.setCity("city");
queryObject.setCountry("country");
queryObject.setState("state");
queryObject.setPostalCode("postalCode");
queryObject.setEmail("email");
queryObject.setWebsite("website");
queryObject.setPhone("phone");
return queryObject;
}
}
POJO
note: I use lombok to generate getter and getter --> reduce complex of POJO class.
import lombok.Data;
#Data
public class QueryObject {
private String name;
private String street;
private String city;
private String state;
private String postalCode;
private String country;
private String email;
private String website;
private String phone;
}
This question already has answers here:
In Spring-mvc the attribute names in view have to always match the property names in model?
(3 answers)
How to customize parameter names when binding Spring MVC command objects?
(10 answers)
Closed 3 years ago.
So, url requested looks like
localhost:8080/contacts?id=22&name=John&eventId=11
and also I got an object to map request into
public class ContactDTO {
private Long id;
private String name;
private Long eventId;
}
I use a controller method like passing my request params into an object
#GetMapping("/contacts")
public ContactDTO contacts(ContactDTO contact) {
// everything is awesome! contact maps clearly
return contact;
}
The question is how to map like this but have different name
localhost:8080/contacts?id=22&name=John&event_id=11
Setting #JsonAttribute doesn't works because Jackson mapper works only in requestbody.
Maybe I should write custom HandlerMethodArgumentResolver or something like that?
P.S.
I've got a dirty hack (objectMapper is injected, so I can use #JsonAttributes),
But this case fails on array mapping, same mapping with requestbody works fine
#GetMapping("/contacts")
public ContactsDTO contacts(#RequestParam Map<String,String> params) {
ContactDTO contactDTO = objectMapper.convertValue(params,ContactDTO.class);
return contactDTO;
}
Since it is an API design requirement, it should be clearly reflected in the corresponding DTO's and endpoints.
Usually, this kind of requirement stems from a parallel change and implies that the old type queries will be disabled during the contract phase.
You could approach the requirement by adding the required mapping "query-parameter-name-to-property-name" by adding it to the ContactDTO. The simplest way would be just to add an additional setter like below
public class ContactDTO {
private Long id;
private String name;
private Long eventId;
public void setEvent_id(Long eventId) {
this.eventId = eventId;
}
}
If you prefer immutable DTO's, then providing a proper constructor should work as well
#Value
public class ContactDTO {
private Long id;
private String name;
private Long eventId;
public ContactDTO(Long id, String name, String eventId, String event_id) {
this.id = id;
this.name = name;
this.eventId = eventId != null ? eventId : event_id;
}
}
Use something like
#RequestParam(name="event_id", required = true) long eventId
in the parameter list to change the parameter name.
Use #RequestBody insteaf of #requestparam.
Suppose I have an embeddable class, that is embedded in an entity class like below.
#Embeddable
public class FullName {
private String firstName;
private String lastName;
// constructor, getters, setters, as needed
}
#Entity
public class Account {
#Embedded
private FullName fullName;
// id, other data and methods
}
One person can have multiple accounts, hence the full name is not a key for the Account class. What I would like to have, is the following method in a jpa repository
public interface AccountRepository extends CrudRepository<Account, Long> {
Collection<Account> findAllByFullName(FullName fullName);
}
Initially, I thought this would just work, but apparently it's not that simple. I could not get a method like this to work. The only thing I thought might help, was implement an equals method on FullName, saying that a FullName with same first and last name are equal, but this had no effect.
What I ended up doing for the moment is this:
public interface AccountRepository extends CrudRepository<Account, Long> {
Collection<Account> findAllByFullNameFirstNameAndFullNameLastName(String firstName, String lastName);
default Collection<Account> findAllByFullName(FullName fullName) {
return findAllByFullNameFirstNameAndFullNameLastName(fullName.getFirstName(), fullName.getLastName());
}
}
Is there a way for me to avoid creating this intermediary (visible) method?
You can use Example as below,
Collection<Account> findAll(Example< Account > account);
And then pass it as,
FullName fullName = new FullName(fname, lname);
Account acc = new Account();
acc.setFullName(fullName);
Collection<Account> accounts = accountRepository.findAll(Example.of(acc));
Hope it helps.
Is it somehow possible to annotate a Java Method in that way that i later can give another Method a Field Identifier or something like that, so that this Method can call the right one?
I know that normally you would do this with interfaces, but in my case this would be a immense count of interfaces... I need to use this in Entity Classes for my Database (and i'm not allowed to use a ORM Mapper)
For example: I have the Entity
public class Account{
private String username;
private String password;
private String name;
private String mail;
public void setUserName(String username){
this.username = username;
}
public String getUserName(){
return username;
}
[all other getter/Setter...]
}
Now i want to tell a Method that it need to validate a Field, for example the username field.
The Method that does should look like this:
public void validateField(XXX Field, Entity entity) throws ValidationFailedException, EmptyFieldException;
where XXX is somehow the FieldIdentifier.
Is that in any way possible in Java?
My only guess it that i Use public static final ìnt stuff in there to give every field a Method or so...
What do you use? I don't see any annotations from which I can guess your framework. If you use Hibernate, you can use something like #NotNull or something else, or even do your custom validation:
This is how you would go with your example:
public class Account{
#NotNull(message="This field should not be null")
private String username;
#NotBlank(message="This string should not be empty or null")
private String password;
private String name;
private String mail;
public void setUserName(String username){
this.username = username;
}
public String getUserName(){
return username;
}
[all other getter/Setter...]
}
http://silentwalker.wordpress.com/2009/04/07/custom-validation-in-hibernate/
You can also create your own annotations without using any framework and use them #prepersist or whatever. Basically the sky is the limit.
P.S Since you don't want to use any non internal code, here is how you can approach:
First, you can define an annotation
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface NotNull{
public String message() default "";
}
Then, before persisting the class, you would inspect its fields:
So, you have something like this:
Field[] classFields = yourObjectPrePersist.getClass().getDeclaredFields();
for (int i = 0; i < classFields.length; i++) {
classFields[i].setAccessible(true);//take notice that if you use a SecurityManager, you should work around it
if (classFields[i].getAnnotation(NotNull.class) != null) {
Object value = classFields[i].get(yourObjectPrePersist);
//here check if value is null or not and then return the message
if (value == null) {
throw new SomeException(((NotNull) classFields[i].getAnnotation(NotNull.class)).message());
}
}
}
Annotations don't seem like the right solution to the problem you describe.
One possibility would be to pass in the name of the field that needs to be validated, then used the java.beans.BeanInfo class to access the field values of the Entity. This will allow you to access arbitrary attributes of an object without having to deal with all of the complexity of reflection.
You can use reflection for this; see the documentation for java.lang.reflect.Field. The caller can pass in the field-name as a string, and validateField can use something like Field f = entity.getClass().getField(fieldName).
But this is not usually a great idea . . . reflection is awesome, but it requires a lot of boilerplate code, and you pay a high price in maintainability. (You also pay a performance penalty, though in my experience the maintainability penalty is much worse than the performance penalty.) Personally I use reflection a lot in test code (it can help in writing extremely comprehensive tests), and I make use of frameworks like Spring and Hibernate that depend on reflection, but I avoid it in my own production code.
You could use a generic interface
public interface Field {
public String getValue();
}
and call your validate method with anonymouse classes
validator.validate(new Field() {
return account.getUsername()
});
(or with old java)
validator.validate(new Field() {
public String getValue() {
return account.getUsername();
}
});
Otherwise what about using java.lang.Methode directly?
P.S.: Sorry for the quote, stackoverflow did not let me post my text directly, said it was not formatted correctly :-/
I am working on a struts2 project. I have created url with in my project and have passed parameters using tags. My question is how do i read the parameter in the actions? also if do the same would i be able to see the parameters as query string. i ask because i am not able to and i saw it in one of the tutorials.
Typically, you will interact with parameters in your actions by using fields on your actions, exposed by setters. Assume the following URL maps to my example Struts2 action:
URL
http://localhost/myAction?firstName=SonOfTheEARTh
Action Code
public class MyAction extends ActionSupport {
private String firstName;
public String execute() throws Exception {
// do something here
return SUCCESS;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(final String firstName) {
this.firstName = firstName;
}
}
JSP
Using Struts tags: <s:property value="firstName"/>
Using JSP EL/JSTL: ${action.firstName}
EDITED answer: It's based on naming conventions of your parameter. Take a look at this link and follow how they set "oldName" parameter.