Search code examples
nancy

Why is Nancy model binding failing?


I have a Nancy.SelfHost service that is working just fine for the Get routes, but is failing on the Post route. If fails on the Bind<T>() command in the following route with a "No parameterless constructor defined for this object" error:

Post["/schedule"] = _ =>
{
   var schedule = this.Bind<OatiDay[]>();
   PostSchedule(schedule);
   return HttpStatusCode.Created;
};            

Here is the call stack returned with the error:

Nancy.RequestExecutionException: Oh noes! ---> System.MissingMethodException: No parameterless constructor defined for this object.
   at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
   at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
   at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
   at System.Activator.CreateInstance(Type type, Boolean nonPublic)
   at Nancy.Serialization.JsonNet.JsonNetBodyDeserializer.CreateObjectWithBlacklistExcluded(BindingContext context, Object deserializedObject)
   at Nancy.Serialization.JsonNet.JsonNetBodyDeserializer.Deserialize(String contentType, Stream bodyStream, BindingContext context)
   at Nancy.ModelBinding.DefaultBinder.DeserializeRequestBody(BindingContext context)
   at Nancy.ModelBinding.DefaultBinder.Bind(NancyContext context, Type modelType, Object instance, BindingConfig configuration, String[] blackList)
   at Nancy.ModelBinding.DynamicModelBinderAdapter.TryConvert(ConvertBinder binder, Object& result)
   at CallSite.Target(Closure , CallSite , Object )
   at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
   at Nancy.ModelBinding.ModuleExtensions.Bind[TModel](INancyModule module)
   at TSID.Scada.Nancy.ScheduleService.<.ctor>b__2(Object _) in p:\TSIDDev\TSID.Scada\TSID.Scada.Nancy\ScheduleService.cs:line 24
   at CallSite.Target(Closure , CallSite , Func`2 , Object )
   at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
   at Nancy.Routing.Route.<>c__DisplayClass4.<Wrap>b__3(Object parameters, CancellationToken context)
   --- End of inner exception stack trace ---
   at Nancy.NancyEngine.InvokeOnErrorHook(NancyContext context, ErrorPipeline pipeline, Exception ex)

It looks as though something is going wrong with the deserializer, but I can't tell what is being created. The post code is passing all its tests on the server using the Nancy Browser, so something must be wrong with the request. But I can't figure out what it is. The OatiDay class is just a poco object.

    public class OatiDay
    {
        [BsonId]
        public string Id { get; set; }
        [BsonDateTimeOptions(DateOnly = true, Kind = DateTimeKind.Local)]
        public DateTime Date { get; set; }
        public String Facility { get; set; }
        public Production OnPeak { get; set; }
        public Production OffPeak { get; set; }

       public class Production
       {
           public Int32 ScheduleForDay { get; set; }
           public double? ActualForDay { get; set; }
           public Int32 ScheduledToDate { get; set; }
           public double? ActualToDate { get; set; }
       }
   }

Here is the request sent from an Angular $http service:

POST http://buckhorn1:7000/schedule HTTP/1.1
Host: buckhorn1:7000
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:39.0) Gecko/20100101 Firefox/39.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json
Referer: http://buckhorn1:7203/schedule
Content-Length: 793
Origin: http://buckhorn1:7203
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache

[{"id":"20150712","date":"2015-07-12T00:00:00Z","facility":"Watson","onPeak":{"scheduleForDay":0,"actualForDay":6.56,"scheduledToDate":77,"actualToDate":92.61},"offPeak":{"scheduleForDay":12,"actualForDay":4.26,"scheduledToDate":71,"actualToDate":64.97},"hasPeak":false},{"id":"20150713","date":"2015-07-13T00:00:00Z","facility":"Watson","onPeak":{"scheduleForDay":8,"actualForDay":8,"scheduledToDate":85,"actualToDate":100.61},"offPeak":{"scheduleForDay":4,"actualForDay":4,"scheduledToDate":75,"actualToDate":68.97},"hasPeak":true},{"id":"","date":"2015-07-14T00:00:00.000Z","facility":"Watson","hasPeak":true,"onPeak":{"scheduleForDay":9,"scheduledToDate":94,"actualForDay":9,"actualToDate":109.61},"offPeak":{"scheduleForDay":5,"scheduledToDate":80,"actualForDay":5,"actualToDate":73.97}}]

Can anyone tell me how to troubleshoot this?


Solution

  • I could not get the Nancy model-binding to work. I suspected it had something to do with the deserialization of the camel cased body. I replaced this:

    var schedule = this.Bind<OatiDay[]>();
    

    with this:

    var reader = new StreamReader(this.Request.Body);
    string text = reader.ReadToEnd();
    var schedule = JsonConvert.DeserializeObject<OatiDay[]>(text);
    

    and my service is working.