Spring MVC How to log all the errors from BindingResult - spring

I have a controller which makes post method. The controller's method validates a entity. In case some errors occurred it redirects to error page otherwise it saves the entity. My code looks like that:
public String createEntity(Entity entity, BindingResult result) {
// Validate here
if(result.hasErrors) {
return "errorPage";
}
service.create(entity);
return "some view";
}
So now if there are errors I want to log them all. I've read this article
How to get error text in controller from BindingResult
but I don't want to type check.
Is there a clever way to do that?
Thank you.

it is very simple just add error list to your model
public String createEntity(Entity entity, BindingResult result,Model model) {
// Validate here
if(result.hasErrors) {
model.addAttribute("errors",result.getAllErrors());
return "errorPage";
}else {
service.create(entity);
return "some view";
}
}
later in your jsp :
<c:if test="${not empty errors}">
//foreach element show error (just an exampl)
</c:if>

Related

Modify and return ModelAttribute object if validation fails

I have simple entity ...
#Entity
public class WatchedDirectory {
#Column(nullable=false)
#NotBlank(message="Filesystem path CANNOT be empty")
String filesystemPath;
}
... and GET endpoint for creating one ...
#GetMapping("/add")
public String add(#ModelAttribute WatchedDirectory watchedDirectory) {
return "mng-dir-add";
}
... that shows form made in Thymeleaf, with error validation and all. Once you hit sumbmit button data goes into POST endpoint ...
#PostMapping("/add")
public String addExecute(#Valid #ModelAttribute WatchedDirectory watchedDirectory, BindingResult result, RedirectAttributes redirect, Model model) {
if(result.hasErrors()) {
// here I want to iterate through
// errors and clean erroneous fields
return "mng-dir-add";
}
watchedDirectory = fs.persistDirectory(watchedDirectory);
redirect.addFlashAttribute("added", watchedDirectory);
return "redirect:/list";
}
... and everything is nice and dandy. When data is valid it get persisted and redirect to list is issued (POST/Redirect/GET). When data is invalid thymeleaf's error fields are populated and I list error messages below appropriate fields.
The only thing I want to change, but I can't figure out how to, is to clear some data from model.
Things I tried so far: modifying #ModelAttribute parameter, setting attributes in Model, setting attributes in RedirectAttributes. Every time I get the very same data user provided without any changes in output form, for some reason I can't change a thing. I tried also redirecting to GET method but it seems it clears slate clean, which I don't want.
If someone is interested this is how form in thymeleaf looks:
<form id="content" action="#" th:action="#{/add}" th:object="${watchedDirectory}" method="post" class="was-validated">
<div class="form-group has-feedback has-error">
<label for="filesystemPath">Filesystem path:</label>
<input th:field="*{filesystemPath}" type="text" id="filesystemPath" name="filesystemPath" class="form-control" placeholder="~/" required />
<label th:if="${#fields.hasErrors('filesystemPath')}" th:errors="*{filesystemPath}"></label>
</div>
<button type="submit" class="btn btn-outline-success">Save</button>
</form>
required attribute on input field will shut up when provided with whitespace but there will be error message from Spring's validation. Clearing this field and returning it to user will make things more consistent than showing mixed signals such as:
Any help would be really appreciated.
You need to define a BeanPropertyBindingResult object that provides the fields having the errors. Then make a model with this results,
#PostMapping( "/add" )
public String addExecute( #Valid #ModelAttribute WatchedDirectory watchedDirectory, BindingResult result, RedirectAttributes redirect, Model model )
{
if( result.hasErrors() )
{
BeanPropertyBindingResult result2 = new BeanPropertyBindingResult( watchedDirectory, theBindingResult.getObjectName() );
for( ObjectError error : theBindingResult.getGlobalErrors() )
{
result2.addError( error );
}
for( FieldError error : theBindingResult.getFieldErrors() )
{
result2.addError( new FieldError( error.getObjectName(), error.getField(), null, error.isBindingFailure(), error.getCodes(), error.getArguments(), error.getDefaultMessage() ) );
}
model.addAllAttributes( result2.getModel() );
return "mng-dir-add";
}
watchedDirectory = fs.persistDirectory( watchedDirectory );
redirect.addFlashAttribute( "added", watchedDirectory );
return "redirect:/list";
}
According to Validation by Using Spring’s Validator Interface from the Spring Framework Documentation:
The Validator interface works by using an Errors object so that,
while validating, validators can report validation failures to the
Errors object.
validate(Object, org.springframework.validation.Errors): Validates
the given object and, in case of validation errors, registers those
with the given Errors object.
And the Errors interface doesn't provide any API for deregistering binding errors.
So, it seems there is no Spring provided way to achieve what you want.

Redirect in MVC controller to another controller not working

Hi Everyone , I am new to MVC but have programmed before. I have setup an error controller
which can be called when an error occurs in the main controller. My aim is to have one error view , which I can pass in the relevant error messages.
In the code below I am calling the error controller to displays the index view when there no record is returned from the product list
Here is my main controller
public ActionResult Details(int ProductID)
{
var model = db.Get(ProductID);
if (model == null)
{
//return RedirectToAction("Index", "ErrorController");
return errorController.Index("Record not Found", "The Product you are looking for is no longer available");
}
return View(model);
}
And this is my Error Controller that it calls successfully
public ActionResult Index(string errorTitle, string errorMessage)
{
ViewBag.ErrorTitle = errorTitle;
ViewBag.ErrorMessage = errorMessage;
return View();
}
At the moment the Index Action Result is called and the view returned but the details view from the original controller is also called, which causes an error as there is no records .
So my Error view is not being displayed .
How can I stop the original details view from being called ?
Any help would be great
Thanks
Try this-
return RedirectToAction("Index", "Error", new {
ErrorTitle = "Record not Found",
ErrorMessage = "The Product you are looking for is no longer available"
});
You basically need to redirect to the error controller and pass in the params as part of the route. RedirectToAction takes a parameter of RouteValues
Also you do not need "ErrorController", you can just use "Error"
Okay so generally in .net you don't want to pass along errors with redirects like that, instead you should use a library like Vereyon.Web FlashMessageCore which will help you with redirecting errors to the user in an easy and concise way.
However, if you want to do it that way (totally fine) you still need to pass the error title and error message like so in the redirect to action call:
return RedirectToAction("Index", "Error", new {errorTitle="error", errorMessage="message"});

FlashAttributes not in model after redirect

I have this controller method:
#GetMapping("/notations")
public String listAll(Model model) {
Iterable<PriceNotation> allItems = loadAllNotations();
model.addAttribute("notations", allItems);
return "supply/notations";
}
Then I have this method which redirects to the one above:
#GetMapping(value = "/notations/delete")
public String delete(#RequestParam(name="id", required=true) Long id, RedirectAttributes redirectAttributes)
{
try {
notationRepository.deleteById(id);
} catch (RuntimeException e) {
redirectAttributes.addFlashAttribute("message", "delete failed");
}
return "redirect:/notations";
}
When I put a breakpoint in the first method after a redirect, the model is empty. Although the documentation says:
After the redirect, flash attributes are automatically added to the
model of the controller that serves the target URL.
Also in my html page I have this header which should display the message:
<h2 th:text="${message}"></h2>
Also this header is empty. What am I missing?
PS, I know this question has been asked before but there was no accepted answer and none of the suggestions worked for me.
they are not added to model as they are passed as query parameters like example.com?message=abc.
So you can either:
access them in controller with #RequestParam and then add to your model
OR access them in thymeleaf with ${#param.message[0]}
in summary you should treat redirectAttributes as reqular query parameters in receiving controller (listAll).

How do I perform error handling in ASP.NET MVC 3 controller?

I have an Account controller which have:
LoginForm (get action)
Login (post action)
RegisterForm (get action)
Register (post action)
In another controller's index action i use render them as:
Html.RenderAction("RegistrationForm", "Acount");
Html.RenderAction("LoginForm ", "Acount");
Everything works ok and I can register a new user, login and validate with unobtrusive validation.
The problem is when do some back-end validation in Register/Login action and if there is an error I don't know how to transfer the error to be rendered.
I've tried with PRG pattern and it works ok. I get the error displayed on the form with the preserved data, but PRG is not the way to do it.
What is an alternative solution to this problem without using ajax to validate or move those methods in the controller where the RegistrationForm/LoginForms are used?
I want to skip using TempData due to session usage in the background.
EDIT CODE SAMPLE:
class AccountController : SomeBaseController{
[HttpGet]
public PartialViewResult RegistrationForm()
{
return PartialView(new RegisterUser());
}
[HttpPost]
public ActionResult RegisterUser(RegisterUser user)
{
if (ModelState.IsValid)
{
var _user;// create domain user from Register user Model;
var _validationOutput = _userService.DoSomeAwsomeServerSideValidation(_user);// do some custom validation
if (_validationOutput.IsFault)
{
// we preseve tempdata in base controller OnActionExecuted
_validationOutput.ErrorMessages.ForEach(x => ModelState.AddModelError(_validationOutput.ErrorCode, _validationOutput));
// redirect to home controller custom error occured
return RedirectToAction("Index", "Home", user);
}
return RedirectToAction("RegistrationInfo");
}
return RedirectToAction("SomeUserInfoAction");
}
}
class HomeController : SomeBaseController {
Index(){
return View();
}}
HomeControllerMarkup {
#{Html.RenderAction("RegistrationForm", "Acount");}
#{Html.RenderAction("LoginForm", "Acount");}
}
You can manually add errors to your ModelState within your post controller using:
ModelState.AddModelError("", #"You didn't perform task XYZ");
You should then be able to return the view and display the errors in a validation summary:
#Html.ValidationSummary(false, "Login was unsuccessful because...")

Route values disappeare in View .Net MVC3

I have simple controller:
public class TestController : Controller
{
public ActionResult Test(string r)
{
return View();
}
}
I have simple View Test.cshtml:
<h2>#ViewContext.RouteData.Values["r"]</h2>
#using (Html.BeginForm("Test", "Test"))
{
<input type="text" name="r" />
<button>Submit</button>
}
I have route rule in Global.asax:
routes.MapRoute(
null,
"Test/{r}",
new { action = "Test", controller = "Test",
r = UrlParameter.Optional }
);
I want to make such thing: user types route value in input, press submit and controller redirects him to page Test/value. But controller show just page with name Test everytime. ViewContext.RouteData.Values["r"] is empty too. I check in debug, Test action recieves user value of r correctly.
How can I realize my idea?
Thanks.
I'm super late to the party, but just wanted to post a solution for reference. Let's assume that this form has more than just a strong as it's input. Assuming there are other inputs, we can wrap up the inputs of the form into a class in our model, called TestModel whose properties maps to the id's of the form's inputs.
In our post, we redirect to the get, passing in the route values we need in the URL. Any other data can then be shuttled to the get using a TempData.
public class TestController : Controller
{
[HttpGet]
public ActionResult Test(string r)
{
TestModel model = TempData["TestModel"] as TestModel;
return View(model);
}
[HttpPost]
public ActionResult Test(string r,TestModel model) //some strongly typed class to contain form inputs
{
TempData["TestModel"] = model; //pass any other form inputs to the other action
return RedirectToAction("Test", new{r = r}); //preserve route value
}
}
You cannot do this without javascript. There are two types of methods that exist when submitting a <form>: GET and POST. When you use POST (which is the default), the form is POSTed to the url but all data entered in input fields is part of the POST body, so it is not part of the url. When you use GET, the input fields data is part of the query string but of the form /Test?r=somevalue.
I wouldn't recommend you trying to send user input as part of the path but if you decide to go that route you could subscribe to the submit event of the form and rewrite the url:
$('form').submit(function() {
var data = $('input[name="r"]', this).val();
window.location.href = this.action + '/' + encodeURIComponent(data);
return false;
});
As far as you are saying to post the form to Html.BeginForm("Test", "Test") you will be always posted back to the same page.
A solution could be to use an explicit Redirect to the action using 'RedirectToAction' (in view) or you can use javascript to change the form's action:
<input type="text" name="r" onchange="this.parent.action = '\/Test\/'+this.value"/>

Resources