Is it possible to have one parameter of type object in a controller's action accepting int, date, float or string from json

StackOverflow https://stackoverflow.com/questions/23349073

Domanda

I have a collection of of key-value pairs, where the value part can be of any of the types (C#) string, DatetimeOffset, long, float, boolean. The key-value pair is an alternate key of a class named Component:

public class Component {
  public long Id { get; set;}

  public string Key { get; set; }

  // another properties here ...

  public object Value { get; set;}
}

The user enters the value part in an input bound with knockoutjs to an observable, and I expect to find the corresponding component instance given the key and that value with this controller action method (MVC4):

public JsonResult GetComponent(string compKey, object compValue)
{  
  var comp = Database.FindComponentValue(compKey, compValue);
  return this.Json(comp, JsonRequestBehavior.AllowGet);
}

which I invoke in a function of the knockout view model in this way:

self.findComponent = function (component) {    
  // component is ko.observable() generated on-the-fly with ko.mapping.fromJS()
  //var compdata = ko.mapping.toJS(component);

  $.get("MyController/GetComponent", {
    compClasskey: component.FullCode(),
    compValue: component.Value() 
  },
  self.validateComponent 
  );    
};

validateComponent is a function that shows an icon OK if the component is found.

Now, if component.Value() has a string value, MyController.GetComponent receives an array of string with the value in the first position (compValue[0]). But declaring compValue parameter as string works:

public JsonResult GetProductComponent(string compClassKey, string compValue) { ... }

But it leads to me to declare the method like this in order to be able to accept the other types:

public JsonResult GetProductComponent(string compClassKey, string compValueString, DatetimeOffset compValueDateTimeOffset, bool? compValueBoolean, long compValueLong) { 
... 
}

Another approach is to be compValue of type string and its corresponding type in another parameter also of type string.

Is this the solution or is it possible to have only one parameter of type object and I am making a mistake I am not seeing?

È stato utile?

Soluzione

Your basic issue is that you are pushing the data payload as items on the querystring, not as a JSON payload. In this scenario the QueryString value provider will be responsible for populating the method's parameters.

All querystring key values are treated as an array by the provider. If you specify a data type and the key's value has 1 entry then the provider will flatten the array to a single value.

In your case you specify an object datatype, so the provider gives you the array of 1 entry.

To fix this you need to switch from $.get to $.ajax, supply a few extra parameters and force the data payload to be a JSON string.

$.ajax({
  url: "MyController/GetComponent",
  type: 'POST',
  contentType: 'application/json',
  dataType: 'JSON'
  data: JSON.stringify( 
   {
    compClasskey: component.FullCode(),
    compValue: component.Value() 
   })
});

The MVC application should now use the JSON value provider ( as the contentType is now changed to application/json ) instead of the QueryString provider and the compValue parameter will now set as you expect.

As an aside: IMHO you should NEVER request a JSON document via a GET request, it is still an attack vector, unless you can 100% guarantee IE10+, Chrome 27+ or Firefox 21+.

JSON Hijacking -- Phil HAACK

SO -- Is JSON Hijacking still possible

Altri suggerimenti

All values are transported from client to server in the request body, this is a "text" stream and can be seen as a large string.

MVC reads the method's arguments and types, and tries to convert the (string) values from the http request to the type of the argument If your argument is defined as an object, it does not know where to convert the incomming string values to. So that is what you are not seeing ;-)

To solve this you indeed need to provide information about the type of the value. having to arguments (compClassKey and compValue) is a solution.

Alternative you can provide a model class

public JsonResult GetProductComponent( ComponentValue value )

where ComponentValue is

public class ComponentValue { public DateTime? DateValue {get;set;} public string TextValue {get;set;} public decimal? DecimalValue {get;set;} }

and than post a JSON object

{ DecimalValue:1 }

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top