1

I'm wonder how to ask this question, but somehow i have to...

TL;DR. I had worked on a system, passed to me by someone else, running on someplace, that they came with their wired request and idea. so i said let do the X thing and make it done, but as i just run it, i saw it didn't worked as it used to... debugging the app, i reach to place, testing testing testing and looking, like my head crashed on that part a whole day, and only thing i could say, why everything is right, why the data exists, why it's valid, and still the controller say it's invalid :|

Well. Today i came back to same project, same point of issue, and here's what i saw:

this is my sent request:

{
    "Code" : 16,
    "StampForm" : {
        "$error" : {},
        "$name" : "stampForm",
        "$dirty" : true,
        "$pristine" : false,
        "$valid" : true,
        "$invalid" : false,
        "$submitted" : true,
        "confirmUser" : {
            "$viewValue" : "a",
            "$modelValue" : "a",
            "$validators" : {},
            "$asyncValidators" : {},
            "$parsers" : [],
            "$formatters" : [null],
            "$viewChangeListeners" : [],
            "$untouched" : false,
            "$touched" : true,
            "$pristine" : false,
            "$dirty" : true,
            "$valid" : true,
            "$invalid" : false,
            "$error" : {},
            "$name" : "confirmUser",
            "$options" : null
        },
        "ReqNo" : "2",
        "ConfirmUser" : "a",
        "SabtDate" : "1395/06/15"
    },
    "MouseData" : {
        "locLeft" : 250.5,
        "locTop" : 395.53125,
        "width" : 812,
        "height" : 663,
        "mouseX" : 223.5,
        "mouseY" : 186.46875
    }
}

And the model i use:

public class StampForm
{
    public string ReqNo { get; set; }
    public string SabtDate { get; set; }
    public string FlightRef { get; set; }
    public string HotelRef { get; set; }
    public string ConfirmUser { get; set; }
    public string PassengerNum { get; set; }
    public string Price { get; set; }
    public string FlightNo1 { get; set; }
    public string FlightNo2 { get; set; }
    public string TicketSrv { get; set; }
    public string VoucherSrv { get; set; }
    /// <summary>
    /// تنظیم کننده
    /// </summary>
    public string Corrector { get; set; }
    /// <summary>
    /// اقامت
    /// </summary>
    public string Stay { get; set; }
    public string PersonPrc { get; set; }
    public string RoomPrc { get; set; }
    public CartableStampPositions Position { get; set; }
    public string Description { get; set; }
}

public class MouseData
{
    public float LocLeft { get; set; }
    public float LocTop { get; set; }
    public float LocRight { get; set; }
    public float LocBottom { get; set; }
    public float Width { get; set; }
    public float Height { get; set; }
    public float MouseX { get; set; }
    public float MouseY { get; set; }
}

public class StampVM
{
    public int Code { get; set; }
    public StampForm StampForm { get; set; }
    public MouseData MouseData { get; set; }
}

And here while i debug:

enter image description here

Do you see that? 'ConfirmUser' is filled with "a" and it generate error, but more than that, it's not 'ConfirmUser', it's 'confirmUser' which start with small 'c', and come from other angular object, which has same name, it even doesn't exists within my model, but since it has same name, the controller let it in, and let it get involved. cause me lot of issue and headache. now that i caught it, i have to solve it, but how should i tell the controller to ignore it?

Update, i forgot to mention that tell you i used MVC.Net WebAPI 2 controller, not the MVC Controller

Hassan Faghihi
  • 1,888
  • 1
  • 37
  • 55
  • *copy-pasting comment here; wrongly put under the below answer* - Do you have the privilage to make changes in the request sent? The model-binder is trying to bind the json object `confirmUser` (as model-binding is case in-sensitive) with your `string ConfirmUser` and thus ending up in error where model binder expects a string and the request is having an object. I would suggest to sent a clean request from client side with only the required values. – Developer Sep 05 '16 at 12:17
  • 1
    That request is wrong, having same property names for two different objects doesnt make sense. I would suggest to contact the team sending that request and ask them to change it. One dirty fix would be changing your model `string ConfirmUser` to an object with the structure coming in the request and read value from `$modelValue`. - With a big caveat that this would break if the order in which the `currentUser' and `CurrentUser` interchanges in the request. – Developer Sep 05 '16 at 12:17
  • @Developer well for MVC maybe, but not in real world, since one start with capital 'C', and other with small 'c', that's the differences, which is valid in both java and C#, but since there are differences between client standard and C# standard, the server should support both, but it first should check, and if found the match, shouldn't check for other possibilities, since it do, then i have to exclude them. – Hassan Faghihi Sep 05 '16 at 12:30
  • yeah, kinda agree. But the request payload out here is rubbish; thats the full angular form with its internal properties if Im not wrong. In this case, may be custom model binder for this particular action is the way to go – Developer Sep 05 '16 at 13:22
  • That s what I'm wonder how to write... How all these binder work together – Hassan Faghihi Sep 05 '16 at 13:46
  • Let me try writing one *if I get to find some free time* – Developer Sep 06 '16 at 02:26
  • i'll be grateful, ... – Hassan Faghihi Sep 06 '16 at 04:20

2 Answers2

1

You can add the following attribute to the method

[Bind(Exclude = "ConfirmUser")] 

So that it looks like this;

public ActionResult MyMethod([Bind(Exclude = "ConfirmUser")] MyModel model)
ChrisBint
  • 12,773
  • 6
  • 40
  • 62
  • i forgot to mention that my controller is web api 2 controller within MVC, and `...(...,[Bind(Exclude = "confirmUser")] StampVM stamp)` nor `...(...[Bind(Exclude = "StampForm.confirmUser")] StampVM stamp)` did worked for me :( – Hassan Faghihi Sep 05 '16 at 08:10
  • 1
    Do you have the privilage to make changes in the request sent? The model-binder is trying to bind the json object `confirmUser` (as model-binding is case in-sensitive) with your `string ConfirmUser` and thus ending up in error where model binder expects a string and the request is having an object. I would suggest to sent a clean request from client side with only the required values. – Developer Sep 05 '16 at 09:27
  • @Developer is there a possibility that i create a custom binder? i tried once, but the attribute run once per whole model, not each field, and i tried to find the binder itself, which i didn't got it at all..., this answer alone is best, but if i can make it possible, since the only option there that work with web api, seem to be only `FromUri` which doesn't have any Include/Exclude property. – Hassan Faghihi Sep 05 '16 at 11:58
  • @deadManN - before going for custom binder, do you have control over the request being sent? – Developer Sep 05 '16 at 12:07
1

There is a workaround for this- Add a JObject property confirmUser' in your viewmodel so that the (unwanted) confirm user object binds to that object instead of Model binder trying to bind thatobjecttostring`:

 public class StampForm
    {
        public string ReqNo { get; set; }
        public string SabtDate { get; set; }
        public string FlightRef { get; set; }
        public string HotelRef { get; set; }
        public string ConfirmUser { get; set; }

        [JsonProperty("confirmUser")]
        public JObject User { get; set; }
   ..........
}

And the result will be: enter image description here

enter image description here

Developer
  • 6,240
  • 3
  • 18
  • 24