Handle null values coming in request in Spring request handler - java

In Spring 3:
My Bean:
public class UserFormBean {
private String userEmail;
private String userMobNo;
public String getUserEmail() {
return userEmail;
}
public void setUserEmail(String userEmail) {
this.userEmail = userEmail;
}
public String getUserMobNo() {
return userMobNo;
}
public void setUserMobNo(String userMobNo) {
this.userMobNo = userMobNo;
}
}
And my request handler in Controller:
#RequestMapping(value = "/userData", method = RequestMethod.GET)
String userData(UserFormBean bean){
//code to handle incoming data
}
if user dosen't set any value in 'userEmail' and 'userMobNo' attributes at request time, spring by default sets null in these attributes and when i am getting value of these attributes in my request handler by String str = bean.getUserEmail(); it returns me null in double quote like "null".
Is there any way to convert these null values as "" (blank) when they contain null because i made an extra handler to handle these "null" values. Or some better idea to resolve this issue.
Thanks

I think the easiest way would be to initialize the properties to the empty String:
private String userEmail = "";
private String userMobNo = "";

I suspect the setter is called with "\"null\"" for the new value. Try this:
public void setUserEmail(final String newValue)
{
if (StringUtils.isNotBlank(newValue) &&
"\"null\"".equals(newValue))
{
userEmail = newValue;
}
else
{
userEmail = StringUtils.EMPTY;
}
}

You could define a standard for naming your "web methods", for example:
#RequestMapping(value = "/userData", method = RequestMethod.GET)
String webUserData(UserFormBean bean){
//code
}
When all your methods start with web*, then you can write an aspect that will initalize all arguments as you would like.
If you need help about creating and defining an aspect, just ask.
UPDATE: creating and defining an aspect
I will not write the code for you, because it's much better for you to learn by reading the documentation - you will get to know a lot of extra details.
That said, the steps to create an aspect are:
Enable AspectJ support - this step is done only once in your whole app
Declare an aspect - create a class that has an #Aspect annotation
Declare a pointcut inside your aspect - specify the point of interest in your code, around which the aspect will do some stuff, in your case, the web* methods
Declare an advice inside your aspect - #Before, #AfterReturning, #AfterThrowing, #After, #Around, here you will write your specific aspect code
In your aspect code you will have to access the current method arguments and do your logic for setting the empty string values for Strings.

Related

Make one property in request body not required

I have the following interface:
public interface TestRequestView {
String getCountryCode();
String getRegionCode();
}
It's used in several end points like so:
#PostMapping("/my/path/{id}")
public String test(#RequestBody TestRequestView request, #PathVariable String id) {
...
}
I would like to add a property to the TestRequestView that is only used in one new endpoint without breaking the rest, how can I mark that one property as ignorable? Something like:
public interface TestRequestView {
String getCountryCode();
String getRegionCode();
String getEmail(); // make this not required
}
Usually it is better to use 1 such a model per endpoint so they are independent. If you share models between endpoints this should be useful
This may help
You can use the #JsonIgnoreProperties annotation
Example:
#JsonIgnoreProperties(value = {"email"})
public interface TestRequestView {
String getCountryCode();
String getRegionCode();
#JsonProperty(required = false)
String getEmail();
}
Add annotation #JsonInclude(Include.NON_NULL) at class level for your TestRequestView. When passing your param you can pass it without that value and in your controller it will be received with that param as null. You just need to make sure that your controller can handle such case

POST request with RequestBody is not working

in the below example, i am using POST and GET methods. post is to initialize a varibale and GET is to get this varibale.
I use Postman to make the requests.
i am receiving an error
#RequestBody(value = "val") //cant resolve method value
please let me know how to fix the belwo error so i can use post method for initialization and get methdo to retrieve the value
Controller1
#Controller
#RequestMapping("/call1")
public class Call1 {
public String str = "inti";
#RequestMapping(value = "/intiparam1", method = RequestMethod.POST)
public void intiParam1(#RequestBody(value = "val") String val) {
this.str = val;
}
#RequestMapping(value = "/getparam1", method = RequestMethod.GET)
public String getParam1() {
return this.str;
}
}
Create a class Variable and use other code in controller.
class Variable {
String data= 'val';
}
#RequestMapping(value = "/intiparam1", method = RequestMethod.POST)
public void intiParam1(#RequestBody Variable val) {
this.str = val.data;
}
When making a request pass json as {"data":"12345"}
and then use #RequestBody Variable v in code instead of String as it will serve your purpose of default value and will make the code extensible as you can add different properties to the existing variable in future if needed.
When to use #RequestBody?
You can not use value with it. You can use this when you have multiple field entity which you want to perform the operation. Let's say you want to save the user then you may need to create User Model first and use in a controller #RequestBody.
Model:
public class User
{
#Id
private int id;
private String firstname;
private String lastname;
//Getters-Setters, AllArgConstructor-constructor
}
#RequestMapping(value = "/requestBodyExample", method = RequestMethod.POST)
public String intiParam1(#RequestBody User user) {
return user.getFirstname();
}
Quick Start with Spring Boot

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;
}
}

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.

Spring : Configure xml to make a controller return a view depending on a parameter

I have a spring MVC based application and I want to add a functionality in which some of my controllers will return the same view depending on the value of a parameter.
#RequestMapping("/someView")
public String returnView(Model model, HttpServletRequest request, String param){
if(param.equals("condition")){
return "commonView";
}
// do stuff
return "methodSpecificView";
}
Is there a way in which the first if condition can be configured in an xml? Since similar functionality needs to implemented in many controllers and I don't want to write boilerplate code an xml configuration can make things simpler.
Furthermore, if the first one is possible, can it be extended to eliminate the parameter param from request mapping method signature and put that in xml too?
You can use #RequestMapping:
#RequestMapping(value = {"/someView", "/anotherView", ...}, params = "name=condition")
public String returnCommonView(){
return "commonView";
}
In Spring 3.2 which is annotation based the below code snippet will give you an idea for your problem:
#RequestMapping("formSubmit.htm")
public String onformSubmit(#ModelAttribute("TestBean") TestBean testBean,BindingResult result, ModelMap model, HttpServletRequest request) {
String _result = null;
if (!result.hasErrors()) {
_result = performAction(request, dataStoreBean);//Method to perform action based on parameters recieved
}
if(testBean.getCondition()){
_result = "commonView";
}else{
_result = "methodSpecificView";
}
return _result;
}
TestBean//Class to hold all the required setters and getters
Explanation:
As the request from your view comes to this method the ModelAttribute reference will hold all the values from view if the condition is obtained from the view than you can directly obtain it from model attribute and return the corresponding view.
If your condition is obtained after applying certain logic than you can set the condition in the testBean and again get it to return the corresponding view.
You should consider implementing this via AOP - Around advice something like below.
#Around("#annotation(RequestMapping)") // modify the condition to include / exclude specific methods
public Object aroundAdvice(ProceedingJoinPoint joinpoint) throws Throwable {
Object args[] = joinpoint.getArgs();
String param = args[2]; // change the index as per convenience
if(param.equals("condition")){
return "commonView";
} else {
return joinpoint.proceed(); // this will execute the annotated method
}
}

Categories

Resources