Spring MVC: where should I do input validation? - javascript

I am using AJAX controller. Here is the workflow:
User enters the data in the textbox, hits search
If the entered data doesn't correspond to the expected (valid) data format (ie some exact regex) - Validation error message should pop up
If the entered data is correct, the controller is called. Controller calls the external service based on (validated) data from the user (textbox), and returns the corresponding JSON, and AJAX returns in back to client-side Javascript
Controller.java
#RequestMapping(value = "api/user/{user}", produces = "application/json", method = RequestMethod.GET)
public #ResponseBody UserData execute(#PathVariable(value = "user") String user) {
if(validator.isValid(user)) {
return service.getUserData(user);
} else {
throw new ResourceNotFoundException(INVALID_USER_FORMAT);
}
Javascript
// obtain user from text input
var user = $('#user_input').val();
$.getJSON("/api/user/"+user, function (data) {
// ...
});
For now, I have the validation done in my controller, and NOT in Javascript. For example, I could remove it from controller, and just have it in Javascript, like this:
// obtain user from text input
var user = $('#user_input').val();
if(validate(asin)) {
$.getJSON("/api/user/"+user, function (data) {
// ...
});
}
The question
What is better, and what is more a good pattern? Having validation in Controller like such, or in Javascript?

It is good to have validation on both side but client side validation is must, client side validation will save lot server hit by restricting data with invalid format.

It is always preferred to validate both the side.
Server side validation required for security reason. It is not necessary that user always send request through client UI. some of malicious user can use curl request [ex. postman] and any script for [ex JavaScript, angular etc] for request manipulation.
So always assume that client can send anything you should validate it on server side as well.

Related

Validate if input already exist in database ASP .NET Core 2.2

I have a form with some input fields and when you click on Save, I do a check if a field already exist in the database. I have a service method for this.
For example in the database the field with value "Test10" already exist and if the user use "Test10" in the input field and clicks on save I want to show this message :
private async Task<bool> CheckIfCodeAlreadyExist(string code)
{
return await _service.CheckCodeExist(code);
}
I tried with session vaiables, with an extra bool parameter but not effective enough.
My JavaScript knowlegde is not that much, but would it possible to check this with a "onClick" event in the form?
If the result is true you stay on the page with the message.
Don't mind the Model.Code is red.
Take a look at Microsoft's documentation for model validation, specifically remote validation. The use case and example for this is almost identical to yours:
https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-2.2#remote-attribute-1
The [Remote] attribute implements client-side validation that requires
calling a method on the server to determine whether field input is
valid. For example, the app may need to verify whether a user name is
already in use.
In your case, you may use a model with a Code property like this one:
[Remote(action: "CheckCode", controller: "Home")]
public string Code { get; set; }
On the server under the corresponding controller, you can provide the endpoint needed to validate the code:
[AcceptVerbs("Get", "Post")]
public IActionResult CheckCode(string code)
{
if (!yourService.VerifyCode(code))
{
return Json($"Code {code} is already in use.");
}
return Json(true);
}
You don't need to do it with onClick if you have liberty to use Remote validation .net MVC offers.
Decorate you model property with something like:
[Remote("Action", "Controller", ErrorMessage = "Invalid Code")]
Or pass additional fields as well:
[Remote("Action", "Controller", AdditionalFields = "Id", ErrorMessage = "Invalid Code")]
with javascript and jquery, you do ajax request.
Bind click event to button:
$('#button').on('click', function(){
$.ajax({
url: '/Controller/Action',
method: 'get',
data: {
code: $('#Code').val()
}
}).done(function (result) {
if (result) {
// code valid
$('form').submit();
} else {
// show error
}
}).fail(function () {
// ajax request failed
});
});
Controller/Action to return bool:
public async Task<bool> CheckIfCodeAlreadyExist(string code)
{
return await _service.CheckCodeExist(code);
}

Avoiding duplicate text input validation in Java and Javascript

I am using AJAX controller to provide the object from the server side. User enters the input, and AJAX controller provides the object for that input request. I have a complex validation logic so I have written it in Java, and put it in the AJAX controller. It looks like this:
#RequestMapping(value = "api/user/{user}", produces = "application/json", method = RequestMethod.GET)
public #ResponseBody UserData execute(#PathVariable(value = "user") String user) {
if(validator.isValid(user)) {
return service.getUserData(user);
} else {
throw new ResourceNotFoundException(INVALID_USER_FORMAT);
}
In Javascript, I call the AJAX controller like this:
// obtain user from text input
var user = $('#user_input').val();
$.getJSON("/api/user/"+user, function (data) {
// ...
});
Now, I want to display on the UI an error message where the entered user in '#user_input' is invalid. And I want to use the validation logic from my controller, and not to have duplicated validation logic also in Javascript.
What should I return from my AJAX controller when the validation doesn't succeed, and how to make use of that in Javascript, so that I can display something like "User format invalid"? What is the best practice?

Asp.net MVC, calling C# method from client-side by onclick JS event

