I am creating a webservice in spring. I have a Params DTO which is nested in my OtherParentDTO's. Each request may contain only certain fields in the params Dto.
If the fields are present then I need to do a validation(basically null check). In the custom validator I ll specify which fields needs to be validated for a particular request. My problem is in the controller the error field is returned as params. Is there any way to change it to params.customerId or parmas.userId.
Update customer req:
{"params":{"customerId" : "b2cab997-df13-4cb0-8f67-4357b019bb96"}, "customer":{}}
Update user req:
{"params":{"userId" : "b2cab997-df13-4cb0-8f67-4357b019bb96"}, "user":{}}
#JsonSerialize(include = Inclusion.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class Params {
private String customerId;
private String userId;
//setter and getter are there
}
public class UpdateCustomerRequestDTO {
#NotNull
#IsValid(params = {"customerId"})
protected Params params;
#NotNull #Valid
private Customer customer;
}
public class UpdateUserRequestDTO {
#NotNull
#IsValid(params = {"userId"})
protected Params params;
#NotNull #Valid
private User user;
}
Custom constrain validator
#Constraint(validatedBy = {RequestParamsValidator.class})
#Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface IsValid {
String[] params() default "";
String message() default "{com.test.controller.validator.IsValid.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class RequestParamsValidator implements ConstraintValidator<IsValid, Params> {
/* (non-Javadoc)
* #see javax.validation.ConstraintValidator#initialize(java.lang.annotation.Annotation)
*/
#Override
public void initialize(IsValid constraintAnnotation) {
validateItems = constraintAnnotation.params();
}
/* (non-Javadoc)
* #see javax.validation.ConstraintValidator#isValid(java.lang.Object, javax.validation.ConstraintValidatorContext)
*/
#Override
public boolean isValid(Params value, ConstraintValidatorContext context) {
try {
for (String reqItem : validateItems) {
final Object curObj = PropertyUtils.getProperty(value, reqItem);
if (curObj == null || curObj.toString().isEmpty()) {
return false;
}
}
} catch (final Exception ignore) {
// ignore
}
return true;
}
}
Controller
#RequestMapping(method = RequestMethod.POST, value="", produces="application/json")
public #ResponseBody BaseResponseDTO updateCustomer(#RequestBody #Valid UpdateCustomerRequestDTO requestDTO,
BindingResult result) throws Exception {
if (result.hasErrors()) {
log.error("[Field] "+result.getFieldError().getField()+" [Message]"+ result.getFieldError().getDefaultMessage())
// But here the result.getFieldError().getField() is returning params. Is there any way with which I can change it to params.customerId/parmas.userId
return false
}
// add customer logic
}
Related
I am using Spring Boot to return data from a Repository. I would like to format my JSON so that it plays nicely with ExtJS' ajax handling. Essentially I would like to include properties to handle success/failure, count, and errorMsg along with a List of data from the repository.
I have tried by creating a ResponseDTO object that I'm returning from my Rest Controller.
#RestController
public class AdminController {
private static final Logger logger = LogManager.getLogger(AdminController.class);
#Autowired
private UserService userService;
#Autowired
private SecurityService securityService;
#Autowired
private UserValidator userValidator;
#GetMapping("/searchUsers")
public ResponseDTO searchUsers(String name, String active) {
int activeFlag;
List<User> users;
ResponseDTO resp;
if(active.equals("true")) {
activeFlag = 1;
} else activeFlag=0;
if(StringUtils.isEmpty(name)) {
users= userService.findAllUsers(activeFlag);
} else {
users= userService.findByUsernameActive(name, activeFlag);
}
return new ResponseDTO(users, true);
}
}
Here's my DTO that I use in the controller:
public class ResponseDTO {
private boolean success;
private int count = 0;
private List<?> values;
public boolean getSuccess() {
return this.success;
}
public void setState(boolean st) {
this.success=st;
}
public int getCount() {
return this.count;
}
public void setCount(int cnt) {
this.count=cnt;
}
public List<?>getValues() {
return this.values;
}
public void setValues(List<?> vals) {
this.values = vals;
}
public ResponseDTO(List<?> items, boolean state) {
this.success = state;
values = items;
this.count = items.size();
}
}
Here's what the JSON I get back looks like:
{
"ResponseDTO": {
"success":true,
"count":2,
"values":[{obj1 } , { obj2}]
}
}
what I would like to get is something more like:
{
"success" : true,
"count" : 2,
"values" [{obj1},{obj2}]
}
I'm using Spring Boot and Jackson annotations. I have used an annotation to ignore individual fields in the objects in the results array, but I can't find a way to unwrap the ResponseDTO object to not include the class name.
When you serialize ResponseDTO POJO, you should not get 'ResponseDTO' in the response by default. Because, the root wrap feature is disabled by default. See the doc here. If you have the below code, please remove it.
mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
I would like to check if the user entered the country is in the list of properties.
public class CountryValidator implements ConstraintValidator<CountryValid,String> {
#Value("#{countryOptions}")
Map<String, String> countryOptions;
#Override
public boolean isValid(String girilenDeger, ConstraintValidatorContext arg1) {
// TODO Auto-generated method stub
return countryOptions.containsKey(girilenDeger);
}
#Override
public void initialize(CountryValid constraintAnnotation) {
// TODO Auto-generated method stub
ConstraintValidator.super.initialize(constraintAnnotation);
}
}
However, I have successfully used this list before in the controller class. I get NullPointerException error when I use it again in my validation class.
#Controller#RequestMapping("/customerForm")
public class CustomerController {
#Value("#{countryOptions}")
Map<String, String> countryOptions;
#RequestMapping("/mainMenu")
public String returnForm(Model model) {
model.addAttribute("theCountryOptions", countryOptions);
Customer customer1 = new Customer();
model.addAttribute("customer1", customer1);
return "customer-view/main-menu";
}
#RequestMapping("/resultPage")
public String returnResult(#Valid #ModelAttribute("customer1") Customer customer, BindingResult result,
Model model) {
model.addAttribute("theCountryOptions", countryOptions);
if (result.hasErrors())
return "customer-view/main-menu";
else {
AddDatabase<Customer> database = new AddDatabase<Customer>();
database.setObj(customer);
database.addData();
System.out.println("Ekleme islemi tamamlandı.");
return "customer-view/result-page";
}
}
}
Or can I retrieve theCountryOptions attribute from the model?
I am trying to convert / serialize the #Id field (which is not a string) of the model class but keep getting this error. The custom ID class just concatenates two values with a colon, e.g. aaaa:2345.
org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [MyIdClass] to type [byte[]]
This is my model class.
#RedisHash("alert")
public class MyClass implements Serializable
{
public static class MyIdClass
{
public String userId;
public Long sessionExpiry;
public MyIdClass()
{
}
public MyIdClass(String id, Long ex)
{
userId = id;
sessionExpiry = ex;
}
}
public static class MyIdClassSerializer implements RedisSerializer<MyIdClass>
{
#Nullable
#Override
public byte[] serialize(#Nullable MyIdClass uid) throws SerializationException
{
return String.format("%s:%d", uid.userId, uid.sessionExpiry).getBytes();
}
#Nullable
#Override
public MyIdClass deserialize(#Nullable byte[] bytes) throws SerializationException
{
String[] t = new String(bytes).split(":");
return new MyIdClass(t[0], Long.parseLong(t[1]));
}
}
#Component
#ReadingConverter
public static class MyIdClassReader implements Converter<byte[], MyIdClass>
{
#Nullable
#Override
public MyIdClass convert(byte[] source)
{
String[] t = new String(source).split(":");
return new MyIdClass(t[0], Long.parseLong(t[1]));
}
}
#Component
#WritingConverter
public static class MyIdClassWriter implements Converter<MyIdClass, byte[]>
{
#Nullable
#Override
public byte[] convert(MyIdClass uid)
{
return String.format("%s:%d", uid.userId, uid.sessionExpiry).getBytes();
}
}
/**
* User ID
* Key := 'alert:' userId ':' sessionExpiry
*/
#Id
public MyIdClass id;
public String value;
}
Here's how I'm configuring the RedisTemplate.
#Bean("redisTemplateActivityAlert")
public RedisTemplate<ActivityAlert.UserIdExpiry, ActivityAlert> redisTemplateActivityAlert()
{
RedisTemplate<ActivityAlert.UserIdExpiry, ActivityAlert> template = new RedisTemplate<>();
template.setConnectionFactory(jedisConnectionFactory());
template.setKeySerializer(new ActivityAlert.UserIdExpirySerializer());
template.setHashKeySerializer(new ActivityAlert.UserIdExpirySerializer());
return template;
}
I've read other posts about configuring ConversionService or TypeConverter but haven't gone far with them.
add this Bean to your RedisConfiguration :
#Bean
public RedisCustomConversions redisCustomConversions(MyIdClassReader myIdClassReader , MyIdClassWriter myIdClassWriter ) {
return new RedisCustomConversions(Arrays.asList(myIdClassWriter,myIdClassReader));
}
How to fetch data from corda Custom tables?
my sample code is as follows :-
Api layer -- getIous() method
{
Field attributeValue=IOUSchemaV1.PersistentIOU.class.getDeclaredField("value");
CriteriaExpression currencyIndex = Builder.equal(attributeValue, "12");
QueryCriteria.VaultCustomQueryCriteria criteria = new
QueryCriteria.VaultCustomQueryCriteria(currencyIndex);
vaultStates = services.vaultQueryByCriteria(criteria,IOUState.class);
}
In ExamplePlugin I added below code for schema registration
public class ExamplePlugin extends CordaPluginRegistry implements
WebServerPluginRegistry
{
#NotNull
#Override
public Set<MappedSchema> getRequiredSchemas()
{
Set<MappedSchema> requiredSchemas = new HashSet<>();
requiredSchemas.add(new IOUSchemaV1());
return requiredSchemas;
}
}
My Schema classes are ---
public final class IOUSchema {
}
#CordaSerializable
public class IOUSchemaV1 extends MappedSchema
{
public IOUSchemaV1() {
super(IOUSchema.class, 1, ImmutableList.of(PersistentIOU.class));
}
#Entity
#Table(name = "iou_states")
public static class PersistentIOU extends PersistentState {
#Column(name = "sender_name") private final String senderName;
#Column(name = "recipient_name") private final String recipientName;
#Column(name = "value") private final int value;
public PersistentIOU(String senderName, String recipientName, int value) {
this.senderName = senderName;
this.recipientName = recipientName;
this.value = value;
}
public String getSenderName() {
return senderName;
}
public String getRecipientName() {
return recipientName;
}
public int getValue() {
return value;
}
}
}
my state has :-
public class IOUState implements LinearState, QueryableState
{
--- some code goes here and below methods as well.---
#Override
public PersistentState generateMappedObject(MappedSchema schema) {
if (schema instanceof IOUSchemaV1) {
return new IOUSchemaV1.PersistentIOU(
this.sender.getName().toString(),
this.recipient.getName().toString(),
this.iou.getValue());
} else {
throw new IllegalArgumentException("Unrecognised schema $schema");
}
}
#Override
public Iterable<MappedSchema> supportedSchemas() {
return ImmutableList.of(new IOUSchemaV1());
}
}
But all the time i am getting below exception.
Caused by: net.corda.core.node.services.VaultQueryException:
Please register the entity 'com.example.schema.IOUSchemaV1' class in your CorDapp's CordaPluginRegistry configuration (requiredSchemas attribute)
and ensure you have declared (in supportedSchemas()) and mapped (in generateMappedObject())
the schema in the associated contract state's QueryableState interface implementation.
Can anyone please help to resolve this.
Try deleting implements WebServerPluginRegistry from your plugin declaration.
My Java Class is
public class User {
private List<UserInfo> userInfoList;
public class UserInfo {
private String id;
}
}
Let's assume it has getter, setter method.
json is
{"userInfoList" : [{"id":"a", "id":"b"}]}
I tried to deserialize it like below.
objectMapper.readValue(json, User.class);
But it throws error.
Can not construct instance of User$UserInfoList: no suitable constructor found
How to deserialize it?
I think you should make UserInfo static. Jackson cannot construct the UserInfo class.
I tried with that change and it works for me :
public class User {
private List<UserInfo> userInfoList;
public static class UserInfo {
private String id;
public UserInfo() {
super();
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
public List<UserInfo> getUserInfoList() {
return userInfoList;
}
public void setUserInfoList(List<UserInfo> userInfoList) {
this.userInfoList = userInfoList;
}
}
And :
public class App {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
User.UserInfo ui1 = new User.UserInfo();
ui1.setId("a");
User.UserInfo ui2 = new User.UserInfo();
ui2.setId("b");
List<User.UserInfo> userInfoList = new ArrayList<User.UserInfo>();
userInfoList.add(ui1);
userInfoList.add(ui2);
User user = new User();
user.setUserInfoList(userInfoList);
System.out.println(mapper.writeValueAsString(user));
user = mapper.readValue(mapper.writeValueAsString(user), User.class);
}
}