Spring 2 + Thymeleaf + Validation = Whitelabel Error Page 500 - validation

I'm trying to validate my form input, but after its submit it does not load my controller method, instead it shows a Whitelabel Error Page with Internal Server Errorand status 500 but with the correct validation errors.
I know it must be a matching issue with the expected parameters of my method, but so far from what I've gathered the rule is simply BindingResult then Model which I did but it still does not trigger my controller method...
Any thoughts are appreciated.
Controller-Method, that is not triggered unfortunately:
#Controller
#Validated
public class UserController {
#PostMapping("/users/add")
def String usersAdd(#Valid #ModelAttribute("user") User user, BindingResult bindingResult, Model model) {
if (bindingResult.hasErrors()) {
return "userAdd"
} else {
userRepository.save(user)
return "usersList"
}
}
Template-Form:
<form action="#" th:action="#{/users/add}" th:object="${user}" method="post">
<div class="form-row">
<div class="form-group col-md-12">
<input type="text" class="form-control" th:field="*{name}">
<div th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Name Error</div>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<button type="submit" class="btn btn-success">Add</button>
</div>
</div>
</form>
Model-Class:
#Document(collection = "users")
public class User {
#Id
String id
#Size(min=3, max=100)
String name
}
Update:
Went debugging and found this part, so it seems it's invoking with the correct parameters. I am new to Spring, so I'm having difficulties thinking about other "newbie"-problems.
in InvocableHandlerMethod.java the method DoInvoke is called on my usersAdd with Station, BeanPropertyBindingResult, BindingAwareModelMap.
When Invoking the method it causes a ConstraintViolationException which will abort invocation, is this expected?

I figured it out after a looong debug session. The #Valid before my User user causes a ConstraintViolationException, which will, expectedly, abort the invocation of my usersAdd because, as expected, the parameter is not valid.
And the reason for this behaviour was the #Validated at the controller class, which I picked up from some tutorial, but when removed everything works and validates as expected.
I guess I was checking a REST-Tutorial and tried to use their approach... not the best idea ;)

Related

Spring Web application tymeleaf : POST request directed to wrong address

I have post mapping for URL: "/bank/addnew" My controller looks like:
Upon submission the form is submitted to "http://127.0.0.1:8082/banks/%20/banks" However, I need it to go to "http://127.0.0.1:8082/banks/addnew". Thanks for the Help!
#Controller
public class BankController {
#Autowired private BankService bankService;
#GetMapping("/banks")
public String bankList() {
return "bank/bank_list";
}
#PostMapping(value="/banks/addnew")
public String addNew(Bank bank) {
bankService.save(bank);
return "redirect: /banks";
}
}
And my template:
<form method="POST" action="#" th:action="#{/banks/addnew}" >
<div class="form-group">
<label for="recipient-name" class="col-form-label">Bank Name:</label>
<input type="text" class="form-control" id="recipient-name" name="name">
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
The Post Method was actually working okay. I checked it by printing the Model object inside the target controller. However, it was being submitted to the database a NULL value because I didn't initialize getters and setters for my Model. Finally, there was a space in the return string, thus I removed it.
The controller Should have a return statement with no space.
return "redirect:/banks";

Cant change column value by pressing button (Spring Boot, Thymeleaf)

I get an Request method 'POST' not supported error every time I press the button
EventController.java fragment
#PostMapping(value = "/ledger/{id}/get-place")
public String modifyLedger(#PathVariable(value = "id") long id, #RequestParam Long place, Model model) {
Event event = eventRepository.findById(id).orElseThrow();
event.setPlace(place - 1);
eventRepository.save(event);
return "ledgerInfo";
}
That function suppose to substract 1 from Long place variable every time when i press the button but every time i got an HttpRequestMethodNotSupportedException error
LedgerInfo.html
<div class = "container mt-5">
<h1>Ledger Info</h1>
<div th:each="elem:${event}" class="alert alert-info mt-2">
<h2 th:text="${elem.title}"/>
Line count:<p th:text="${elem.line}"/>
Place remain:<p th:text="${elem.place}"/>
Place status:<p th:text="${elem.status}"/>
<form method="post">
<button type="submit" class="btn btn-success">Get Place</button>
</form>
</div>
</div>
Have no idea how to fix this. Any clues?
You did not specified the action for your form.
Use th:action="#{/your Url}" in the form tag.

