View Models in Servlets / MVC - java

I am building an application with simple Servlets and the MVC pattern. I am not using any framework like Spring.
I have some Model classes like this:
public class BlogPost {
private List<Comment> _comments;
// Things
}
and
public class Comment {
// Code
}
Posts can have zero or more comments associated with them in that collection.
However, I want to attach some additional information to the BlogPost Model before it is passed to the View, namely a value I set in a Cookie once a user makes a comment on a BlogPost. Strictly speaking, this is not a part of the BlogPost Model itself -- it is unrelated, incidental information, however I am not sure if I should make it easy on myself and just add it to the BlogPost class or do something to abstract this out a bit more.
So, should I add a field to the BlogPost class to handle this additional value, OR should I make a "View Model" along the lines of this which gets passed to the JSP view:
public class BlogPostView {
public BlogPostView(BlogPost bp, String message) {
// Constructor stuff, save these to instance variables
}
public BlogPost getBlogPost() { /* ... */ }
public String getMessage() { /* ... */ }
}

If BlogPost and your cookie data are unrelated, it is a bad idea to put the cookie data in your BlogPost class. The BlogPost class should represent what it's called - a blog post. It would be confusing to have other data associated.
Your second option of creating a class specifically to pass to the view is a better idea, though I'm curious to know why you need to pass the blog post and the cookie data as one object to your view? If you're using raw servlets:
request.setAttribute("blogPost",blogPost);
request.setAttribute("cookieData",cookieData);
Using a model class (e.g. Spring MVC ModelMap):
model.addAttribute("blogPost",blogPost);
model.addAttribute("cookieData",cookieData);
Your view will have access to both pieces of data, which you can manipulate using JSTL or other tag libraries.
If there's something I'm missing, can you elaborate more?

Create a HashMap model - and pass it along with the response to view.
model.put("blog", blog)
model.put("message", "some message")

Related

Rest Resources Separation

