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.
Related
Based on the information here http://docs.valence.desire2learn.com/res/course.html#actions I would expect that to 'update' a courseOffering I would specify a PUT with a CourseOfferingInfo block, which only contains a few attributes. Every time I try this, I get a 404, not found - even using the same route for a successful GET (404 says org doesn't exist OR org is not an offering - neither is true). However, if I specify a CreateCourseOffering block (directly from a previous GET), the PUT works fine. Is this correct and the documentation not? Or are there other things I should look for in this scenario? The documentation says use CreateCourseOffering for the POST to create an offering… I simply want to update one attribute of that offering and as such thought the PUT was the way to go.
If you use the "create" POST route with a CreateCourseOffering block, this will create a new course offering, and send back the CourseOffering block for the newly created course offering (this will include the org unit ID value for the new org unit you've built).
If you want to update an existing course offering, you should, as you suspected, use the "update" PUT route with a CourseOfferingInfo block. Note that you must provide valid information for all the fields in this block, since when used successfully, the LMS will use all the properties you specify in that block for new values for the org unit. The StartDate and EndDate fields are particularly finicky: you must provide either a valid UTCDateTime value (notice that the three-digit millisecond specifier in these values is mandatory) or a JSON null value if the field is not applicable.
Why a 404? What you're seeing with the 404s and the data you're passing is likely down to the way the back-end service is doing data binding. It tries to de-serialize your provided JSON data (and query parameters) into data objects it can read/manipulate -- if you provide a JSON block that contains a superset of the properties it's expecting, then this may work (for example, if you provide a CourseOffering block when you're expected to provide a CourseOfferingInfo) as the binding layer may ignore fields it doesn't need. If the binding process fails, because you provide a value for a property that can't be bound to the data type expected, or because you fail to provide a JSON property field it expects, then this can cause the service to return a 404 (because binding/de-serializing incoming parameterized data happens at the same time as matching the URL route to an underlying service handler).
If you provide a JSON structure (and query parameters) that the web-service can bind to its expected data objects, but the values you provide are invalid or nonsensical, then this can cause the underlying service handler to respond with a 400 (signalling an Invalid Request). But in order to get this far, your parameterized data still needs to get properly deserialized and bound into data objects for the underlying service to examine.
We'll be updating the documentation to more explicitly draw out this fact. The safest policy from the calling client perspective is to pass valid JSON structures that are exactly what's expected by the individual routes, especially since the underlying back-end service implementation might change how it handles incoming requests.
I have situation where Json.Encode is working locally (localhost) which is 64bit iis7 windows 7 box.
If I deploy to windows 2003 32 bit IIS6 I get circular reference errors. Shown below 'Error here' is the line where the error starts.
#*var model = '#Html.Raw(Json.Encode(Model))';*# <<<<Error here
var model = '#Html.GetJson(Model)';
As part of trying to resolve this I thought maybe one of the project dll's that were being used on the server was different than that locally so i copied any reference dll to the server bin directory. This did not help.
I do have a fix which is pretty easy. I would have preferred to be able to identity the issue. My guess is that it is using some dll on the server differently than locally to return json via Json.Encode then on the server.
My resolution as shown 2nd line above is to use Json.Net and a mvc helper
public static MvcHtmlString GetJson(this HtmlHelper htmlHelper, ViewModel vm)
{
string s = JsonConvert.SerializeObject(vm);
return new MvcHtmlString(s);
}
Has anyone seen this and resolved? (without json.net)
Assuming you're using Entity Framework, taking a look at the entities developed by the framework will shed some light on the topic. For example, I had table called Sessions and one called Enrollments, the Enrollments table having an FK relationship to the Sessions table PK. This resulted in the Session objects having a collection of Enrollments, and the Enrollment objects having a virtual instance of the Session that the enrollment was for ... which in turn pointed to a collection of Enrollments etc ... you get the picture, AND why the circular reference issue was detected by the json serialization.
The solution was to NOT query a collection of Session objects in the code, but to query an anonymous (untyped) object with all the same fields in it instead. The prevents the Json.Encode() from getting confused about, since it doesn't know about, the circular reference that Entity Framework is OK with in the code behind / server side code.
First of all, I have to say that I understand how Data Annotation -based Model Validation works in ASP.NET MVC4 and I have it successfully implemented with DataAnnotationsModelValidatorProvider. So I don't need assistance on setting it up.
But when it comes down to HtmlHelpers, I'm struggling with trying to figure the context of the error message. And by saying context, I mean which error we're talking about. Which Attribute returned the error?
What I can get, is the Key for the error and the current ErrorMessage but programmatically, there's nothing, that at least I'm aware of, that would communicate which error we're talking about. Whether it was Required attribute or some other attribute, there's not way that I can find how to distinguish them.
Let's open the scenario a little bit. I have custom HtmlHelpers to render ContentEditable elements. For example Html.ContentEditableValidationMessageFor(m => m.firstName);. It will output something like this:
<span contenteditable="true" data-valmsg-for="firstName" data-valmsg-replace="Please provide first name" class="field-validation-error">Please provide first name</span>
Now, I do have a jQuery plugin to handle and persist the changes in the contenteditable element and it will persist them into the backend. However, the UI has nothing that would say which error message we're talking about. Humans can easily see it's the RequiredAttribute, but programmatically there's no data to differentiate it from some MinLengthAttribute for example.
In this scenario, if I would simply use the data-valmsg-for="firstName" as the key for the localization, that'd return the same error message for all the errors concerning the same property.
To Round it Up
What would be the Best Practise, when ModelState is available, to emit a unique ID for ModelError? Considering I'm using ASP.NET MVC4 and DataAnnotationsModelValidatorProvider.
I can think of tons of ways to "Hack it Together" but I would like to use the ModelState and whatever MVC provides. If it all goes down to writing a custom ModelValidatorProvider, then I'm all open for it. As long as it is the best and most sustainable way of going about it. I'm all for Doing More Now and Less Later than Hacking it Now and Hacking it Forever to Keep It Working
Can you give some context around the need to know which rule triggered the validation error, could it be a case of you trying to do something you shouldn't have too?
In general I use FluentValidation (http://fluentvalidation.codeplex.com/wikipage?title=mvc) in place of Data Annotation validation for many reasons, de-cluttering models, unit testing validation logic, allowing vastly more complex validation that include business logic. If your free to use 3rd party libraries I'd give it a look as it has always solved any validation problems I've had in the past.
It lets you write c# code that deals with your model validation via a fluent API. It has an MVC extension that wires everything up for you so other than creating the models validation class there is little impact from then on. An example for your code snippet above would be...
RuleFor(modelname => modelname.FirstName).NotEmpty().WithMessage("lease provide first name");
Even implementing ModelValidatorProvider will not help, it is just a mechanism to provide ModelValidators based on Model Metadata. When during model binding process in a controller action ModelValidators are being invoked the result is just ModelValidationResult which only contains MemberName and a text Message.
I think there is a dirty way to find out which ModelValidator is failed by checking the error message like this:
var modelErrors = ModelState.Where(m => m.Value.Errors.Count > 0).Select(m => new { Name=m.Key , Errors=m.Value.Errors});
by checking ErrorMessage of Errors for each key in modelErrors against ValidatorProvider error messages you can find out the error belongs to which Validator.
Using asp.net MVC in c#, I am making a call to a stored procedure using Linq into my SQL Members table. I have no internal caching on the application, already checked to make sure it is turned off.
Test case:
I have my username set to test1. From the website I change my username to test2. The website still shows test1. I go to Management Studio and run my stored procedure called MemberByEmail, it shows test2 correctly.
I start simple, refresh my page to see if it's browser cache, still test1. I go to debugging and walk through the code and find that it goes correctly all the way to here to call the database:
/// <summary>Method that is mapped to the dbo.MemberByEmail database procedure.</summary>
/// <returns></returns>
[System.Data.Linq.Mapping.Function(Name="dbo.MemberByEmail")]
public System.Data.Linq.ISingleResult<BookCrossing.Data.Member> MemberByEmail(
[System.Data.Linq.Mapping.Parameter(DbType="nvarchar(100)")] string email)
{
var methodInfo = (System.Reflection.MethodInfo)System.Reflection.MethodInfo.GetCurrentMethod();
var result = this.ExecuteMethodCall(this, methodInfo, email);
return ((System.Data.Linq.ISingleResult<BookCrossing.Data.Member>)(result.ReturnValue));
}
I turned on the profiler for my sql db, and it actually shows an entry for MemberByEmail, and the result set that came back had username = test1 .
Again I ran the stored procedure through Management Studio, and it came up with test2 as the username. I waited for 15 minutes, refreshing the web page every 5 or so, and it never cleared and served the correct test2 from the db. The last strange piece, I ran IISReset and refreshed the page, test2 was returned.
I'm guessing this I am just overlooking something obvious. Any help or advice would be great. Thanks
UPDATE: I created a console application to take out the web piece of it. The problem is the same when accessing directly from a console app also, no change.
How are you calling this from the webpage? If via an Ajax call, IE helpfully caches the result for you...
Took a while but we got this resolved. Data access is done through a MemberRepository in our project, and we loaded member repository in our MembershipProvider class. The problem is that the MembershipProvider class was loaded at the start of the application and never removed, so all MemberRepository calls were done through the same context. The strange part is that the call went all the way to SQL (as noted we were able to see the request in profiler), but the bowels of the code got back the results set but instead used the first calls result set and sent that back to us.
So by moving the Repository into the desired method or our MembershipProvider, it was destroyed after each call and that solved the issue. I don't know that this is specific to our set up, but hopefully it will help someone in the future.
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.