Fehler (hasErrors()) mittels redirect übergeben

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?

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

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.

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

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.