0

Why RegularExpressionAttribute validation doesn't compare the input string with the value of all matches concatenated?

I asked a question here about the scenario below, but I found a solution the next day and found it better to raise the issue here.

[Required(
    AllowEmptyStrings = false,
    ErrorMessage = "Required")]
[RegularExpression(
    "^[^0]{1}|..+",
    ErrorMessage = "Expressao Regular")]
public string EncryptedValue { get; set; }

Except by empty string or "0", the property should be valid in ModelState, but:

enter image description here

You can test HERE the expression and value.

Expression
^[^0]{1}|..+

Value
+iCMEBYZQtWbnU2RPX/MmqrDPuVJzSGGWhkFd+9/zpMbHVoOlZFuF9ND1xAxsQy3YFCPIsUBEgg2RJNkPefrmQ==

You will notice that the expression match, but with two matches. The first match itself are not equals to the input string, you need to concatenate both match value to reach that.

But apparently this is not done in the validation of ModelState, even with jquery.validate.unobtrusive this happens (with jquery, I need to click in submit button two times to see this, but it's happens).

Solution
You need to build an expression that match input string completelly in the first match.

When you build a expression to validate a field, every OR in your expression must match all the input string.

So whenever you mount an expression with OR operators, always mount from largest input to smallest input.

In this case:
From ^[^0]{1}|..+ to ..+|^[^0]{1}

Henryk Budzinski
  • 1,161
  • 8
  • 20

2 Answers2

2

let's take a look at class System.ComponentModel.DataAnnotations.RegularExpressionAttribute . we are interested in the following method :

 [__DynamicallyInvokable]
public override bool IsValid(object value)
{
  this.SetupRegex();
  string input = Convert.ToString(value, (IFormatProvider) CultureInfo.CurrentCulture);
  if (string.IsNullOrEmpty(input))
    return true;
  Match match = this.Regex.Match(input);
  if (match.Success && match.Index == 0)
    return match.Length == input.Length;
  return false;
}

In our case we have regex expression "^[^0]{1}|..+" and input string +iCMEBYZQtWbnU2RPX/MmqrDPuVJzSGGWhkFd+9/zpMbHVoOlZFuF9ND1xAxsQy3YFCPIsUBEgg2RJNkPefrmQ== . Regex validation return two matches, the first + (first symbol) and the second is the rest part. the first match length less then input string, that is why IsValid return false.

Why RegularExpressionAttribute validation doesn't compare the input string with the value of all matches concatenated? because it works with the first match

Z.R.T.
  • 1,543
  • 12
  • 15
0

So, if your regex is ^[^0]{1}|..+

And you value is : +iCMEBYZQt...

One match it will be +, and the other: iCMEBY0ZQt....

The thing is, in the first part ^[^0]{1}, your regex try to match any character that is not 0, once (at the beginning of the line/string). So, the regex looks the string and say something like:

Voilah!! I have something that is not zero, at the beginning and is one character, the character is : +, Thanks, thanks, where is my cookie?!

Also, the or instruction (|) tells the regex to be more flexible about the patterns and is something like, If you don't find the first expression, don't feel bad, you also can look for this another thing. But once the regex has the first cookie doesn't make sense to go back and try to look again for the second expression (..+) because already found a solution for the first expression, and the regex knows that there will not be extra cookies for the same work. So, life continues and we have to move on.

Here you can see that the first and the second expressions are independent ways to satisfy the regex.

Regex with Or

https://regex101.com/r/UWtcF8/3


Your second regex is : ..+|^[^0]{1}:

Second regex with Or

https://regex101.com/r/la3wDa/1

Where the order of the expressions are swapped. This is the same but the first expression that the regex will try to satisfy is ..+ which basically is give anything that involves 2 or more characters. So for example if you have 00, the regex will say:

Yumm, cookies !

And that can be fine or not, it will depend on what you want to solve.


Now another option is:

^(?!0$).+

Regex with negative lookahead

https://regex101.com/r/gxJdch/1

Where we are going to look for anything but a single zero.


Useful and fun links: https://regex101.com/ < Even when you are not working on php you can select the php version and check regex debugger to learn more things about regex!) https://regexper.com/
mayo
  • 3,845
  • 1
  • 32
  • 42
  • I think that that is another problem,, the proposed regex only discard a string that is only an unique zero. Don't make any kind of control over email or telephone numbers. For email validation I suggest to read: https://stackoverflow.com/questions/5342375/regex-email-validation, and here is a nice answer about phone numbers too : https://stackoverflow.com/questions/18091324/regex-to-match-all-us-phone-number-formats! – mayo Dec 03 '17 at 21:16
  • Why not consider the OR operator? – Henryk Budzinski Dec 03 '17 at 21:20
  • mmm, I think that is just another way to see the problem. Because you are looking for `not zero or anything`, which is like `everything but if you find a zero discard the value` (which can be similar idea of an early return). Using the Or is valid too ! – mayo Dec 03 '17 at 21:25
  • I do not see performance as an answer here. Regex serves to find something. Limit the tool because of a foreach, sounds unnecessary to me, and that's is quite uncommon, but is a possibility. So, why not. – Henryk Budzinski Dec 03 '17 at 21:30
  • What do you mean with `limit the tool because a foreach`? – mayo Dec 03 '17 at 21:36
  • I can use the OR operator to build a result, if I need to write a full expression in each OR, this is a limitation. – Henryk Budzinski Dec 03 '17 at 21:50
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/160385/discussion-between-henryk-budzinski-and-mayo). – Henryk Budzinski Dec 03 '17 at 21:51