Using remote attribute in .net core razor pages. The Parameter does not get value in Action Method of Controller

I am using Remote Attribute validation
The method is invoked successfully on textchange event. However, the parameters in the action method of the controller does not get the value of the field.
Here is the Action Method in the HomeController.cs. When invoked the Name parameter remains null. I will be pleased if someone solve this problem
[AcceptVerbs("Get", "Post")]
public async Task<ActionResult> IsExist(string Name)
{
List<Keywords> keywords = new List<Keywords>();
HttpClient client = _api.Initial();
HttpResponseMessage res = await client.GetAsync("api/Keywords");
if (res.IsSuccessStatusCode)
{
var result = res.Content.ReadAsStringAsync().Result;
keywords = JsonConvert.DeserializeObject<List<Keywords>>(result);
}
if (keywords.FirstOrDefault(x => x.Name == Name) == null)
{
return Json(false);
}
else
{
return Json(true);
}}
Here is the Model
public partial class Keywords
{
public int Id { get; set; }
[Display(Name = "Name of Keyword")]
[Required]
[Remote(action: "IsExist",controller: "Home", ErrorMessage = "Keyword already present!")]
public string Name { get; set; }}
Here is the razor page in which I want to implement validation
#page
#model Consumer.Pages.Keyword.CreateModel
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Keywords</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Keywords.Name" class="control-label"></label>
<input asp-for="Keywords.Name" class="form-control" />
<span asp-validation-for="Keywords.Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Keywords.Department" class="control-label"></label>
<select asp-for="Keywords.DepartmentId" class="form-control" asp-items="ViewBag.Department"></select>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-page="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
I found the solution. It is to
Remove partial in the model class definition.
Replace in the Razor Page
<input asp-for="Keywords.Name" class="form-control" />
with
<input asp-for="Keywords.Name" name="Name" class="form-control" />
The [Remote] attribute is all but useless. There's a long-standing problem from the ASP.NET MVC days that migrated its way into ASP.NET Core unabated, as well. Namely, the action that handles the remote must take a param that matches the input name of what what's being validated. In other words, your action takes a param, name, but what's being sent to it is Keywords.Name, which cannot bind to name. You'd have to do something like:
public async Task<ActionResult> IsExist(Keywords keywords)
And then, then the value will be available via keywords.Name. This obviously makes the usage highly dependent on the particular implementation, so if there's a situation with a different input name like Foo.Keywords.Name, then you'd need an entirely different action, to match that binding, and basically duplicate the logic (though you could factor out the majority the logic into some common method all the actions could utilize).
Long and short, you're probably better off just handling this yourself manually. Just bind to the input event on the name input, and then call your method via AJAX. Then you can explicitly control the key that's sent in the request body, such that it will always match your action. Just note that you'll also want to debounce sending that AJAX request so that it only happens every few keystrokes or so. Otherwise, you're going to hammer your action.

how to resolve Request method 'GET' not supported