I've been trying to start a REST api with Spring Boot and I'm a bit strugling with the separation of my resources and which endpoint should be in which file.
Let's say we have an api enpoint to deal with a user and achievements from this user:
/user/{id} GET - to fetch user by id
/achievement/{id} GET - to fetch by achievement
Which are both in their separates resources file:
UserResource
#RestController
public class UserResource {
public UserResource() {...}
#GetMapping("/users/{id}")
public UserDTO getUser(String id) {
log.debug("REST request to get User : {}", login);
return userService.getUserWithAuthoritiesById(id).map(AdminUserDTO::new));
}
And AchievementResource
#RestController
public class AchievementResource {
public AchievementResource(...) {...}
#GetMapping("/achievements/{id}")
public ResponseEntity<Achievement> getAchievement(#PathVariable Long id) {
return achievementRepository.findById(id);
}
}
So far so good, pretty simple. My problem comes when I must get all achievements from a User. Naming covention says I should have an endpoint such as:
/user/{id}/achievements GET
But where should this endpoint be? I feel like both Resources could be good since for the UserResource, the root of the endpoint is the user, but the AchievementResource could be logical too since we are returning achievements.
Easy answer: you have the wrong problem
But where should this endpoint be?
The definition of the resource should be in your machine readable api definition. You produce the class files you need by feeding your definition into a code generator for your choice of language. The generator will put the classes it creates in files somewhere, and you leave them in this default arrangement until some point in the future when you have a compelling reason to arrange them differently (at which point, you fork the code generator and make your preferred design the default).
That said, when designing by hand there's nothing particularly special about "REST endpoints". The guidelines for where resource classes belong is no different from any other classes in Java....
That said, I find that the literature around file layout heuristics rather disappointing. There doesn't seem to be a lot of material discussing the trade offs of different designs, or contexts in which one choice might be more compelling than another.
For your specific situation, I would advise putting the new resource into a file of its own. The argument here being that your UserResource has User dependencies, and your AchievementsResource has achievements dependencies, but your new thing has both, and as a matter of (hand waves) principle, we should avoid bringing unneeded achievements dependencies into the namespace of the UserResource (and vice versa).
In other words, if we find ourselves adding imports to an existing file to implement a new thing, that's a hint that the new thing may be better placed somewhere else.
Using separate files also has nice mechanical advantages - it reduces merge collisions, each file will have its own source control history (meaning that the history of Users isn't cluttered with a bunch of commits that are exclusively about new thing). See Adam Tornhill's work over at CodeScene, for example.
As you separated the controllers, it is not wrong, you should classify the methods by their general entity, "if I need to recover the user's achievements", it is related to both, however, where does she get this data from? of the Achievements knowing that each achievement must have a relationship in the database with the user, you can very well look it up in the achievement controller with a List returnAchievementsByUser (Integer Id) method.
It depends on your point of view and the business behind the scene. You can use just one endpoint in many cases; if "users" are the main resources who have achievements, then "/users/{user-id}" and {users/{user-id}/achievements/{achievement-id} get the user by Id and special achievement of the user
#RestController
#RequestMapping("users")
public class UsersRestController{
#GetMapping("/{user-id}")
public UserDTO getUser(#PathVariable("user-id") String id) {
code...
}
#GetMapping("/{user-id}/achievements/{achievement-id}")
public AchievementDTO getAchievement(#PathVariable("user-id") String userId,
#PathVariable("achievement-id") String achievementId) {
code...
}
}
And if locating "achievements" on top of "users" in their entity hierarchy has meaning to you and your business, then /achievements/{achievement-id}/users/{user-id} can be a rest presentation:
#RestController
#RequestMapping("achievements")
public class AchievementsRestController{
#GetMapping("/{achievement-id}")
public UserDTO getAchievement(#PathVariable("achievements-id") String id) {
code
}
#GetMapping("/{achievements-id}/users/{user-id}")
public AchievementDTO getAchievement(#PathVariable("user-id") String userId,
#PathVariable("achievement-id") String achievementId) {
code
}
}
finally ,whenever they are not in an entity hierarchy, you can pass userId to
"/achievements/{achievements-id}" (or achievement-id to "/users/{user-id}") as a RequestParam.

Explanation of the model object in Spring

#Controller
public class GreetingController {
#GetMapping("/greeting")
public String greeting(HttpServletRequest request, Model model) {
String name = request.getParameter("name");
model.addAttribute("name", name);
return "greeting";
}
}
What does the Model do? Is this essentially the context variables that are passed to the "greeting.html" file? Is the only use of it addAttributes that will then be passed to the HTML template? Where could I read more about what Model does and how it should be used?
Did you see the documentation of Model?
Java-5-specific interface that defines a holder for model attributes. Primarily designed for adding attributes to the model. Allows for accessing the overall model as a java.util.Map.
Model is an essential part of MVC pattern which is widely used in Spring. As you have said, a Model is a holder of the context data passed by a Controller to be displayed on a View.
You can use only one Model which contains more data distinct with a unique key because the Model is based on the java.util.Map - as the documentation says..
Well, yes, that's what the Model do. But you can merge two Model objects parameters too, so this makes things easier.
More than it, you use ModelMap and ModelandView objects. If you want to read about it, take a look at: Spring MVC Model Map Model
In this case the model is used to pass data to the view. For example you are using JSP, or some template like thymeleaf, you can put thigns that you want to be displayed in the model, and you will be able to access it from those views.
You could read more about that in the Spring MVC Reference

Best way to pass a POJO to a spring MVC controller

I have done a few MVC controllers now and used the spring form tags to pass data back and forth but I realise now my actual understanding is a little thin. In my current case I could actually just send the response as url parameters but there are about 15 and I would prefer to send it as a pojo if possible.
My actual question... is ... is it possible to set up a spring style model attribute in a jsp without the attribute having been passed in and without using the form tags ?
So for example something along the lines of
//Pojo
Class personclass
{
private String name + getters and setters
private String address + getters and setters
private String phone + getters and setters
...
}
////first mvc call
#RequestMapping ("/")
Public ModelAndView LandingPage()
{
// no mention of Person pbject
Return mandvobject;
}
//jsp page
//This is the question!
SET ModelAttribute that wasn't passed in to the page
personclass = X
//New MVC call without a submit
window.open ("/NewMVCCall")
//New mvc call
#RequestMapping ("/NewMVCCall")
Public void newMVCPage(#ModelAttribute ("pc") personclass pc, Model model)
{
//process pc object
}
Or am I missing the point and I would have to send it as a json string parameter? Sorry my grasp of this is pretty rudimentary and I'm not sure whether I could quite easily set my own http form content or whether it is because I have used Spring form objects so far that I haven't grasped the complexity of what is going on behind the scenes (i.e form tags converting pojos to json and so on) ?
Many thanks if anyone has the time to set me on the right path...
I am not sure if I am understood your question correctly but you can link a Model to your controller without having to manually pass it to a the view every time you need it, spring will take care of that:
in your Controller :
public class MyController{
#ModelAttribute("pc")
public PersonneClass getPersonnelClass(){
return new PersonneClass();
}
#RequestMapping ("/NewMVCCall")
Public void newMVCPage(#ModelAttribute ("pc") personclass pc, Model model)
{
//process pc object
}
//other methods
}
It is a good practice to stick to java conventions when naming classes so
(personneClass ) must start with an uppercase (PersonneClass) .

JSP MVC Model 2 Architecture Question

I want to develop a web application and I have access this API. In the API there are methods that allow you to get the userId of the current user via context objects. Maybe I'm overthinking this, but I'm very confused as to where to put my CurrentUserId() method. Does that method go in the controller or the model? I was thinking it goes in the model, but it seems redundant to write a property called "getUserId" to return a string called getUserId().toString(). Is this normal and I'm overthinking or am I correct? My co-worker told me to put the logic in the view, but from everything I've read you never put java code or scriplets in the view. I hope this makes sense.
Also here's a method I wrote to return the userId as a string
protected String CurrentUserId(HttpServletRequest request)
{
ContextManager ctxMgr = ContextManagerFactory.getInstance();
Context ctx = ctxMgr.setContext(HttpServletRequest request);
Id userID = ctx.getUserId();
return userID.toString();
}
It should go to Controller.
Create a utility class having this method as static
Because here HttpServletRequest is this model specific(jsp,servlet) , suppose tomorrow if you want to apply the same model to your desktop application then it would fail so better place is controller.

Spring #ModelAttribute and translating request parameter binding names

I'm working on converting a legacy project to Spring (trying to adjust little as possible for now) and I'm running into a small issue with mapping/translating legacy parameters to a model attribute object. I may be completely wrong in thinking about this problem but it appears to me that to translate a parameter to a specific model attribute setter is to pass in the request parameter through a method for creating a model attribute and manually call the correct setter:
#ModelAttribute("form")
public MyForm createMyForm(#RequestParameter("legacy-param") legacy) {
MyForm myForm = new MyForm();
myForm.setNewParam(legacy);
return myForm;
}
I don't necessarily want to change the request parameter name yet since some javascript and JSPs are depending on it being named that way but is there any way to do something like this? Or is there a different way to map/translate request parameters to model attributes?
public class MyForm {
#ParameterName("legacy-param")
private String newParam;
public void setNewParam(String value) { ... }
public String getNewParam() { ... }
}
#Controller
public class MyController {
#RequestMapping("/a/url")
public String myMethod(#ModelAttribute("form") MyForm myForm, BindingResult result) { ... }
}
The way you've written that model attribute method is indeed odd. I'm not entirely clear what you're actually trying to do.Assuming there are many parameters, you're going to end up with an awful lot of instances of MyForm in your ModelMap. A more 'normal' way to create model attribute would be like this:
#ModelAttribute("legacyParamNotCamel")
public MyForm createMyForm(#RequestParameter("legacy-param-not-camel") String legacy) {
return legacy;
}
Then in the JSP you can refer to it directly in expression language. e.g.,
<c:out value="${legacyParamNotCamel}"/>
If you want to put them onto a form backing object, you need to do it all in a single method that creates the object, not make new copies of it in each method. (assuming your form has more than a single parameter associated with it.)
--
It seems like what you're really trying to do though is translate the parameter names in the request before the web data binder gets ahold of it, so that you can bind oddly named parameters onto a java bean? For that you'll need to use an interceptor that translates the names before the binding process begins, or make your own subclass of the databinder than can take a property name translation map.
You placed the #ModelAttribute at the Method Level but the intention seems to be more of a formBackingObject hence we should be dealing at the Method Parameter Level
There's a difference.
I put up an explanation here on my blog along examples at Spring 3 MVC: Using #ModelAttribute in Your JSPs at http://krams915.blogspot.com/2010/12/spring-3-mvc-using-modelattribute-in.html

Categories