Fehler (hasErrors()) mittels redirect übergeben


#1

Meine Eingabemaske für mein Spring Boot / Thymeleaf - Projekt sieht momentan so aus

<!DOCTYPE HTML>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
	<head>
       <meta charset="UTF-8">
        <title>Title</title>

        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
		<link th:href="@{/css/style.css}" rel="stylesheet" media="screen"/>
		<style th:remove="all">
			body {
			}
		</style>	
        
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>		
	</head>
	
	<body>
	    <div th:fragment="content">
	    	<h2 th:text="#{${subtitle}}">Create or edit a Project</h2>
	    		    	
	    	<form class="form-horizontal" action="#" th:action="@{/project/save}" th:object="${project}" method="post">

		    	<div th:if="${#fields.hasErrors('*')}" class="allert alert-danger">
		    		<p th:text='#{project.hasErrors}'>Error Message</p>
		    	</div>
	    	
				<div class="form-group">
				  <label class="control-label col-sm-2" for="name" th:text="#{project.mask.name}  + ' :' ">Name :</label>
				  <div class="col-sm-10">
				    <input type="text" th:field="*{name}" class="form-control" id="name" th:placeholder="#{project.mask.name.placeholder}">
                    <span th:if="${#fields.hasErrors('name')}" th:errors="*{name}" class="text-danger"></span>
				  </div>
				</div>

				<div class="form-group">
				  <label class="control-label col-sm-2" for="key" th:text="#{project.mask.key}  + ' :' ">Key :</label>
				  <div class="col-sm-10">
				    <input type="text" th:field="*{key}" class="form-control" id="key" th:placeholder="#{project.mask.key.placeholder}">
                    <span th:if="${#fields.hasErrors('key')}" th:errors="*{key}" class="text-danger"></span>
				  </div>
				</div>
				
				<div class="form-group">
				  <label class="control-label col-sm-2" for="description" th:text="#{project.mask.description}  + ' :' ">Description :</label>
				  <div class="col-sm-10">
				    <textarea class="form-control" rows="5" id="description" th:field="*{description}" th:placeholder="#{project.mask.description.placeholder}"></textarea>
                    <span th:if="${#fields.hasErrors('description')}" th:errors="*{description}" class="text-danger"></span>
				  </div>
				</div>
	    		            
	            <div class="row">
	                <div class="col-md-6 mt-5">
	                    <input type="submit" class="btn btn-primary" value="Add Project">
	                </div>
	            </div>    
	    	
	    	</form>
	    </div>
	</body>
</html>

hierzu habe ich auch einen Controller mit einer Ansichts- und Speicherfunktion.

@GetMapping("/project")
ModelAndView create(Project project) {
	    ModelAndView modelAndView = this.getMaskView("project");
	    
	    modelAndView.addObject("subtitle", "project.subtitle.create");
	    
 	   return modelAndView;		 
 }

 ....
@PostMapping("/project/save")
public ModelAndView save(@Valid Project project, BindingResult result) {
	    
        if (result.hasErrors()) {
        	return new ModelAndView("redirect:/project");
        }
        	        
        projectRepository.save(project);

        return new ModelAndView("redirect:/projects");
}	 

Das Project Object sieht so aus:

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;

import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

/**
 * this is a entity for project
 * 
 * @author tomth
 */
@Entity
public class Project {

	@Id
	@GeneratedValue
	private Long id;

	/**
	 * create at 
	 * 
	 * @var Long
	 */	
	@CreationTimestamp
	private Date    created_at;
	
	/**
	 * updated at 
	 * 
	 * @var Long
	 */	
	@UpdateTimestamp
	private Date    updated_at;	

	/**
	 * selected Project 
	 * 
	 * @var TINYINT(1) not null default(0)
	 */	
	@Column(nullable = false)
	private Boolean	selected = false;	
	
	/**
	 * title of the project
	 * 
	 * @var String 255
	 */
	@NotBlank(message = "Name is mandatory")
	@Size(min=5, max=60)
	private String name;

	/**
	 * key of the project
	 * 
	 * @var String 10
	 */
	@NotBlank(message = "Title is mandatory")
	@Size(min=3, max=10)
	private String key;	
	
	/**
	 * description of the project
	 * 
	 * @var TEXT
	 */
	@Lob 
	private String description;

	/**
	 * default constructor
	 */
	public Project() {
	}
	
	/**
	 * intialize constructor
	 * 
	 * @param String name			name of the project
	 * @param String key			the key of the project
	 */
	public Project(String name, String key) {
		this.name = name;
		this.key = key;
	}
	
	/**
	 * id of the entity
	 * 
	 * @return
	 */
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	
	/**
	 * create date of the project
	 * 
	 * @return
	 */
	public Date getCreatedAt() {
		return this.created_at;
	}
	public void setCreatedAt(Date createdAt) {
		this.created_at = createdAt;
	}
	
	/**
	 * last update date of the project
	 * 
	 * @return
	 */	
	public Date getUpdatedAt() {
		return this.updated_at;
	}
	public void setUpdatedAt(Date updatedAt) {
		this.updated_at = updatedAt;
	}
	
	/**
	 * project name
	 * 
	 * @return
	 */	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
	/**
	 * key number of the project
	 * 
	 * @return
	 */	
	public String getKey() {
		return key;
	}
	public void setKey(String key) {
		this.key = key;
	}
	
	/**
	 * description of the project
	 * 
	 * @return
	 */	
	public String getDescription() {
		return description;
	}
	public void setDescription(String description) {
		this.description = description;
	}
	@Override
	public String toString() {
		return "Project [id=" + id + ", createdAt=" + created_at + ", updatedAt=" + updated_at + ", name=" + name
				+ ", key=" + key + ", description=" + description + "]";
	}
	
	
}

Das eigentlich Speichern funktioniert wunderbar. Problem tritt nur dann auf, wenn ich eine fehlerhafte Eingabe absetze.

<div th:if="${#fields.hasErrors('*')}" class="allert alert-danger">
    <p th:text='#{project.hasErrors}'>Error Message</p>
</div>

fields.hasErrors scheint nicht zu greifen. Meine Vermutung ist, weil ich einen redirect setze vergisst er die Fehlermeldungen.

Wie kann ich es hinbekommen, dass ich die Fehlermeldung übergeben bekomme, trotz redirect?


#3

Hi,

vielleicht hilft das hier weiter:

Demnach müsstest du nicht redirect:/project machen, sondern nur project

Also hier:

if (result.hasErrors()) {
        	return new ModelAndView("project");
        }

Gruß,
Martin


#4

Im Prinzip ja.

Die Eingabemaske läuft auf

localhost/project

und die Listenansicht auf

localhost/projects

Zum speichern wird

localhost/project/save

gepostet.

Wenn ich die Maske “project” zurückgebe, dann würde im Browser immernoch die URL localhost/project/save angezeigt und das möchte ich eigentlich nicht, Im URL Adressenfeld soll angezeigt werden,

localhost/project

Damit es auch erkenntlich ist. Ahh ich bin hier wieder auf der Eingabemaske. Leider habe trotze Tante :wink: Googel noch kein passendes Beispiel gefunden.


#5

Sprich was dagegen, zum Speichern auch einfach /project benutzt, und nicht /project/save?


#6

euch beiden Danke schön für eure Hilfe. Jep mrBrown du hast recht, ca. 1 Stunde nachdem ich es geschrieben hatte, viel mir das auch auf :joy: Ja, manchmal sieht man vor lauter Wald den Baum direkt vor einen.