I got "MultiLanguageProvider" which is ordinary C# class, not Controller or Model. The idea is when user clicks Change language - it must call back server-side void ChangedLanguage() on MultiLanguageProvider instance. This doesn't work at all:
#MultiLanguageProvider.Instance.SelectAppropriate("на русском", "in english")
- 'cause all the code inside #{ } get executed immideately - at the time of page-load. I am not informed about AJAX, so maybe someone of u can show me the right direction to do this simply job?
I don't understand which one you are trying to invoke on anchor tag click. But if I understand the essence of your question, you are trying to call some server side method to change the language, and here I assume you want to save the language selection that was made on user interface (UI). If this is what you are looking, on client side, you do the changes suggested by Stephen Muecke. On server side, you need to add a [HTTPPOST] action method on controller something like:
public ActionResult SwapLanguage(LanguageViewModel languageViewModel)
{
//do the save action => like saving to database
return Json(new { data = {urlToRedirt: "/someUrl_GeneratedVia_UrlHelper"},
status = "success",
message = "Language Changed Successfully" }, JsonRequestBehavior.AllowGet);
}
}
On Client side:
$('#swaplanguage).on('click', function(event) {
event.preventDefault();
$.post( '#Url.Action("SwapLanguage")', function( data ) {
// Handle the server side response here.
// Like if you want to redirect, use something like:
// window.location = data.urlToRedirt;
});
}
Of course, you need to handle error conditions on both client as well as server side.
If you don't want to save anything, and you just want to redirect user to some url based on the language user selects, then your onclick event handler is something like:
$('#swaplanguage).on('click', function(event) {
urlToRedirect = 'url_to_redirect' + '/' + $('#languageDropDownId').val();
window.location = urlToRedirect;
}
Finally, your anchor tag is:
<a id="swaplanguage">Change Language</a>
Hope that helps.

Spring MVC - Handling Errors, Form Submits with Multiple forms

I have a .JSP file that has three forms on it whose fields are dynamically shown/hidden based on user interaction with Javascript/Jquery elements.The forms are spring forms sending their action to a URL that matches a controller.
The issue is that when I submit a form, and it does not validate, the URL the form submitted to stays in the URL. Then any action I take that is URL dependent is basically corrupted because the URL has the form action appended to it.
For example, if my .JSP's normal URL is /admin/, and my spring form is:
<form:form id="form" method="POST" action="${pageContext.request.contextPath}/admin/createUser" modelAttribute="User">
If validation fails my URL will now be /admin/createUser. If I am then taking some action using Javascript/Jquery the URL is no longer a valid way to navigate. I could just work around the URL but it just seems...un-ideal.
I have tried using redirects like: "redirect:/admin/", but spring validation will not work with this because you are basically just reloading the page.
Are there any best-practice or "elegant" solutions to this, or something really simple that I'm overlooking?
The redirection should probably be done on the client side anyways with a param of returnToURL, and as for error handling in Spring, you can register method as error handler to return proper error responses:
class YourController {
/**
* see #link{org.springframework.web.bind.annotation.ExceptionHandler}
*/
#ExceptionHandler(Exception.class)
public #ResponseBody String handleException(Exception e, HttpServletResponse rsp) {
// set the response status
return "{\"error\" : ...}"'
}
}
If you get error you can return error message to a custom error page to display the error message. or in your case you can return the admin page again..
here is some example code .
#RequestMapping(value = "/createUser" ,method = RequestMethod.POST)
public ModelAndView create(#ModelAttribute User user) {
if(user == null){
Map<String, String> model = new HashMap<String, String>();
model.put("errorCode", "00");
model.put("errorText", "Error Message Here");
return new ModelAndView("error_page", model);
}
return new ModelAndView("Welcome_page");
}

Manipulate form data before it's sent with jQuery

I want to encrypt some data in a form using jQuery before it's sent to the server, it can be a MD5 hash. It is a small project, so I don't really need to use SSL.
I have the following JavaScript code where I use $.md5 in the password confirmation info:
$(document).ready(function() {
var dataToSend = {};
dataToSend['action'] = 'signup';
dataToSend['name'] = name.val();
dataToSend['email'] = email.val();
dataToSend['confsenha'] = $.md5(pass2.val());
var options = {
target: '#error',
url: 'insert.php',
beforeSubmit: validate,
data: dataToSend,
success: function(resposta) {
$('#message').html(resposta);
}
};
$('#customForm').ajaxForm(options);
});
The problem is that the data is being duplicated. I tought that overwriting the data being sent by using the var dataToSend would make ajaxForm send only data in that map. But besides sending data from dataToSend, it also sends data from the form, so what I wanted to encrypt using MD5 appears both encrypted and clean. This is an example of what goes in the request:
usuario=user&email=user%40email.com&senha=12345&confsenha=12345&send=&action=signup&name=user&email=user%40email.com&confsenha=d41d8cd98f00b204e9800998ecf8427e
I know I have to define the a function beforeSerialize, but I don't know how to manipulate form data. Can anyone tell me how to do that?
As per the documentation on the plugin site:
data
An object containing extra data that should be submitted
along with the form.
The word along is the crux.
So when you pass data as a part of the options object that data is serialized and is sent along with any data/input elements values that are part of a form.
A better approach would be to hash the password value and assign it to the same field or another hidden field in the beforeSubmit handler(in your case the validate function) and remove the dataToSend object totally.
Something like:
Without any hidden element:
function validate(){
//Other Code
pass2.val($.md5(pass2.val()));
}
With a hidden element in the form:
function validate(){
//Other Code
$("#hdnPass").val($.md5(pass2.val()));
pass2.val("");
}

Categories