Make one property in request body not required - java

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

Related

How to return required parameters as json response from pojo class in spring boot?

what I am trying to do is,
If I take one pojo class like
#Entity
#Table(name = "property_table")
public class Property {
#Id
#Column(name = "property_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int propertyId;
#Column(name = "property_name")
private String propertyName;
#Column(name = "property_type")
private String propertyType;
}
In RestController I wrote Two Methods like
#GetMapping(value = "/getProperties", produces = { "application/json",
"application/xml" }, consumes = { "application/xml", "application/json" })
#ResponseBody
public List<Property> getProperties() {
//some code
}
#GetMapping(value = "/getPropertyById", produces = { "application/json",
"application/xml" }, consumes = { "application/xml", "application/json" })
#ResponseBody
public Property getPropertyById() {
//some code
}
So, hear what I am trying to do is
for first api method I want return json like some parameters from Property pojo class i.e., like
for getProperties api method
{
"property":[
{
"propertyId":001,
"propertyName":"PROPERTY 1"
},
{
"propertyId":002,
"propertyName":"PROPERTY 2"
}
],
In the Above json I want to return only two parameters i.e propertyId,propertyName and remaining parameter i.e propertyType I dont want to retun in json.
How to return like that?
and for the second api method I want to return all three parameters. i.e., like below
for getPropertyById api method
{
"propertyId":001,
"propertyName":"PROPERTY 1",
"propertyType:"PROPERTY_TYPE 1"
},
how to maintain different json response using same pojo class with different parameters for different api methods.
please help me to solve this isuue.
Thanks.
REST API under/over-fetching is a well-known problem. There's only two (classical ways) to handle that.
The first one is to build one model per each attribute visibility state. So, in your case, you'll need to create two different models (this kind of models are called DTO - Data Transfert Object). One model will have a propertyType attribute, the other will not. The model Property you've shared shows that you use the same class as entity and as transfert object. This solution will add some complexity to your app because you will have to implement some mappers to convert your entity to a corresponding DTO.
The second one is to accept that you send an attribute that will not be useful (be aware of the over-fetching). This solution is often the most adopted one. The cons of this solution is when you don't want to send something to your client (imagine a User model, you want to get the password from your client but you don't want to sent it back to it). Another obvious negative point is that the transactions will be larger but it is negligible in most cases
I would strongly advice you to keep your #Entity isolated in the 'db' layer. So that changes on the database side don't affect your API and vice versa. Also, you will have much better control over what data is exposed in your API. For your needs you can create 2 true DTOs, like PropertyDto and PropertyDetailsDto (or using private fields and getters/setters).
public class PropertyDto {
public String propertyId;
public String propertyName;
}
public class PropertyDetailsDto extends PropertyDto {
public String propertyType;
}
Map your #Entity to a specific dto corresponding to your needs.
EDIT
public List<PropertyDto> getProperties() {
return toPropertyDtos(repository.findAll());
}
public PropertyDetailsDto getPropertyById(Long id) {
return toPropertyDetailsDto(repository.findBy(id));
}
in some Mapper.java
...
public static List<PropertyDto> toPropertyDtos(List<Property> properties) {
return properties.stream()
.map(Mapper::toPropertyDto)
.collect(toList());
}
private static PropertyDto toPropertyDto(Property property) {
PropertyDto dto = new PropertyDto();
dto.propertyId = property.propertyId;
dto.propertyName = property.propertyName;
return dto;
}
// same stuff for `toPropertyDetailsDto`, you could extract common mapping parts in a separate private method inside `Mapper`
...

Spring Bind #PathVariable to JavaBean

I try to bind an object in Spring controller so it can be used as #PathVariable. I want to do so, since there are some #PathVariable that I want to pass. I have tried the solution from Bind Path variables to a custom model object in spring and also Is it possible to bind path variable and request param into a single object?. But both are not working.
I have created something like this in my controller.
#RestController
#RequestMapping("/buildings")
#RequiredArgsConstructor
public class BuildingController {
private final BuildingService buildingService;
#GetMapping("/{buildingId}/floors/{floorId}/rooms/{roomId}/sections")
public Flux<SectionDTO> getRoomSections(BuildingRequestBean request) {
return this.buildingService.getRoomSections(request);
}
}
and BuildingRequestBean.java like this
#Getter
#Setter
public class BuildingRequestBean {
private String buildingId;
private String floorId;
private String roomId;
}
When I check BuildingRequestBean, the attributes is null when I call it with GET localhost:8080/buildings/a/floors/b/rooms/c/sections.
However, it will not null if I call it as #RequestParam, something like this GET localhost:8080/buildings/{buildingId}/floors/{floorId}/rooms/{roomId}/sections?buildingId=a&floorId=b&roomId=c
How to fix it so it will behave like #PathVariable rather than behave like #RequestParam?
U can get it by using #ModelAttribute
Try with this:
#GetMapping("/{buildingId}/floors/{floorId}/rooms/{roomId}/sections")
public Flux<SectionDTO> getRoomSections(#ModelAttribute BuildingRequestBean request) {
return this.buildingService.getRoomSections(request);
}
PathVariable must be added to the function parameter
Try this :
#GetMapping("/{buildingId}/floors/{floorId}/rooms/{roomId}/sections")
public Flux<SectionDTO> getRoomSections(#PathVariable String buildingId,#PathVariable String floorId ,#PathVariable String roomId) {

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

Do not serialize subtype properties with Jackson in Spring MVC 3

I am using Spring MVC with a controller like this:
#RequestMapping(value = "/list", method = RequestMethod.GET, produces = "application/json")
public #ResponseBody List<Service> list() {
return services.list();
}
The model is like this:
public class Service {
private User user;
...
}
public class User {
private String name;
...
}
public class ExtendedUser extends User {
private Location location;
...
}
For sure, an object of type ExtendedUser is created in the application and set in Service. When the controller /list answer the request, an object of type ExtendedUser is serialized despite the reference in Service class is User. I would like to know if there is some way with annotations to only serialize supertype (the referenced type) and avoid the subtype propierties.
Taking the example into account, I want a JSON without the location property to be returned.
Thanks in advance
Try #JsonInclude(Include.NON_NULL) on ExtendedUser
Your statement of "I want a JSON without the location property to be returned" can easily be accomplished using the #JsonIngore annotation:
public class ExtendedUser extends User {
#JsonIgnore
private Location location;
...
}
Is that what you're trying to do, though, just eliminate the location from the response, or does the actual type returned (type id) matter? If I'm off base, please post your expected JSON result and your actual JSON result.
I think this will do the trick:
#JsonSerialize(using=User.class)
See this related answer: https://stackoverflow.com/a/13926740/1292605
I recommend to use as property of #JsonSerialize. BTW, #JsonSerialize can be declared on the fields so that it will not impact the common behavior of serialization of User or ExtendedUser.
public class Service {
#JsonSerialize(as = User.class)
private User user;
...
}

spring RequestHeader annotation on model object?

Can I put #RequestHeader on a model object property? I.e.
class MyModel {
String ua;
public void setUa(#RequestHeader("User-Agent") String ua) {
this.ua = ua;
}
}
This works with #RequestParam, but seems to have no effect when I use #RequestHeader. Am I missing something? And, if this isn't supported, is there some reason why?
You can do this only in controller methods. Not model objects. Check the documentation

Categories

Resources