I am trying to implement custom validation in spring boot but there is some issue with the code even if I am passing wrong phone number the phone number should be 10 digit but I am passing a string and still I get status as OK can anyone tell me what is wrong with my code
#Data
public class StudentData {
#Phone
String phone;
}
#RestController
public class HomeController {
#PostMapping("/showData")
public String display(#Valid #RequestBody StudentData studentData){
return "OK";
}
}
#Documented
#Constraint(validatedBy = PhoneValidation.class)
#Target( { ElementType.METHOD, ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
public #interface Phone {
String message() default "{Wrong phone}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class PhoneValidation implements ConstraintValidator<Phone, String> {
#Override
public void initialize(Phone phone) { }
#Override
public boolean isValid(String phoneField, ConstraintValidatorContext constraintValidatorContext) {
if(phoneField == null) {
return false;
}
if(phoneField.matches("^[0-9]*$"))
return true;
return false;
}
}
Payload I am passing
http://localhost:8080/showData
{
"phone":"abc"
}
Try adding #Validated to your controller.
If that does not work, I would also confirm that validation code is being hit. Running your API in debug mode with a breakpoint in the isValid method can confirm if this is a problem in the validation logic, the Bean validator injection or your test setup (is it a test scenario?).
First, you don't need to use #Validated over #RestController, servlet container will validate it for you on data input annotated with #Valid
Second, your regex validates all strings consisting of digits, in spite of their length, so "1" or "22222222222222222" is a valid Phone
And last but not least - your code is working as desired :)
Related
I need to disable execution of one unit test on local environment. We run unit tests in local with local-test profile
-Dspring.profiles.active=local-test
I need to execute my test at all profiles except local-test How can I achieve this?
Here is my test, if I put value = "!local-test" it does not work
#IfProfileValue(name = "spring.profiles.active", value = "local-test")
#RunWith(SpringRunner.class)
public class EnvironmentTests {
#Test
public void TestNightTimeFormatCorrect() {
throw new RuntimeException("test exception");
}
}
I found a solution, hope it will be useful for somebody.
In this case TestNightTimeFormatCorrect will be executed every time when there is no local-test profile. It will be alse executed when no profile is set.
#RunWith(SpringJUnit4ClassRunner.class)
#ProfileValueSourceConfiguration(EnvironmentTests.ProfileProfileValueSource.class)
#IfProfileValue(name = "local-test", value = "false")
public class EnvironmentTests {
#Test
public void TestNightTimeFormatCorrect() {
throw new RuntimeException("test exception");
}
public static class ProfileProfileValueSource implements ProfileValueSource
{
#Override
public String get(String string)
{
final String systemProfiles = System.getProperty("spring.profiles.active", System.getProperty("SPRING_PROFILES_ACTIVE", ""));
final String[] profiles = systemProfiles.split(",");
return Arrays.asList(profiles).contains(string) ? "true" : "false";
}
}
}
I have a List type field populated using solrj where it marshals data directly to bean using getBean() method. The solr field is marked as multivalued but it really is single valued. In the rest response I want to transmit it as a single string. Here is the code
#XmlRootElement
#JsonSerialize(include = Inclusion.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class Record {
#JsonIgnore
#Field //solrj field populated based on schema type
private List<String> titleList;
public String getTitle() {
if(titleList!= null && titleList.size() > 0) {
return titleList.get(0);
}
return "";
}
}
When I get the response object from non jersey rest clients I see the 'title' field populated correctly as String but with jersey REST client I get it as empty String. How can it be correctly deserialized as derived value for all REST clients?
I am getting value from java client as
Record response = target.queryParams(queryParams).request().buildGet().invoke(Record.class);
Chrome Rest client output
{
"title": "new trend",
jersey client output
{
"title" : "",
I used #JsonIgnore on both getter and setter methods instead of field. That worked for both deserialization and serialization
#Field("title")
private List<String> titleList;
#JsonIgnore
public List<String> getTitleList() {
return titleList;
}
#JsonIgnore
public void setTitleList(List<String> titleList) {
this.titleList= titleList;
}
public String getTitle() {
if(titleList!= null && titleList.size() > 0) {
return titleList.get(0);
}
return null;
}
In my class, I have a property for a file attachment like so...
public class Certificate {
[Required]
// TODO: Wow looks like there's a problem with using regex in MVC 4, this does not work!
[RegularExpression(#"^.*\.(xlsx|xls|XLSX|XLS)$", ErrorMessage = "Only Excel files (*.xls, *.xlsx) files are accepted")]
public string AttachmentTrace { get; set; }
}
I don't see anything wrong with my regex, but I always get ModelState.IsValid false. This seems pretty trivial and simple regex, am I missing something? Do I need to write my own custom validation?
I'm populating AttachmentTrace via a regular input of type file:
<div class="editor-label">
#Html.LabelFor(model => model.AttachmentTrace)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.AttachmentTrace, new { type = "file" })
#Html.ValidationMessageFor(model => model.AttachmentTrace)
</div>
The action method is just a regular action:
public ActionResult Create(Certificate certificate, HttpPostedFileBase attachmentTrace, HttpPostedFileBase attachmentEmail)
{
if (ModelState.IsValid)
{
// code ...
}
return View(certificate);
}
Ok, here's the solution I found. I'm sure there are other solutions out there. First a little background, because my application uses EF code-first migration, specifying a HttpPostedFileBase property type in my model, produces this error when adding migration:
One or more validation errors were detected during model generation:
System.Data.Entity.Edm.EdmEntityType: : EntityType
'HttpPostedFileBase' has no key defined. Define the key for this
EntityType. \tSystem.Data.Entity.Edm.EdmEntitySet: EntityType:
EntitySet 'HttpPostedFileBases' is based on type 'HttpPostedFileBase'
that has no keys defined.
So I really had to stick with using a string type for the AttachmentTrace property.
The solution is to employ a ViewModel class like this:
public class CertificateViewModel {
// .. other properties
[Required]
[FileTypes("xls,xlsx")]
public HttpPostedFileBase AttachmentTrace { get; set; }
}
Then create a FileTypesAttribute like so, I borrowed this code from this excellent post.
public class FileTypesAttribute : ValidationAttribute {
private readonly List<string> _types;
public FileTypesAttribute(string types) {
_types = types.Split(',').ToList();
}
public override bool IsValid(object value) {
if (value == null) return true;
var postedFile = value as HttpPostedFileBase;
var fileExt = System.IO.Path.GetExtension(postedFile.FileName).Substring(1);
return _types.Contains(fileExt, StringComparer.OrdinalIgnoreCase);
}
public override string FormatErrorMessage(string name) {
return string.Format("Invalid file type. Only {0} are supported.", String.Join(", ", _types));
}
}
In the controller Action, I needed to make a change to use the ViewModel instead, then map it back to my Entity using AutoMapper (which is excellent by the way):
public ActionResult Create(CertificateViewModel certificate, HttpPostedFileBase attachmentTrace, HttpPostedFileBase attachmentEmail) {
if (ModelState.IsValid) {
// Let's use AutoMapper to map the ViewModel back to our Certificate Entity
// We also need to create a converter for type HttpPostedFileBase -> string
Mapper.CreateMap<HttpPostedFileBase, string>().ConvertUsing(new HttpPostedFileBaseTypeConverter());
Mapper.CreateMap<CreateCertificateViewModel, Certificate>();
Certificate myCert = Mapper.Map<CreateCertificateViewModel, Certificate>(certificate);
// other code ...
}
return View(myCert);
}
For the AutoMapper, I created my own TypeConverter for the HttpPostedFileBase as follows:
public class HttpPostedFileBaseTypeConverter : ITypeConverter<HttpPostedFileBase, string> {
public string Convert(ResolutionContext context) {
var fileBase = context.SourceValue as HttpPostedFileBase;
if (fileBase != null) {
return fileBase.FileName;
}
return null;
}
}
That's it. Hope this helps out others who may have this same issue.
I am currently experimenting with validation attributes,
and now I am trying to validate my ViewModel which contains an EmailAddress with a custom validation attribute.
public class UserLoginModel
{
[Required]
[EmailAddress]
public string email { get; set; }
[Required]
public string password { get; set; }
public bool rememberMe { get; set; }
}
I have made a unit-test where I give a false email address and try to validate my viewmodel.
[TestMethod]
public void TestingInvalidEmailAddress()
{
UserLoginModel model = new UserLoginModel();
model = GetAValidLoginModel(); //Get a default-model where all parameters are correct
model.email = "thisisnotavalidemail.com";
ValidationContext context = new ValidationContext(model, null, null);
var results = new List<ValidationResult>();
bool validModel= Validator.TryValidateObject(model, context, results);
//This is always true
Assert.IsFalse(validModel);
}
The result of this test is always False.
So I checked my attribute, because I thought I might have made a mistake:
[TestMethod]
public void Email()
{
string email;
var attr = new EmailAddressAttribute();
email = "myemail#domain.com";
Assert.IsTrue(attr.IsValid(email));
email = "thisisnotavalidemail.com";
Assert.IsFalse(attr.IsValid(email)); //If this fails, the test is successfull
}
And that did pass the test, using the exact same email address.
And when I test it in my browser, it also validates correctly.
So why does it not tell me that my email address is invalid in the first test-method?
I found my solution in over here.
Apparently I am just missing an extra parameter.
bool validModel= Validator.TryValidateObject(model, context, results, **true**);
I have to check some textField with the following regex:
[\sa-zA-Z.'-àáâãäåçèéêëìíîïðòóôõöùúûüýÿ]+
It works with regex checkers but it doesn't work with my GWT/GXT application. I use the function above in a class that extends AbstractValidator.
public static native boolean isValidName(String name) /*-{
var pattern = /[\sa-zA-Z.\'-àáâãäåçèéêëìíîïðòóôõöùúûüýÿ]+/;
return pattern.test(name);
}-*/;
I use GXT2 and GWT 1.6.4.
have you tried using String.macthes for validate the value and Validator class in gxt TextField?
someTextField.setValidator(new Validator() {
#Override
public String validate(Field<?> field, String value) {
return value.matches("[\\sa-zA-Z.'-àáâãäåçèéêëìíîïðòóôõöùúûüýÿ]+")
? null : "not a valid value";
}
});
Why go native, while you can still use Java ? Or even better, try to see if GXT gives you some validation tools. Like Validators
Related : Regular Expressions and GWT
OK for gxt2...
the corresponding method is setRegex(String regex) which will get called if all other validators have passed
http://www.jarvana.com/jarvana/view/com/extjs/gxt/2.2.0/gxt-2.2.0-javadoc.jar!/com/extjs/gxt/ui/client/widget/form/TextField.html
Why don't you use (for gxt3):
com.sencha.gxt.widget.core.client.form.Field<T>.addValidator(Validator<T> validator)
field.addValidator(new RegExValidator( "[\sa-zA-Z.\'-àáâãäåçèéêëìíîïðòóôõöùúûüýÿ]", "some message"));
see gxt field api
see gxt RegExValidator
Sencha GXT4:
enum VType
public enum VType {
ALPHABET("^[a-zA-Z_]+$", "Alphabet"), ALPHANUMERIC("^[a-zA-Z0-9_]+$",
"Alphanumeric"), NUMERIC("^[+0-9]+$", "Numeric"), EMAIL("^(\\w+)([-+.][\\w]+)*#(\\w[-\\w]*\\.){1,5}([A-Za-z]){2,4}$","Email");
String regex;
String name;
VType(String regex, String name) {
this.regex = regex;
this.name = name;
}
}
Validator class
import java.util.ArrayList;
import java.util.List;
import com.google.gwt.editor.client.Editor;
import com.google.gwt.editor.client.EditorError;
import com.sencha.gxt.widget.core.client.form.Validator;
import com.sencha.gxt.widget.core.client.form.error.DefaultEditorError;
public class VTypeValidator implements Validator<String> {
private String message;
private final VType type;
public VTypeValidator(VType type,String message){
this.type = type;
this.message=message;
}
#Override
public List<EditorError> validate(Editor<String> editor, String value) {
List<EditorError> res = null;
if (!value.matches(type.regex)) {
List<EditorError> errors = new ArrayList<EditorError>();
errors.add(new DefaultEditorError(editor, message,""));
return errors;
}
return res;
}
}
How to use with textField
field.addValidator(new VTypeValidator(VType.EMAIL,"Invalid email"));