The behavior described here appears to now be the default for ASP.NET MVC 2 (at least for Preview 1).
When modelbinding a querystring like this :
?Foo=&Bar=cat
The following binding occurs (assuming you're binding to a model with 'Foo' and 'Bar' string properties)
ASP.NET MVC 1
model.Foo = "";
model.Bar = "cat":
ASP.NET MVC 2 (preview 1 through RC)
model.Foo = null;
model.Bar = "cat":
Wanted to give anyone who is playing with V2 a heads up since this wasn't mentioned in the 'gu-notes'. Also curious if anyone in the know can comment on whether or not this will be the final implementation or a configurable feature? I'm fine either way but just hope they dont switch back to the old way ! Being configurable would be even better.
Edit: The lesson to learn from this point is whatever version you're developing against not to write code that says Foo.Length == 0 to test for an empty string or Foo.Length > 3 to check for a minimum length. Use string.IsNullOrEmpty(Foo) and/or check for null first.
Update: This question sparked my curiosity as to why they would actually make this change. I think that I stumbled on the answer while researching disabled controls. The W3 HTML spec defines a 'successful control' as follows :
A successful control is "valid" for
submission. Every successful control
has its control name paired with its
current value as part of the submitted
form data set. A successful control
must be defined within a FORM element
and must have a control name.
In other words - a successful control is one that will make it back to the server as a query string parameter. Now, if a control doesn't have a valid value then according to the spec :
If a control doesn't have a current value
when the form is submitted, user
agents are not required to treat it as
a successful control.
(spot the 'open to interpretation' language here with 'not required to...')
So I think by sending a null instead of an empty string it reduces browser incompatibilites where certain browsers may send Foo=&Bar= and others may not even send that query string parameter. By always interpreting Foo= as if Foo wasn't there at all forces you to be more defensive.
I think I'm at least on the right track as to the reason why here - and at least in part has something to do with the notion of a 'succcessful control'.
http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2
Null is more representative of what it actually is, and it is compatible with other nullable types besides string, so I imagine it's by design.
I prefer the behavior of v1. How will you be able to pass an empty string in v2? Additionally, with the latter you can't tell whether foo is in the query parameters or not.
One way to configure would be to replace the default model binder in V2 (or V1) to get consistent behavior. I prefer the null, myself.
Related
The following code returns a message which states whether the input value is a palindrome:
(for the sake of this post, the syntax isn't important, or written in any particular language)
function isPalindrome(
value: string,
successMessage: string = "Is palindrome",
failureMessage: string = "Is not palindrome"): string {
return value.reverse() == value ? successMessage : failureMessage;
}
Notice in the code above, the default messages are written in English, however as they are supplied as parameters, this method could easily be localized, and because the method has default values for the messages, this doesn't force the developer to provide them; for example:
isPalindrome("level") // returns "Is palindrome"
However we can demonstrate localization; for example, in spanish:
isPalindrome("level", "es palíndromo", "no es palíndromo") // returns "es palíndromo"
This got me thinking, when should code be designed with localization in mind?
Another example would be with exceptions; for example:
class PalindromeException : Exception("Is not a palindrome")
function validatePalindrome(value: string): void {
if (value.reverse() != value) {
throw PalindromeException();
}
}
Notice in this example that there is no way to localize the message in the exception. I know this could easily be fixed using the same principles in the first example, but it has been purposefully designed to demonstrate a lack of globalization.
Therefore, when should globalization be applied to code, so that it may be localized? When does it matter, and when does it not?
I believe, there's no ideal answer - everything depends on your architecture and use-cases.
However, I would suggest the following patterns:
All log messages (both server and client) should be in English
All error API responses should always provide a description in English and a unique error code which you can translate into a friendly message on the client side
In order to provide a good UX, all client messages should be in a user's language
In general, it's a good practice to have all technical data (logs, errors, etc) in English. All user-facing data has to be understandable for a user.
It strongly depends on your use-case. I would recommend to localise messages, that are displayed in a frontend, right away. My experience is, it's very costly to do it later. For debugging or monitoring messages I probably would do it just in english, 'cause most of developers are able to read english messages.
The only place where localization should take place is in the UI. If you’re sticking to the MVC principle of separation of concerns (or something similar like MVP or such), then the localization happens exclusively in the View part. Your isPalindrome function sounds more like business logic belonging to the Model part and should therefore not be concerned with i18n at all. Neither should exceptions worry about it, since no exception should ever be printed to the UI as is (except to provide debugging information, which doesn’t need to be/shouldn’t be localized).
Your function should only return true or false, and an entirely separate UI part should translate that into something user facing, potentially localizing it in the process. Same with the exception, it should be caught by something which loads an appropriately localized UI explaining the issue to the user.
All labels needs to be translated.
For this definition, text is an information being read by end user. In that context, a label is a piece of text which at the same time is not user input or user data.
To give an example, in an 'Save as' dialog the file name would be user input, the file content user data and the Save and Cancel labels on the two buttons would be the labels (and therefore in need of an translation).
Given this definition, the rule is as follows:
Only user interface code needs to translate and it translates all labels. On the opposite, business logic code not directly facing end users (such as libraries or backend services) shall not translate at all. Further, neither the business logic implementation nor the business logic API handles text other then user input or user data.
The rule thus also implicates clean separation of business logic code from user interface code. That comes very handy for testing.
For the palindrome example, the function would be business logic, and it would not return a text but something more suitable e.g. an boolean or enum. The user interface code would then evaluate the return and translate it appropriately. The same applies to the exception.
Is there any recommended/standard way to implement such MVC-code that a validation of form fields that are stored as nullable integers (int?) would work?
One of the root problems I'm having with this now, is that if I input something that is not integer like "abc123" and try to submit the form, the MVC tech stack will automatically convert this erroneous integer value to empty string before any validators are processed. Because empty is totally acceptable value in my case, validators will pass, form saving looks like proceeding correctly to the end and everything looks good until the user opens the updated form again and sees only empty field instead of the integer he or she inputted.
At first, I tried multiple dataAnnotation-validators to solve this problem, but none of them worked and the reason seems to be the fact that the erronoeus integer will be converted to empty string before the value is passed to validators.
[Integer]
[RegularExpression("[0-9]{1,}")]
public int? MyTroublesomeIntField { get; set; }
After this I tried to solve the problem by creating custom model binder by implementing IModelBinder. After experimenting with custom model binders, it seems that the erroneous value gets converted to empty string even before the custom model binder starts to run. Here's couple of code snippets from my modelBinder:
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
//f.e. this returns empty string
value.AttemptedValue
//f.e. this also returns empty strings, even though name "raw" leads one to think that this is the value no one has touched.
value.RawValue
//even this returned empty string even though one could think that by accessing the field from HttpRequest would give access to the truly original value inputted by the user, but no, mvc stack gets to tinker this one as well.
string valueBefore = HttpContext.Current.Request.Form[bindingContext.ModelName];
So it seems to me, that there is some part of MVC tech stack that makes this conversion (erroneous integer input to empty string value) even before the custom model binders start to work. I'm just wondering, where is this happening, and is there a chance to customize it to get access to the original value?
Other alternative I have been thinking by now would be to create a separate string field for each of the free input int? form fields I have in the system. For string fields even those simple dataAnnotations like RegularExpressions would work straight away and the main problem would be solved. Finally I would just need to add binding data from all these "dummy"-string values to real int?-values somewhere in the controller/business logic. But this just seems like a massive work as I'm having loads of these ints in the systm. It feels strange that there would be this much extra work to do in order to get int?s working, when at the same time, exactly similar apporach works pefectly with double? fields which is currently not working with int?s - if I input erroneous double containing f.e. letters, client side validation will catch the error.
Any suggestions what would be the best way to proceed here?
My tech stack is pretty much like this: Razor, MVC5, Entity framework,
I have MVC4 WebApi project that is working fine on my development machine but is acting
strangely on a deployed server. Both boxes are Server 2008 with IIS 7.5
I have a single controller with a single Post method that takes a complex type as
a parameter. I am forcibly using the XmlMediaType formatter and XmlSerialization so
HttpConfiguration.Formatters.XmlFormatter.UseXmlSerializer = true and the complex type is "old school" XmlSerializable.
For the same request my dev box correctly deserializes the XML in the body into
an instance of the complex type and the result returned to the client from the Post method is correct. On the other machine the parameter instance is null on arrival in the Post method.
I have to reiterate that this is identical code and an identical request (except
for host name). That makes me think there has to be something environmentally
different between the machines. Unfortunately after much searching I still have no idea what that might be.
I should also point out that if I modify my Post method to take a string parameter
and then do the deserialization myself internally the behavior on both boxes is the same and correct.
I have also implemented a custom serializer (XmlFormatter.SetSerializer) and again
the dev box works; the other produces a null parameter. Interestingly, when I log
the body of the request before deserialization in this case I see the same XML on both machines. It's just that one box drops the deserialzed value somewhere on the way to the Post method.
Can anyone offer some suggestions on how to proceed to troubleshoot this strange
behavior?
Check the ModelState on the controller. If deserialization fails for any reason, it gets recorded as a ModelState exception. It might help you at least figure out what's going on.
It's easy to set our action not to validate the input just hooking up the attribute like
[HttpPost, ValidateInput(false)]
public ActionResult Signup(SignupModel model)
{
...
}
But I was wondering, is there a way to only do this in just one form field? and not all of them in the actual <form>
or, I have to use this and then worry about encoding properly all other fields?
You should not rely on ValidateInput to 'encode' your values. It doesn't encode values - it just rejects some values outright. And even if you use it, you still must encode all your values that are user-entered and displayed on the site in any way.
In fact, because of that - I've never used that validation myself. For example, if all I did was rely on that validation, people would not be able to enter some very basic things in forms like this one, such as trying to show example HTML.
But even the MSDN documentation and every book I've read said that the ASP.NET validation there does not protect you against every possible malicious input. So essentially, you can not rely on it to protect your users. You still must encode values you are displaying (and you should encode them only as you are displaying them, otherwise you'll end up with all sorts of display bugs where you've double-encoded things)
Using MVC3 , the attribute can be applied to Model by specifying SkipRequestValidation.
I have a simple web-site. Almost every action takes int toonId as an argument (toonId does not equate to the user: one user can own multiple toons).
At the moment I provide that value to every view from controller, and every link and submit button sends this value back to the controller.
This works. I am just looking for an easier way to accomplish this (AOP comes to mind).
I use cookies to persist "favorite/default toon", and this works fine (used with ActionAttribute, that takes toonId from cookie and passes is to the toonId argument, if toonId wasn't provided). But I want to support cookie-less sessions as well.
Question: What is an easy way to add ambient variable to the page, without passing it explicitly all over? Such that it would work with cookie-less browsers.
Is Viewstate a way to go (which isn't supposed to be in the MVC)?
Is server-side session a way to go?
This is exactly what session is meant for.
There is no page lifecycle in asp.net mvc, hence no viewstate.
I believe you can automatically pass query string parameters for use cookie-less browsers if you plug in your own implementation of IRouteHandler. I have not tried it though. I found an example of implementing IRouteHandler although it does not show how to implement the query string parameter functionality.
I'm not clear on what 'toonid' represents. If it's 'ambient per user session', then as womp stated, Session state would work well for this. However, if it's somethign that has a larger scope (e.g. a colleciton of toonids that are commonly accessed by all users) then teh Cache would be a better and more scalable strategy.