Spring mvc - Use modelAttribute inside method instead of method argument as annotation - java

Following is a code snippet where we can use #ModelAttribute at method parameter level
#ReqestMapping(value = useruri)
public void submitInfo(#ModelAttribute User user) {
// Business logic
}
#ReqestMapping(value = personuri)
public void submitInfo(#ModelAttribute Person person) {
// Business logic
}
Can we do like following?
#RequestMapping(value = genericuri)
public void submitInfo(HttpServletRequest request, #PathVariable String type) {
if (type.equals("user")) {
User user = someSpringMvcMethod(request, User.class)
} else if (type.equals("person")) {
Person person = someSpringMvcMethod(request, Person.class)
}
//Business logic
}
Reason is, I am expecting different type of submitted data based on a type and I want to write a generic controller since only difference is conversion of request data to specific java class.
User and Person class has lot of different data and I don't think I can use inheritance/polymorphism to solve my use-case here

I don't recommend such a thing.
Look here
if (type.equals("user")) {
User user = someSpringMvcMethod(request, User.class)
} else if (type.equals("person")) {
Person person = someSpringMvcMethod(request, Person.class)
}
This is already wrong, imho. A single method managing multiple models.
What if you need another model's type? Another if branch.
For example, this is a lot better
#ReqestMapping("base-path/user")
public void submitInfo(#ModelAttribute final User user) {
commonLogic(user.valueOne, user.valueTwo);
}
#ReqestMapping("base-path/person")
public void submitInfo(#ModelAttribute final Person person) {
commonLogic(person.valueOne, person.valueTwo);
}
private void commonLogic(final String one, final String two) {
... // Business logic
}
commonLogic manages the common business logic between the models' types.
It centralizes the work.
You can even place commonLogic in a Service, which is where it should go anyway.

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`
...

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

Conditional property serialization with Jackson and Jersey

I'm unsuccessfully trying to conditionally and dynamically pick which property to serialize to respond to each request with Jersey (using Jackson). The idea behind this is to securely access to properties of objects within a REST API.
I have several objects that I return in API calls that should show/hide fields depending in the user who is authenticated.
For example, lets say I have an object Car
public class Car implements Serializable {
private Long id;
private String VIN;
private String color;
...
}
Lets say that if an user with the ROLE_ADMIN is authenticated, all properties should be returned, but if there isn't a logged user only the first two need to be shown.
I was thinking on building something that's annotation based. Something like:
public class Car implements Serializable {
private Long id;
private String VIN;
#Secured({AccessRole.ROLE_ADMIN})
private String color;
...
}
In this case, the color property should only be returned if the access role of the requesting user matches the ones passed via the annotation.
But I'm unable to get a hook on where should I implement this logic.
What I'm trying to implement is a sort of #JsonIgnore but that's conditional and dynamic. All solutions I found so far are static.
Is this even possible?
Jersey has support for Entity Filtering. Aside from general filtering, it also supports Role-based Entity Filtering using (javax.annotation.security) annotations.
So you can use the #RolesAllowed, #PermitAll, and #DenyAll annotations on the domain model properties
public static class Model {
private String secured;
#RolesAllowed({"ADMIN"})
public String getSecured() { return this.secured; }
}
To make this work though, you need to have set the SecurityContext inside of a request filter. Jersey will look up the SecurityContext to validate the roles. You can read more about it in this post (Note: the entity filtering is separate from any real authorization that is mentioned in that post. But the post does explain about the SecurityContext).
Basically you will have something like (notice the last line where you set the SecurityContext).
#PreMatching
public static class SimpleAuthFilter implements ContainerRequestFilter {
private static final Map<String, User> userStore = new ConcurrentHashMap<>();
static {
userStore.put("peeskillet", new User("peeskillet", Arrays.asList("ADMIN", "USER")));
userStore.put("paulski", new User("paulski", Arrays.asList("USER")));
}
#Override
public void filter(ContainerRequestContext request) throws IOException {
final String authHeader = request.getHeaderString("Authorization");
final String username = authHeader.split("=")[1];
final User user = userStore.get(username);
if (user == null) {
throw new NotAuthorizedException("No good.");
}
request.setSecurityContext(new SimpleSecurityContext(user));
}
}
Where the SimpleSecurityContext is just a class of your own, where you need to override the isUserInRole method and check if the user has the role
private static class SimpleSecurityContext implements SecurityContext {
private final User user;
SimpleSecurityContext(User user) {
this.user = user;
}
#Override
public Principal getUserPrincipal() {
return new Principal() {
#Override
public String getName() {
return user.getUsername();
}
};
}
#Override
public boolean isUserInRole(String role) {
return user.getRoles().contains(role);
}
#Override
public boolean isSecure() {
return false;
}
#Override
public String getAuthenticationScheme() {
return "simple";
}
}
That's pretty much it. You will also need to register the SecurityEntityFilteringFeature with the application to make it all work.
See a complete test case in this Gist
You can register a custom MessageBodyWriter https://jersey.java.net/documentation/latest/user-guide.html#d0e6951
The MessageBodyWriter will use your custom logic to decide what to write.
It can be done with #JsonView as #dnault suggested.
http://www.baeldung.com/jackson-json-view-annotation
Your MessageBodyWriter will hold a jackson mapper and you will apply the writerWithView with the matching view class as described in the above link.
EDIT: see this one - Jackson Json serialization: exclude property respect to the role of the logged user

Implementing Paging and Sorting with Domain Driven Design

This is with respect to my solution of implementing Paging & Sorting in Domain Driven Design with intention of not to pollute the Domain models and repository contracts,
Base class for REST Request
public class AbstractQueryRequest {
...
private int startIndex;
private int offset;
...
}
Interceptor to retrieve the Query Meta data and store it in ThreadLocal container
public class QueryInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
...
QueryMetaData.instance().setPageMetaData(/*Create and set the pageable*/);
}
}
Container for Query Meta data
public class QueryMetaData {
private static final ThreadLocal<QueryMetaData> instance = new ThreadLocal<>() {
protected QueryMetaData initialValue() {
return new QueryMetaData();
}
};
public static QueryMetaData instance() {
return instance.get();
}
private ThreadLocal<Pageable> pageMetadata = new ThreadLocal<>();
public void setPageMetaData(Pageable pageable) {
pageMetadata.set(pageable);
}
public Pageable getPageMetaData() {
return pageMetadata.get();
}
//clear threadlocal
}
I intend to retrieve this ThreadLocal value in the repository, if available use it with the queries to the datastore.
I hope this may not be a very dirty solution, but want to know is there better widely used pattern for this.
don't use your solution. It's quite complex and will be hard to debug and maintain. If you start using async methods it won't even work.
I don't understand why you can't have sorting/paging properties in your repository methods? If you want a cleaner solution you might create a new object which carries the sort and paging settings.
When you want to fetch data you typically do it through your application services. And those can use your repositories directly before converting the result to DTOs.
then you got something like:
public class YourAppService
{
public YourAppService(ISomeRepository repos)
{
}
public IList<UserDto> ListUsers(string sortColumn = "", bool ascending = true, int pageNumber = 1, pageSize = 30)
{
var querySettings = new QuerySettings(sortcolumn, ascending, pageNumber, pageSize);
var users = _repos.FindAll(querySettings);
return ConvertToDto(users);
}
}
you can read more about sorting and paging in my article here:
http://blog.gauffin.org/2013/01/11/repository-pattern-done-right/

"Chaining" two controllers?

A REST-API I am developing allows access to various kinds of user data.
Users can be identified via their Id, their email or their username. A user then has a couple of other data such as orders etc.
Now I am planning to expose the following endpoints:
/rest/user/byemail/test#example.org/profile
/rest/user/byemail/test#example.org/orders
/rest/user/byemail/test#example.org/address
/rest/user/byid/123456/profile
/rest/user/byid/123456/orders
/rest/user/byid/123456/address
/rest/user/byusername/test/profile
/rest/user/byusername/test/orders
/rest/user/byusername/test/address
As you can see, the URL always consists of two "parts": One for identifying the user and the other one for identifying the resource.
It would be great if I could now avoid writing 9 different methods in my controller (as there might be other types of information in the future).
Is it somehow possible to write one UserController which is then returned and parametrized by the MainController?
public class UserController {
#RequestMapping("/profile")
public ModelAndView profile(User user) {
//...
}
#RequestMapping("/orders")
public ModelAndView profile(User user) {
//...
}
#RequestMapping("/address")
public ModelAndView profile(User user) {
//...
}
}
#RequestMapping("/rest/user")
public class MainController {
#RequestMapping("byemail/{email}")
public ??? byEmail(#PathVariable String email) {
User user = //Find user by email
//???
}
#RequestMapping("byusername/{username}")
public ??? byUserName(#PathVariable String username) {
User user = //Find user by username
//???
}
#RequestMapping("byid/{id}")
public ??? byId(#PathVariable String id) {
User user = //Find user by id
//???
}
}
Or is it maybe possible to solve this via some kind of routing?
However, it would be important to "split" the URL and use one half to find the correct user which will then be available as a parameter when parsing the second half.
Why not just one controller with these request mappings?
#RequestMapping("/rest/user")
public class UserController {
#RequestMapping("{keyType}/{key}/orders")
public ModelAndView orders(#PathVariable String keyType, #PathVariable String key) {
User u = findUser(keyType, key);
// ...
}
#RequestMapping("{keyType}/{key}/profile")
public ModelAndView profile(#PathVariable String keyType, #PathVariable String key) {
User u = findUser(keyType, key);
// ...
}
#RequestMapping("{keyType}/{key}/address")
public ModelAndView address(#PathVariable String keyType, #PathVariable String key) {
User u = findUser(keyType, key);
// ...
}
private User findUser(String keyType, String key) {
// ...
}
}
Take a look at Spring Data REST.
This API exposes a SimpleJpaRespository to the web, accessible via RESTful paths.

Categories

Resources