Thymeleaf - Verify if preperty already exists - spring

Basically my question is very simple, I have a registration form with a constraint on field "login".
Table(uniqueConstraints=#UniqueConstraint(columnNames = { "login" }))
So, when I add a login which is already exists, an exception shows up: Duplicate entry 'MyLogin' for key 'UKew1hvam8uwaknuaellwhqchhb'.
I'm asking if there's any way in thymeleaf to just show a message saying that name already exists.

I would check if user already exists in table with the same username(I think your "login" is same as username for the user) before making any persistence transaction for new user, if username already exists in the table simply do not go forward and execute new user registration logic and let form controller return view with the Model object with the duplicate username attribute returned by service method.(you can use exceptions in service methods for better logic)
#RequestMapping("/registerNewUser")
public String showModel(#ModelAttribute UserDataTransferObject userDTO, Model model){
String existedUsername = service.createUser(userDTO);
if(existedUsername != null){
model.addAttribute("existedUsername",existedUsername);
}
return "registrationstatus";
}
createUser method checks if repository contains entry with the same username and if so returns that username as a string (Simple implementation).
in thymeleaf:
<div th:if="${existedUsername != null}" class="alert alert-danger notification" role="alert">
<p th:text="${existedUsername}"></p><p> already exists</p>
</div>

Related

Spring Boot Post Request Method not having all Object values

Using Spring Boot, Hibernate JPA and Thymeleaf.
I have an Order database table which currently only holds 1 record. This record has a few columns and some of the columns are not seen on any forms, they are set upon saving the Order, for instance the creation date.
On the GET request below I select the specific Order and all values are returned into the Order object as expected.
This is my GET Request method:
#RequestMapping(value = "/editorder/{orderId}", method = RequestMethod.GET)
public String editOrderGet(Model model, #PathVariable long orderId)
{
Order order = orderService.findById(orderId);
model.addAttribute("order", order);
return "/editorder";
}
This is a small snippit of my edit order html form using Thymeleaf, binding the Order object to the form using th:object as below:
<form role="form" th:action="#{/editorder}" th:object="${order}" method="post">
<input type="hidden" th:field="*{orderId}"/>
<button type="submit" class="btn btn-primary">Update Order</button>
.
.
</form>
And this is my POST Request method:
#RequestMapping(value = "/editorder", method = RequestMethod.POST)
public String editOrderPost(Model model,
#Valid #ModelAttribute("order") Order order, BindingResult bindingResult)
{
//rest of code here
}
As you can see, on the GET request I am adding the Order object to the model.
On the html form, I am binding the Order object to the entire form. Then on the POST request I am getting the Order object.
But on the POST it is seen as a new Order and only contains the fields as specified in the form, it does for instance not contain the creation date as seen in the GET request.
My question is this:
Am I missing something or do I explicitly need to go set each of those fields as hidden fields on my form?
In your GET response you may be returning the whole Order object into the Model, but Thymeleaf when trying to build the actual html from template will pick only the items it needs to build the template. So only the fields that are used in the form are used to build the form in your html page.
So when u resubmit the form to the POST service only those fields that are available in the form is reposted.
If u want these fields to be displayed on the page then add these fields in the Form. Thymeleaf picks them and displays in the form. If you dont want them to be shown in the Page then just ignore them. The Order object which u receive in the POST would not have that fields as they were not available in original form.
U can get them by querying the database, any how you do have the order id saved as the Hidden field.
public String editOrderPost(Model model,
#Valid #ModelAttribute("order") Order order, BindingResult bindingResult){
Order orderFromDB = orderService.findById(order.getId());
// Code to update the orderFromDB from order object
orderService.save(order);
}
This will save the updated fields to the database.
Generally its not a good practice to expose the Entity objects to the API. Try using a DTO/value object. This can have only fields that define your business fields. Also u can use BeanMapper frameworks like dozer/mapstruct/modelmapper to copy from DTO to Entity and vice versa.

How to control two operations for same url in Spring mvc?

Consider the following problem.
The user has chosen to create a document by clicking on the Create document and then he writes data into the document. The url for creating the document is /document/save.
For the subsequent write up, the existing document must be saved instead of creating a new one.
Here is my code for that.
#Controller
public MyController implements Controller, TemplateTypeAware
{
#RequestMapping("/document/save")
public String saveOrCreateDocument(#ModelAttribute DocumentWrapper wrapper, ModelAndView m)
{
if(m.getModel().get("document_id")==null)
{
Document doc=createDocument(wrapper);
m.addObject("document_id",doc.getId());
}
else
{
saveDocument(m.getModel().get("document_id"), wrapper);
}
return documentView;
}
}
Template:
<form>
<input type="hidden" name="document_id" th:value="*{document_id}"/>
<!-- other fields -->
</form>
The problem here is, I am getting document_id always null. Is there any work around for this problem?
Thanks in advance. Hope you will reply as soon as possible.
Form fields will be automatically bound to your DocumentWrapper fields if they have matching names, that means DocumentWrapper needs a field named document_id, otherwise the document_id request parameter won't be bound to your object.
Model attributes will be exposed to the view, at this point the model will be empty, you can add attributes in your handler method and they will become in your view, but request parameters won't be in the model. That explains why you always get null.
If you just need the document_id parameter, use #RequestParam:
#RequestMapping("/document/save")
public String saveOrCreateDocument(#RequestParam("document_id") Long documentId, Model m) {
...
}
Please refer to the binding section of Spring MVC: http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-requestparam

Spring + Hibernate Update Password

I have an administration panel where i can add users.
In insert form i have USERNAME, PASSWORD (no confirm-password), and some ordinary fields like name, surname,....
When i insert a new user, i want that hibernate will validate it:
username not null
unique username in db (with an unique index on table)
password not null (for now, no length controls)
until here, everything is working fine.
As in future i will store encrypted passwords,
in Edit form, i have normal editable fields like username, name,
but i didn't put password. instead i put new password field just in case i want to change it (not mandatory).
my edit form:
<!--
- username
-->
<div class="form-group">
<label class="col-sm-4 control-label">Username*:</label>
<div class="col-sm-8" ><input class="form-control" type="text" data-th-field="*{username}" placeholder="Username" th:errorclass="'has-error-input'"></input></div>
</div>
<!--
- password
- fingo password
-->
<div class="form-group">
<label class="col-sm-4 control-label">Password:</label>
<div class="col-sm-8" >******</div>
</div>
<!--
- new password
- (if i want to change it)
-->
<div class="form-group">
<label class="col-sm-4 control-label">New Password:</label>
<div class="col-sm-8" ><input class="form-control" type="password" id="password_new" name="password_new" placeholder="New Password"></input></div>
</div>
As you can see the password_new field is a normal tag, without using spring notation. I will retrieve this as parameter in controller and i will check if new password was written or no.
Now the problem: when i submit the form, i get validation error, as password is null.
I'm asking you this:
am i on a good way to do this?
is it possible skip a validation (password) in hibernate on update,
but set as mandatory on insert? and then update all fields except
password (you will see that i commented the line
userToUpdate.setPassword(user.getPassword());)?
could be better remove new password field from edit form, and change
password in a page where i update only the password?
is it a stupid / unsafe idea to set an hidden field called 'password' in edit form containing the encrypted password, so it will not give me validation errors? thinking for a second, i think it's really not a good idea, as someone can see encrypted password just looking the source code of the page. having a encrypted password "in clear" can be dangerous / unsafe?
User.java (model)
#NotNull(message = "PASSWORD cannot be null")
#NotEmpty(message = "PASSWORD cannot be null")
#Size(max = 50, message = "Max 50 char")
#Column(name = "password", length = 50)
private String password;
UserDao.java
#Override
public void updateUser(User user) throws UserNotFoundException {
User userToUpdate = getUser(user.getId());
userToUpdate.setUsername(user.getUsername());
//userToUpdate.setPassword(user.getPassword());
userToUpdate.setNome(user.getNome());
userToUpdate.setCognome(user.getCognome());
userToUpdate.setEmail(user.getEmail());
userToUpdate.setEnabled(user.getEnabled());
userToUpdate.setRole(user.getRole());
getCurrentSession().update(userToUpdate);
}
UserController.java
#RequestMapping(value = "/edit", method = RequestMethod.POST)
public String editingUser(#Valid #ModelAttribute User user,
BindingResult result, RedirectAttributes redirectAttrs,
#RequestParam(value = "action", required = true) String action,
#RequestParam(value = "password_new") String password_new
){
logger.info("IN: User/edit-POST: " + action);
List<Role> user_roles = RoleService.getRoles();
if (action.equals("abort"))
{
/**
* message
*/
String message = "Edit not saved";
redirectAttrs.addFlashAttribute("message", message);
redirectAttrs.addFlashAttribute("message_class", "alert-warning");
redirectAttrs.addFlashAttribute("user_roles", user_roles);
}
else if (result.hasErrors())
{
logger.info("User-edit error: " + result.toString());
redirectAttrs.addFlashAttribute("org.springframework.validation.BindingResult.user", result);
redirectAttrs.addFlashAttribute("user", user);
/**
* as in my list page i have also the insert form,
* i need to populate the select
*/
redirectAttrs.addFlashAttribute("user_roles", user_roles);
return "redirect:/users/edit?id=" + user.getId();
}
else if (action.equals("save"))
{
try
{
logger.info("User/edit-POST: " + user.toString());
/**
* i see if i changed the password
*/
logger.info("new password: " + password_new);
if (!password_new.equals(""))
{
user.setPassword(password_new);
}
else
{
//nothing, or what?
}
/**
* now i can update the user in DB
*/
UserService.updateUser(user);
/**
* message
*/
String message = "User edited with success";
redirectAttrs.addFlashAttribute("message", message);
redirectAttrs.addFlashAttribute("message_class", "alert-success");
/**
* as in my list page i have also the insert form,
* i need to populate the select
*/
redirectAttrs.addFlashAttribute("user_roles", user_roles);
}
catch (UserNotFoundException e)
{
logger.info("User/edit-POST: id [" + user.getId() + "] not found");
String message = "User id [" + user.getId() + "] not found!";
redirectAttrs.addFlashAttribute("message", message);
redirectAttrs.addFlashAttribute("message_class", "alert-danger");
}
}
return "redirect:/users/list";
}
thank you very much
PS.
As it's my first application in spring / hibernate / java word, for now i'm storing plain passwords, in future i will encrypt them.
Sure!
Only if you remove the #Valid annotation. What you could do is to remove the annotation, then set all properties in the controller and before calling the DAO function validate the entity manually:
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
Set<ConstraintViolation<User>> constraintViolations = validator.validate(user);
It's up to you.
Yes, don't do that :)
•username not null
You can use #NotNull in model.
•unique username in db (with an unique index on table)
instead of doing at the time of inserting you can make an AJAX call when username field on form get out of focus to check entered user is exit or not.
While updating you want to update fields without updating password (if its blank)
First of all I would like to tell you that NotEmpty and NotNull annotation from domain model. I don't use the #NotNull and #NotEmpty validations in practice you can refer this article
You can use hibernates dynamic-update to update only those properties which is changed. Go Through this
You can use password field on the edit form and as per your idea you can validate if user enters something in password field then update is required other wise first fetch the object from DB set to updating object then call for hibernate to update the object.
Better Way
To change your models As you said you want to take users FirstName,LastName,other personal details that should be in Person model and all the login account related part in LoginAccount model
So your model becomes like :
Person
PersonId
FirstName
LastName
etc.
LoginAccount
LoginAccountId
UserName
Password
FK_PersonID
And you make viewmodel for new registration form and change password and edit profile functionalities to update details.

MVC Controller/View removing model attribute

I'm creating a small MVC application and am passing a User object from a controller to an ActionResult method in another controller. One of the attributes of the User object is a list of Property objects called Properties.
Here's the rub: when the User object is finally passed to the relevant View, it's list does not contain any properties.
Here's the setup:
User class:
public class User
{
public int Id {get;set;}
public List<Property> Properties {get;set;}
}
AccountController
public ActionResult LogOn(int userId, string cryptedHash)
{
//code to logOn (this works, promise)
User user = dbContext.getUser(userId);
//debugging shows the user contains the list of properties at this point
return RedirectToAction("UserHome", "Home", user);
}
HomeController
public ActionResult UserHome(User user)
{
ViewBag.Messaage = "Hello, " + user.Forename + "!";
return View(user); //debugging shows that user.Properties is now empty(!)
}
UserHome.cshtml View
#model emAPI.Model_Objects.User
#{
ViewBag.Title = "UserHome";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>UserHome</h2>
<div>
#Model.Forename, these are your properties:
<ul>
#foreach (var property in #Model.Properties)
{
<li>property.Name</li>
}
</ul>
</div>
The view loads without any problem - #Model.Forename is fine, but as far as HomeController is concerned user.Properties was empty when it received it, although I know it wasn't when AccountController sent it.
Any help or advice anyone has to offer would be gratefully received.
You cannot pass entire complex objects when redirecting. Only simple scalar arguments.
The standard way to achieve that is to authenticate the user by emitting a forms authentication cookie which will allow you to store the user id across all subsequent actions. Then if in a controller action you need user details such as forename or whatever you simply query your data store to retrieve the user from wherever it is stored using the id. Just take a look at the way the Account controller is implemented when you create a new ASP.NET MVC 3 application.
So:
public ActionResult LogOn(int userId, string cryptedHash)
{
//code to logOn (this works, promise)
User user = dbContext.getUser(userId);
//debugging shows the user contains the list of properties at this point
// if you have verified the credentials simply emit the forms
// authentication cookie and redirect:
FormsAuthentication.SetAuthCookie(userId.ToString(), false);
return RedirectToAction("UserHome", "Home");
}
and in the target action simply fetch the user id from the User.Identity.Name property:
[Authorize]
public ActionResult UserHome(User user)
{
string userId = User.Identity.Name;
User user = dbContext.getUser(int.Parse(userId));
ViewBag.Messaage = "Hello, " + user.Forename + "!";
return View(user);
}
Ah and please, don't use ViewBag. Use view models instead. If all that your view cares about is welcoming the user by displaying his forename simply build a view model containing the forename property and then pass this view model to the view. The view doesn't care about your User domain model and it shouldn't.
RedirectToAction method returns an HTTP 302 response to the browser, which causes the browser to make a GET request to the specified action. You should not think about passing a complex object in that to the next action method.
In this case, may be you can keep your user object in the Session variable and access it in the remaining places.
public ActionResult LogOn(int userId, string cryptedHash)
{
User user = dbContext.getUser(userId);
if(user!=null)
{
Session["LoggedInUser"]=user;
return RedirectToAction("UserHome", "Home");
}
}
public ActionResult UserHome()
{
var loggedInUser= Session["LoggedInUser"] as User;
if(loggedInUser!=null)
{
ViewBag.Messaage = "Hello, " + user.Forename + "!";
return View(user);
}
return("NotLoggedIn");
}

Entity Framework - Problems to update user information

I'm new to Entity Framework, and I have to update a record in my database. I used the "Edit" example generated by the MVC3 framework and tried to customize to my needs.
I have a password field and before submit it to update I need to encrypt it with MD5. All process is running ok, except for the db.SaveChanges(); it saves the data posted by the form. Doesn't matter if I try to change the password, the framework just ignore that and save the data as it was posted in the form.
My .cshtml file:
<div class="editor-label">
#Html.Label("password", "Senha")
</div>
<div class="editor-field">
#Html.Password("password")
</div>
My method:
[HttpPost]
public ActionResult Editar(FormCollection form)
{
var newPassword = form["password"];
var email = Session["email"].ToString();
UserSet user = db.UserSet.SingleOrDefault(m => m.Email == email);
if (ModelState.IsValid)
{
//Changing password
user.Password = Crypto.CalculateMD5Hash(newPassword);//this line is ignored
TryUpdateModel(user);
db.SaveChanges();
return Redirect("~/Home/Mural");
}
return View(user);
}
What am I missing?
Your line
TryUpdateModel(user);
Will overwrite anything you've done on your model prior.
Change the order to
TryUpdateModel(user);
user.Password = Crypto.CalculateMD5Hash(newPassword);//this line is ignored
And it'll probably work.

Resources