I am getting below warning as
WARN [org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver] (default task-1) Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'GET' not supported]
I have already set the method as POST but still I'm getting the above error. I'm getting this warning message for my delete controller all other CRUD operations are working fine, except delete.
Please find below code
Controller mapped deleteproducts :
#RequestMapping(value="/deleteproducts", method= RequestMethod.POST)
public String deleteProduct(#PathVariable("productId")int productId) {
IProductsDAO ip = new ProductsDAOImpl();
boolean b = ip.deleteProduct(productId);
if(b)
return "success";
else
return "deleteproducts";
here's my jsp view:
<body>
<form id="update product form" action="${pageContext.request.contextPath}/deleteproducts" method="post" role="form" style="display: none;">
<div class="form-group row">
<label for="product Id" class="col-sm-2 col-form-label">Id</label>
<div class="col-sm-10">
<input type="text" name="productId" class="form-control" id="productid" placeholder="Enter the product Id you want to delete">
</div>
</div>
</form>
</body>
DAOimplementation for delete Method call:
public boolean deleteProduct(int productId)
{
boolean b = true;
try
{
sess.beginTransaction();
Products p = (Products)sess.load(Products.class, new Integer(productId));
sess.delete(p);
sess.getTransaction().commit();
}catch(Exception ex)
{
sess.getTransaction().rollback();
b = false;
}
return b;
}
can some one now tell me what changes should I make in my code to fix this ?
Thank you!
edit 1:
#DeleteMapping(value="/deleteproducts/{productId}")
public String deleteProduct(#PathVariable("productId")int productId) {
IProductsDAO ip = new ProductsDAOImpl();
boolean b = ip.deleteProduct(productId);
if(b)
return "success";
else
return "deleteproducts";
}
still getting a warning as:
WARN [org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver] (default task-1) Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'GET' not supported]
I don't understand #RequestMapping(value="/deleteproducts", method= RequestMethod.POST)
what do you mean by this? You are making a RequestMapping want to delete a record and method is POST?
I would suggest please follow the standard way of development. If you want to delete DeleteMapping, for POST use PostMapping and to retrieve some information you can use GetMapping.
Ideally, it should be
#DeleteMapping("/deleteproducts/{id}")
public void deleteStudent(#PathVariable long id) {
deleteproductsRepository.deleteById(id); or some CRUD logic to delete
}
You can refer to this link for better understanding REST
AS i think request form going to GET Method you can try javascript to submit form with function call.
Please find below code :
<form id="update product form" action="${pageContext.request.contextPath}/deleteproducts" method="POST">
<div class="form-group row">
<label for="product Id" class="col-sm-2 col-form-label">Id</label>
<div class="col-sm-10">
<input type="text" name="productId" class="form-control" id="productid" placeholder="Enter the product Id you want to delete">
</div>
<div class="col-sm-10">
<input type="button" value="submit" onclick="javascript:formSubmit()" name="submit" ></a>
</div>
</div>
</form>
<script>
function formSubmit() {
if(!isEmpty(document.from.productId.value)){ //even you can validate values in productId
document.form.method='POST';
document.form.action='/deleteproducts';
document.form.submit();
}
)
<script>
After writing the below code
#DeleteMapping(value="/deleteproducts/{productId}")
public String deleteProduct(#PathVariable("productId")int productId) {
IProductsDAO ip = new ProductsDAOImpl();
boolean b = ip.deleteProduct(productId);
if(b)
return "success";
else
return "deleteproducts";
}
After this instead of running on a normal browser, try running it on a REST API. I tried it on POSTMAN API and I don't get the error. Look at the below image

Spring controller isn't binding data from html form

In my current spring-boot project, I have a form which data is handled by by this controller method:
#RequestMapping(value="alterar", method=RequestMethod.POST)
#ResponseBody
public void altera(#ModelAttribute("object") E object, BindingResult result) throws Exception {
serv.altera(object);
}
and this method call that one in the service class:
public void altera(E e) throws Exception {
settings.save_settings(e);
}
but when I try submit data through this controller, despite the browser's developer tools shows the fields are being sent to the server, no data is received by the controller (I saw both values for object and result.getModel(); for both, the value for the fields are null).
Anyone can see what I am doing wrong here?
ps.: the html for the form:
<form role="form" class="form" id="form" method="post" enctype="application/x-www-form-urlencoded" action="/Paypal/alterar">
<div class="field-box">
<label>http.ConnectionTimeOut</label>
<div class="col-md-7">
<input type="text" name="http_ConnectionTimeOut" class="form-control" value="" />
</div>
</div>
...
</form>
There is not model attribute defined in html form
Try updating your view as
<form role="form" class="form" id="form" method="post" enctype="application/x-www-form-urlencoded" action="/Paypal/alterar" modelAttribute="object">
Moreover modelattribute will just bind your whole class with one single object that is object here and will directly give the default value which is null. Try using #ResponseBody instead.

Resources