I have a simple service which I'm using to POST data to a Rails controller.
My service looks something like this:
app.service('autoRulesService', function($http) {
return({
createRule: createRule
});
function createRule() {
var request = $http({
method: 'POST',
url: '/rules.json',
data: { one: 'two }
});
return request.then(handleSuccess, handleError);
}
function handleSuccess() {
// body omitted...
}
function handleError() {
// body omitted...
}
});
I use that service in my controller in a pretty standard way:
$scope.saveRule = function() {
rulesService.createRule().then(function() {
// do stuff...
});
}
The problem is that I get a weird unwanted key in my parameters when I inspect the sent data in the Rails log. Where is the "rule" parameter coming from?
Processing by AutoAnalysis::RulesController#create as JSON
Parameters: {"one"=>"two", "rule"=>{}}
It doesn't appear in the request payload (as inspected in Chrome Dev tools)
and my controller action is pretty standard (there's no before filters either):
class RulesController < ApplicationController
def create
# NOTE: I'm referencing an :auto_analysis_rule parameter here because
# that's my desired param key name. It doesn't exist in the request
# as shown here.
render json: Rule.create(params[:auto_analysis_rule])
end
end
and I can't find any mention of $http inferring a root JSON key from the URL or anything in the docs.
Where is the "rule" param key coming from?
Rails automatically wraps parameters that are attributes of the model. If one were an attribute of the Rule model, the payload would look like: {"rule" => {"one" => "two"}}
This functionality removes the need for a top-level key that contains all attributes. In other words, the following payloads would be treated the same if attr1 and attr2 are fields in the MyModel model:
{ "mymodel" : { "attr1" : "val1", "attr2" : "val2" } }
{ "attr1" : "val1", "attr2" : "val2" }
This functionality can be disabled per-controller or app-wide in an initializer. Check out this answer for more information: Rails 3 params unwanted wrapping
Related
i have content listed in a div and i have a dropdown with various options to order and filter that content.
I'm using ajax to filter/order that content and is working but i use other php page with the content i want on the div that has the content, like this
function order(str){
$.post("order_products.php",
{
q: str,
},
function(data, status){
document.getElementById("txtHint").innerHTML = data;
});
}
What i wanted was to instead of putting the code (data) to change in another page just for that, i could put that code inside a class php function that i have.
<?php
class products{
function list(){
blablabla
}
That way i would "save space" and organize everything, considering that i have many other things to order/filter but i don't know to to make the ajax request to that function, or if it's possible without having a page in between and then get the response from the function and put it on the div.
You can do this using Laravel by setting up a route to a function that will do the ordering. Please note I've made a lot of assumptions in the following answer as I can't see all your code and have made it quite general, please adjust the code to your project or provide more details of your code if you don't understand the answer fully.
routes.php
Route::post('products/order', [
'as' => 'products.order',
'uses' => 'ProductsController#orderProducts'
]);
Your view (assuming you're using blade)
$txtHint = $('#txtHint'); // grab the reference to txtHint dropdown
$.post( '{{ route("products.order") }}', // hit our route
{
q: str,
},
function(data, status){
$txtHint.empty(); // clear the dropdown
// loop through the data and assign each element to the dropdown
$.each(data, function(value, key) {
$txtHint.append($("<option></option>")
.attr("value", value)
.text(key));
});
});
ProductsController.php
public function orderProducts()
{
$orderBy = \Input::get('q');
return \Products::lists('name', 'id')->orderBy($orderBy);
}
For outside of a framework just change the url to your php file and add in a data attribute for the method you require to be fired from the file.
$.post( 'products.php', // hit our route
{
action: 'order',
q: str,
},
...
Then in products.php you'd do something like this
if(isset($_POST['action']) && !empty($_POST['action'])) {
$action = $_POST['action'];
switch($action) {
case 'order' : order();break;
case 'otherFunction' : otherFunction();break;
}
}
function order()
{
// order logic here
// get $_POST['q']
// do your ordering
// return data as json
}
See here for similar question: using jquery $.ajax to call a PHP function
We have a website that developed in symfony 1.4 framework. This website should be able to have multiple domains. Each domain has its special homepage and everything else. Actually the domain must be such a parameter for each action that according to it, the action gets the data from database and show it.
For example, we have a about us page. We save about us contents in about_us table. This table has a website_id. We keep website information in the website table. Suppose this:
website (id, title, domain)
about_us (id, content, website_id)
website contents:
(1, 'foo', 'http://www.foo.com') and (2, 'bar', 'http://www.bar.com')
about_us contents:
(1, 'some foo', 1) and (2, 'some bar', 2)
The question is, how should I configure my Symfony project, to be able to do like this? to get domain as a parameter and use that in Symfony actions?
You can create your own route class extending sfRoute. This route will add a 'domain' parameter to all requests:
//apps/frontend/lib/routing/myroute.class.php
class myRoute extends sfRoute
{
public function matchesUrl($url, $context = array())
{
// first check if it is a valid route:
if (false === $parameters = parent::matchesUrl($url, $context))
{
return false;
}
$domain = $context['host'];
// add the $domain parameter:
return array_merge(array(
'domain' => $domain
), $parameters);
}
}
Routing.yml (example):
default_module:
class: myRoute
url: /:module/:action/:id
...
In your action you get the domain with:
$request->getParameter('domain');
There are many ways for doing this.
You could extend the sfFrontWebController, and add extra code inside the dispatch() method.
# app/myapp/config/factories.yml
all:
controller:
class: myController
// lib/myController.class.php
class myController extends sfFrontWebController
{
public function dispatch()
{
$selectedSite = SiteTable::retrieveByDomain($_SERVER['HTTP_HOST']); // Example
if (!$selectedSite) {
throw new sfException('Website not found');
}
// Store any site value in parameter
$this->context->getRequest()->setParameter('site_id',$selectedSite->getId());
parent::dispatch();
}
}
I am trying to post data in JSON format to a .NET MVC Controller like this.
$.ajax({
type: 'POST',
url: 'http://mvc.tester.local/Home/NameConverter',
data: JSON.stringify({ convertermodel.InputName: obj.currentTarget.value }),
contentType: 'application/json'
});
But Javascript complains about the JSON.Stringify() bit.
The convertermodel.InputName to be exact.
The thing is I actually need this JSON data name to be that way i.e. have the same name as a property in my model; in order to take advantage of reflection for automatic binding.
This is my model:
public class NamesViewModel
{
public NameConverterModel convertermodel = new NameConverterModel();
}
and the sub Class
public class NameConverterModel
{
private string _inputName = "";
public string InputName
{
get { return _inputName; }
set { _inputName = value; }
}
}
How can I solve this please ?
I hope I am clear enough.
You would need your JSON to be of the structure like this:
{"convertermodel" : {
"InputName" : obj.currentTarget.value
}
}
Your JSON representation of your object needs to reflect the appropriate nesting that your object model that you're trying to model in the client-side requires. So defining your JSON by nesting your hierarchy at one level won't work -- you need to create objects of objects like you did in your C# code.
I have a controller defined as:
[AcceptVerbs(HttpVerbs.Post)]
public JsonResult PostMoreData(DataContracts.Address address, DataContracts.GeoLocation geoLocation)
{
return Json("test");
}
where DataContracts.Address and DataContracts.GeoLocation are complex types.
From my View i'm trying to post using jQuery as such:
function PostMoreData() {
var JsonAddress = {
"Building": $('Building').val(),
"UnitNumber": $('UnitNumber').val(),
"StreetNumber": $('StreetNumber').val(),
"StreetName": $('StreetName').val(),
"StreetType": $('StreetType').val(),
"Suburb": $('Suburb').val(),
"State": $('State').val(),
"Postcode": $('Postcode').val(),
"MonthsAtAddress": $('MonthsAtAddress').val()
};
var JsonGeoLocation = {
"Latitude": $('Latitude').val(),
"Longitude": $('Longitude').val()
};
jQuery.post("/AddressValidation/PostMoreData", {address: JsonAddress, geoLocation: JsonGeoLocation}, function(data, textStatus) {
if (textStatus == "success") {
var result = eval(data);
if (result.length > 0) {
alert(result);
}
}
}, "json");
}
However, on the controller, I get nulls.
It works if my Controller takes just 1 argument and I post just one object.
[AcceptVerbs(HttpVerbs.Post)]
public JsonResult PostMoreData(DataContracts.Address address)
{
return Json("test");
}
function PostMoreData() {
var JsonAddress = {
"Building": $('Building').val(),
"UnitNumber": $('UnitNumber').val(),
"StreetNumber": $('StreetNumber').val(),
"StreetName": $('StreetName').val(),
"StreetType": $('StreetType').val(),
"Suburb": $('Suburb').val(),
"State": $('State').val(),
"Postcode": $('Postcode').val(),
"MonthsAtAddress": $('MonthsAtAddress').val()
};
jQuery.post("/AddressValidation/PostMoreData", JsonAddress, function(data, textStatus) {
if (textStatus == "success") {
var result = eval(data);
if (result.length > 0) {
alert(result);
}
}
}, "json");
}
Any ideas how i can post more than one object?
Note that the "default serialization" that jQuery is doing here isn't going to work no matter what your controller does. jQuery doesn't "traverse" the parameter map below the first level, so the example in the question is likely generating this post data:
address=[object]&geoLocation=[object]
The other, working example does not contain any sub-objects, so it is being translated directly, like this:
Building=value&UnitNumber=value&...&MonthsAtAddress=value
The easiest fix is making the parameter map flat, each key prefixed with either 'Address.' or 'GeoLocation.', depending.
Thank you everyone for your input on this issue.
At this stage, we have departed from using jquery to post complex types to controllers. Instead we use the ms ajax framework to do that. ms ajax post nicely binds the complex types automatically out of the box.
So our solution now uses a mix of jquery and ms ajax framework.
Ash
Your code requires that the way jquery serializes an object is compatible with the MVC default model binder, which I think is unlikely.
If you can build your javascript object so that it serializes as a flat object with dot notation (JsonAddress.Building) that would work, or you can let jquery do the default serialization and then create a custom model binder to deserialize to the action parameter types.
I had the same problem and couldn't get anything to work. Also someone raised it as a bug with jquery and they closed it as not a bug.
I have found a few solutions which answer part of the whole question.
And the answer includes the following.
1) Client side: we would need to stringyfy all the objects you need to send. This could be a normal object or an array. It works on both.
2) Client side: You send the data as you have in the first post. As you would object by object.
Tip: When you send parameterised objects, jquery encodes the data sent to the server.
Following all are server side implementations
1) Deserializer class: which will take the input string and put it back in to object/list<>/IList<> what ever you have defined as datatype of the parameter of the controller function.
You would need to implement ActionFilterAttribute for the above.
2) Finally add an attribute to controller function, so that it uses the deserialiser class to get the parameters.
As this is quite a lot of code let me know if you need details or have you solved the problem.
Deepak Chawla
I have a complex JSON object which is sent to the View without any issues (as shown below) but I cannot work out how Serialize this data back to a .NET object when it is passed back to the controller through an AJAX call. Details of the various parts are below.
var ObjectA = {
"Name": 1,
"Starting": new Date(1221644506800),
"Timeline": [
{
"StartTime": new Date(1221644506800),
"GoesFor": 200
}
,
{
"StartTime": new Date(1221644506800),
"GoesFor": 100
}
]
};
I am not sure how this object can be passed to a Controller Method, I have this method below where the Timelines object mirrors the above JS object using Properties.
public JsonResult Save(Timelines person)
The jQuery I am using is:
var encoded = $.toJSON(SessionSchedule);
$.ajax({
url: "/Timeline/Save",
type: "POST",
dataType: 'json',
data: encoded,
contentType: "application/json; charset=utf-8",
beforeSend: function() { $("#saveStatus").html("Saving").show(); },
success: function(result) {
alert(result.Result);
$("#saveStatus").html(result.Result).show();
}
});
I have seen this question which is similar, but not quite the same as I am not using a forms to manipulate the data.
How to pass complex type using json to ASP.NET MVC controller
I have also seen references to using a 'JsonFilter' to manually deserialize the JSON, but was wondering if there is a way to do it nativly though ASP.NET MVC? Or what are the best practices for passing data in this way?
Edit:
This method should no longer be needed with the arrival of MVC 3, as it will be handled automatically - http://weblogs.asp.net/scottgu/archive/2010/07/27/introducing-asp-net-mvc-3-preview-1.aspx
You can use this ObjectFilter:
public class ObjectFilter : ActionFilterAttribute {
public string Param { get; set; }
public Type RootType { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext) {
if ((filterContext.HttpContext.Request.ContentType ?? string.Empty).Contains("application/json")) {
object o =
new DataContractJsonSerializer(RootType).ReadObject(filterContext.HttpContext.Request.InputStream);
filterContext.ActionParameters[Param] = o;
}
}
}
You can then apply it to your controller methods like so:
[ObjectFilter(Param = "postdata", RootType = typeof(ObjectToSerializeTo))]
public JsonResult ControllerMethod(ObjectToSerializeTo postdata) { ... }
So basically, if the content type of the post is "application/json" this will spring into action and will map the values to the object of type you specify.
You say "I am not using a forms to manipulate the data." But you are doing a POST. Therefore, you are, in fact, using a form, even if it's empty.
$.ajax's dataType tells jQuery what type the server will return, not what you are passing. POST can only pass a form. jQuery will convert data to key/value pairs and pass it as a query string. From the docs:
Data to be sent to the server. It is
converted to a query string, if not
already a string. It's appended to the
url for GET-requests. See processData
option to prevent this automatic
processing. Object must be Key/Value
pairs. If value is an Array, jQuery
serializes multiple values with same
key i.e. {foo:["bar1", "bar2"]}
becomes '&foo=bar1&foo=bar2'.
Therefore:
You aren't passing JSON to the server. You're passing JSON to jQuery.
Model binding happens in the same way it happens in any other case.
A different take with a simple jQuery plugin
Even though answers to this question are long overdue, but I'm still posting a nice solution that I came with some time ago and makes it really simple to send complex JSON to Asp.net MVC controller actions so they are model bound to whatever strong type parameters.
This plugin supports dates just as well, so they get converted to their DateTime counterpart without a problem.
You can find all the details in my blog post where I examine the problem and provide code necessary to accomplish this.
All you have to do is to use this plugin on the client side. An Ajax request would look like this:
$.ajax({
type: "POST",
url: "SomeURL",
data: $.toDictionary(yourComplexJSONobject),
success: function() { ... },
error: function() { ... }
});
But this is just part of the whole problem. Now we are able to post complex JSON back to server, but since it will be model bound to a complex type that may have validation attributes on properties things may fail at that point. I've got a solution for it as well. My solution takes advantage of jQuery Ajax functionality where results can be successful or erroneous (just as shown in the upper code). So when validation would fail, error function would get called as it's supposed to be.
There is the JavaScriptSerializer class you can use too. That will let you deserialize the json to a .NET object. There's a generic Deserialize<T>, though you will need the .NET object to have a similar signature as the javascript one. Additionally there is also a DeserializeObject method that just makes a plain object. You can then use reflection to get at the properties you need.
If your controller takes a FormCollection, and you didn't add anything else to the data the json should be in form[0]:
public ActionResult Save(FormCollection forms) {
string json = forms[0];
// do your thing here.
}
This answer is a follow up to DaRKoN_'s answer that utilized the object filter:
[ObjectFilter(Param = "postdata", RootType = typeof(ObjectToSerializeTo))]
public JsonResult ControllerMethod(ObjectToSerializeTo postdata) { ... }
I was having a problem figuring out how to send multiple parameters to an action method and have one of them be the json object and the other be a plain string. I'm new to MVC and I had just forgotten that I already solved this problem with non-ajaxed views.
What I would do if I needed, say, two different objects on a view. I would create a ViewModel class. So say I needed the person object and the address object, I would do the following:
public class SomeViewModel()
{
public Person Person { get; set; }
public Address Address { get; set; }
}
Then I would bind the view to SomeViewModel. You can do the same thing with JSON.
[ObjectFilter(Param = "jsonViewModel", RootType = typeof(JsonViewModel))] // Don't forget to add the object filter class in DaRKoN_'s answer.
public JsonResult doJsonStuff(JsonViewModel jsonViewModel)
{
Person p = jsonViewModel.Person;
Address a = jsonViewModel.Address;
// Do stuff
jsonViewModel.Person = p;
jsonViewModel.Address = a;
return Json(jsonViewModel);
}
Then in the view you can use a simple call with JQuery like this:
var json = {
Person: { Name: "John Doe", Sex: "Male", Age: 23 },
Address: { Street: "123 fk st.", City: "Redmond", State: "Washington" }
};
$.ajax({
url: 'home/doJsonStuff',
type: 'POST',
contentType: 'application/json',
dataType: 'json',
data: JSON.stringify(json), //You'll need to reference json2.js
success: function (response)
{
var person = response.Person;
var address = response.Address;
}
});
in response to Dan's comment above:
I am using this method to implement
the same thing, but for some reason I
am getting an exception on the
ReadObject method: "Expecting element
'root' from namespace ''.. Encountered
'None' with name '', namespace ''."
Any ideas why? – Dan Appleyard Apr 6
'10 at 17:57
I had the same problem (MVC 3 build 3.0.11209.0), and the post below solved it for me. Basically the json serializer is trying to read a stream which is not at the beginning, so repositioning the stream to 0 'fixed' it...
http://nali.org/asp-net-mvc-expecting-element-root-from-namespace-encountered-none-with-name-namespace/