Model Passed into MVC 4 Controller Null - ajax

I'm trying to serialize a form and pass it into a controller as a model. What I'm doing I've done in the past, but it's not working for some reason, so I suspect I am missing something stupid. Perhaps you can find it.
In my controller I have a method:
[HttpPost]
public ActionResult AddShippingLocation(PricingRequestModel model)
{
model.ShippingLocationsModel.Add(new ShippingLocationsModel());
return PartialView("shiplocationPartial", model);
}
In my view I have a script that looks like this:
function AddShippingLocation() {
$.ajax({
data: { model: $('#shippinginfoform').serialize() },
type: "POST",
url: "/PricingRequest/AddShippingLocation",
success: function (response) {
$('#shiplocation-wrapper').html(response);
}
})
}
This is called from a link that gets clicked. Also in the view I have a form that uses this:
#using (Html.BeginForm("AddShippingLocation", "PricingRequest", FormMethod.Post, new { id = "shippinginfoform" }))
{
I put the Addshippinglocation in as the method because I wanted to test to see if the model would be serialized using the built in helper. The model gets passed in properly using Html.BeginForm, it also gets passed in properly when using Ajax.BeginForm. When using jquery.serialize, though, it doesn't get passed in properly. On a side note, I'm using MVC 4. Any ideas? Thanks.
EDIT: Here's the request headers. The top one is a successful post of the model to the method, the bottom is the .serialize() that passes in a null model. I examined the post strings and the are the exact same.
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding gzip, deflate
Accept-Language en-US,en;q=0.5
Connection keep-alive
Cookie .ASPXAUTH=9F06BF2A7D03211E0D2ACEC26D7A568754C89F8A265EE61D9F8010BB8DF1D97670212F1E853FDE960E87AAC5DC7D364A251F670560448482517DA7C072864F62AC0C5C3E1EE8D375ACC1EA8F4D63CFC3C1DD28BBDCAC945155D15289DCDDA3B540756C0609611C13A438B5FF4CA747219290AFB51F58B8AD35AE40C01D3AFAF8B32ADD7E200148B1E1646400CAC0F116; ASP.NET_SessionId=v3qwt02dn1pd13posl5zzk3n
Host localhost:2652
Referer http://localhost:2652/PricingRequest/custinfo
User-Agent Mozilla/5.0 (Windows NT 6.1; WOW64; rv:16.0) Gecko/20100101 Firefox/16.0
Request Headers From Upload Stream
Content-Length 471
Content-Type application/x-www-form-urlencoded
Accept */*
Accept-Encoding gzip, deflate
Accept-Language en-US,en;q=0.5
Cache-Control no-cache
Connection keep-alive
Content-Length 555
Content-Type application/x-www-form-urlencoded; charset=UTF-8
Cookie .ASPXAUTH=9F06BF2A7D03211E0D2ACEC26D7A568754C89F8A265EE61D9F8010BB8DF1D97670212F1E853FDE960E87AAC5DC7D364A251F670560448482517DA7C072864F62AC0C5C3E1EE8D375ACC1EA8F4D63CFC3C1DD28BBDCAC945155D15289DCDDA3B540756C0609611C13A438B5FF4CA747219290AFB51F58B8AD35AE40C01D3AFAF8B32ADD7E200148B1E1646400CAC0F116; ASP.NET_SessionId=v3qwt02dn1pd13posl5zzk3n
Host localhost:2652
Pragma no-cache
Referer http://localhost:2652/PricingRequest/custinfo
User-Agent Mozilla/5.0 (Windows NT 6.1; WOW64; rv:16.0) Gecko/20100101 Firefox/16.0
X-Requested-With XMLHttpRequest

The request bodies are the same? Somehow, I'm doubtful.
Your ajax request body is going to have
model=....
where .... is your form serialized, which url encodes the inputs, and then the serialization itself is urlencoded. You're urlencoding twice with your ajax request. That doesn't happen with normal form posts, and urlencoding is not idempotent with respect to equal signs.
Try
data: $('#shippinginfoform').serialize(),
If the shippinginfoform form is the same form that's posted, I believe that should post the same data (well, generally: there may be some corner cases with values associated with submit buttons and such.).
I'll admit that there's some chance that I'm wrong, in which case I'll promptly delete this answer.

Related

GetMapping "produces" works even though doesn't match accept header

Intro
There is a #GetMapping attribute, as the following in one of our projects:
#GetMapping(path = "/", produces = SaConstants.SA_MEDIA_TYPE)
public HttpEntity<Resource<Home>> get(HttpServletResponse response) {
In the SaConstants class:
public static final String SA_MEDIA_TYPE="application/sa+json";
When I access the page from any internet browser, I am getting the proper response that I want - and my breakpoint in the controller is being triggered.
The browser is sending the following headers:
Host: 127.0.0.1:8001
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.104 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
DNT: 1
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.8
Cookie: io=Qt74kp5V5ziUNIxlAAAG
When I make a request to the page, without an Accept header, the page is not working.
If I add to postman the following Accept header, everything works:
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Question
My question are:
why does it work even if the Accept: header of the request doesn't match the produces attribute of the Rest Controller?
Why does it fail if no Accept header is provided (given the first question).
"*/*" means all types, this header is by default provided by most of the popular browsers

Angular's $http.post doesn't work, nor does its' $http... but jQuerys ajax does. Why?

For some reason this:
return jquery.ajax('my url', {
crossDomain : true
, data : JSON.stringify({"brand": self.current})
, type : 'POST'
}).success(function(data){
scope.results = data;
});
and/or this:
curl -X POST -H "Content-Type: application/json" -d '{"brand":"target"}' myUrl
work fine, but this:
var req = {
method: "POST"
, url : "my url"
, data : JSON.stringify({"brand": self.current})
};
return $http(req).
success(function(data){
scope.results = data;
});
fails miserably with
"OPTIONS my url (anonymous function) # angular.js:9866sendReq # angular.js:9667$get.serverRequest # angular.js:9383processQueue # angular.js:13248(anonymous function) # angular.js:13264$get.Scope.$eval # angular.js:14466$get.Scope.$digest # angular.js:14282$get.Scope.$apply # angular.js:14571(anonymous function) # angular.js:21571jQuery.event.dispatch # jquery.js:4430jQuery.event.add.elemData.handle # jquery.js:4116
(index):1 XMLHttpRequest cannot load my url. No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://localhost:5000' is therefore not allowed access. The response had HTTP status code 404."
They're the same url. Wtf.
I have a sneaking suspicion that the "crossDomain : true" option in jquery is why the jquery one works, but if that's the case, then the question is:
how do I do that with angular?
-- When using jquery's default ajax method, the scope isn't updating with the results, but i know the data is being assigned because i'm logging it out, and if i submit the request again, the scope does update with the second value.
Second question- why isn't my view updating with the results?
update:
The reason this is failing has nothing to do with the response I'm getting back from the server, the problem is that Angular is transforming this POST request into an OPTIONS request:
(taken from google chromes' xhr tool:)
Remote Address: the remote address
Request URL:the request endpoint
Request Method:OPTIONS <-------------
Status Code:404 Not Found
Further inspection reveals:
OPTIONS /my url HTTP/1.1 <--------------
Host: my urls host
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Access-Control-Request-Method: POST
Origin: http://localhost:5000
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36
Access-Control-Request-Headers: accept, charset, content-type
Accept: */*
Referer: http://localhost:5000/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
which is not what it should be doing because I'm specifically saying in the req object i'm passing to $http that this is a POST request.
...
So how do I make angular... NOT do that?
also- why is it doing that?
When you do a cross-origin request from your browser, all browsers hit the URL (provided in AJAX call) to confirm if the cross-origin request is available or not which is known as preflight request. https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
So, your server's endpoint must allow the preflight request in order to make this call work by setting some response headers like (an example in Groovy):
response.setHeader "Access-Control-Allow-Headers", "Content-Type"
response.setHeader "Access-Control-Allow-Methods", "POST,DELETE,PUT"
response.setHeader "Access-Control-Allow-Origin", "*"

Why the Symfony form is "not submitted" after data is correctly sent through AJAX?

I want to sent a Symfony form through AJAX (AngularJS).
However, even if data is clearly sent, the form is said by Symfony to be "not submitted" (no error actually, but some debug show that isSubmitted returns false)
Here is a test on FOSUserBundle registration form.
EDIT: although pictures appear small, they are sufficiently resolved so that they are perfectly readable if displayed at their full size.
Here are the logs when I use a standard submission from an HTTP form:
Here are the logs when I use AJAX submission:
I have disabled CSRF token
The method I use from AJAX is "post" so it should be ok since it is the default Symfony expects (and FOSUserBundle does not override that default AFAIK).
Other forms (outside of FOSUserBundle scope) are correctly sent with AJAX
Would you have any idea on the matter? Any pointer? Or any other log I could check?
EDIT:
I use the default FOSUserBundle registration controller.
I almost use the default FOSUserBundle registration form, but I have added a (currently) empty child form which I'll need it further in the development.
namespace NONG\SecurityBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class UserRegistrationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options) {}
public function getParent() {
return 'fos_user_registration';
}
public function getName() {
return 'nong_user_registration';
}
}
Headers and data sent with the HTML form (NB. added a dot before star in Accept so that the coloration stays correct):
POST /server/web/testfosuser/register/ HTTP/1.1
Host: mywebsite.com
Connection: keep-alive
Content-Length: 229
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/.*;q=0.8
Origin: http://mywebsite.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.111 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Referer: http://mywebsite.com/server/web/testfosuser/register/
Accept-Encoding: gzip, deflate
Accept-Language: fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4
Cookie: PHPSESSID=xxxxxxxxxxxxxxxxxxxxxxxxx
fos_user_registration_form%5Bemail%5D=bidon%40bidon.com&
fos_user_registration_form%5Busername%5D=bidon&
fos_user_registration_form%5BplainPassword%5D%5Bfirst%5D=bidon&
fos_user_registration_form%5BplainPassword%5D%5Bsecond%5D=bidon
Headers sent with the AJAX request: (NB. added a dot before star in Accept so that the coloration stays correct):
POST /server/web/testfosuser/register HTTP/1.1
Host: mywebsite.com
Connection: keep-alive
Content-Length: 229
Accept: application/json, text/plain, */.*
Origin: http://mywebsite.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.111 Safari/537.36
Content-Type: application/json;charset=UTF-8 application/x-www-form-urlencoded; charset=UTF-8
Referer: http://mywebsite.com/client/
Accept-Encoding: gzip, deflate
Accept-Language: fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4
Cookie: PHPSESSID=xxxxxxxxxxxxxxxxxxxxxxxxx
fos_user_registration_form%5Bemail%5D=bidonqdsfqsdf%40qsdf&
fos_user_registration_form%5Busername%5D=charles222&
fos_user_registration_form%5BplainPassword%5D%5Bfirst%5D=bidon&
fos_user_registration_form%5BplainPassword%5D%5Bsecond%5D=bidon
I had similar problems and i changed the following inside of the controller:
$user = new User();
$form = $this->createForm(
new UserRegistrationType(),
$user,
array(
'attr' => array('novalidate' => 'novalidate'),
'action' => $this->generateUrl('user_registration')
)
);
Turning of the html5 error bubbles and setting the action URL directly worked for me. If this is not working, show your JS code.
I finally managed to find the correction:
The correct URL is ...../register/ with a final / that I forgot when I did the AJAX request.
However, I'm not sure what are the mechanisms at stake here. The deepest I could go debuging (when using the wrong URL) was in HttpFoundationRequestHandler, where the $request->getMethod() call returned GET (instead of POST), thus not submitting the form.

REST method's won't PUT or POST to the server

I'm trying to get some REST methods working in my Spring app but seem to be running into little success. I'm obviously missing something but I can't tell for the life of me what it would be. Here is my controller:
#Controller
public class IndexController {
static Logger log = Logger.getLogger(IndexController.class);
#Autowired
private ProvisionService provisionService;
#RequestMapping(value="/home/data", method=RequestMethod.GET,
headers="Accept=application/json")
public #ResponseBody List<Provision> getData() {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String username = null;
if(principal instanceof UserDetails)
username = ((UserDetails)principal).getUsername();
return provisionService.getAllByUser(username);
}
//JSON put request - doesn't work currently
#RequestMapping(value="/home/data", method=RequestMethod.PUT,
headers="Content-Type=application/json")
#ResponseStatus(HttpStatus.NO_CONTENT)
public void updateProvisions(#RequestBody List<Provision> provisions) {
log.info("Provisions: " + provisions.toString());
}
#RequestMapping(value={"/","/home"}, method=RequestMethod.GET)
public void showIndex() {}
}
Here is the main part of JSP that utilizes it:
<sf:form id="homeForm" method="put" action="${homeData_url}"></sf:form>
The form is submitted through Javascript when the user clicks on a button. Anyway, things work fine for the GET. I get Json returned with my List of objects, no problems. I then display that using Dojo and so far so good. However, when I try to return the Json with this form I'm getting a 405 - Request method 'POST' not supported error. As you can see I've got the method handler in my Controller so I'm really not sure what I'm doing wrong. I've taken those handler's out of the Spring in Action 3 book and it also resembles what some Spring docs and stuff say to do, but obviously I'm missing a key component. Anyone have any thoughts?
I do have the HiddenHttpMethodFilter mapped in my web.xml which is why I'm using the Spring form tag.
Anyway, any thoughts or help are appreciated. Thank you.
------------------UPDATE------------------
Here are the headers after I click on the button and get the 405 error, if it helps:
http://localhost:8080/NFI/home
POST /NFI/home HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:7.0.1) Gecko/20100101 Firefox/7.0.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
DNT: 1
Connection: keep-alive
Referer: http://localhost:8080/NFI/home
Cookie: JSESSIONID=584AC21ADE4F214904B9E7E2370363EF
Content-Type: application/x-www-form-urlencoded
Content-Length: 11
HTTP/1.1 405 Method Not Allowed
Server: Apache-Coyote/1.1
Allow: GET, PUT
Content-Type: text/html;charset=utf-8
Content-Length: 1085
Date: Fri, 21 Oct 2011 15:39:26 GMT
Submitting a Form is done using POST. You get a "POST" not supported error.
Above, I see you are using a RequestMethod.PUT in your source code. There's no mention of POST at all.
Add you need to add a parameter _method with value PUT to your request. Not to the json content!
So in the first step I would change requested URL to /home/data?_method=PUT.
If this work you can search for an way how to add the _method parameter to the request content without disturbing the Json data.
You updated your question with the headers, could you also put the entire request out there (actual dumped values) to see the _method parameter(s) being sent?
Also, while I guess the headers=""-rules are valid they shouldn't be needed. You have a json converter bean that will do marshall and unmarshall based on content-type and accept headers, if no valid converter is found Spring will return an error.
The only reason to include it in the #RequestMapping would be if you had a method that actually did something else if you called it with xml instead of json and that sounds like a bad design.
Remove those header-rules and try again, make it as simple as possible and gradually add logic.

Problem with Spring 3 + JSON : HTTP status 406?

I'm trying to get a list of Cities by sending the State name through Ajax in my SpringMVC 3.0 project.
For the purpose, I've used the following call (using jQuery) in my JSP:
<script type="text/javascript">
function getCities() {
jq(function() {
jq.post("getCities.html",
{ stateSelect: jq("#stateSelect").val()},
function(data){
jq("#cities").replaceWith('<span id="cities">Testing</span>');
});
});
}
</script>
And here's my Controller code:
#RequestMapping(value = "/getCities", method = RequestMethod.POST)
public #ResponseBody List<StateNames> getCities(#RequestParam(value="stateSelect", required=true) String stateName,
Model model) {
// Delegate to service to do the actual adding
List<StateNames> listStates = myService.listCityNames(stateName);
// #ResponseBody will automatically convert the returned value into JSON format
// You must have Jackson in your classpath
return listStates;
}
But I get HTTP 406 error stating the following when i run it:
406 Not Acceptable
The requested resource is only capable of generating content not acceptable according to the Accept headers sent in the request.
I've used Jackson in my Maven dependencies & have defined in my context file.
I've googled extensively & I guess the problem is #ResponseBody is not automatically converting my List to appropriate JSON object.
My Firebug says:
Response Headers
Server Apache-Coyote/1.1
Content-Type text/html;charset=utf-8
Content-Length 1070
Date Sat, 12 Feb 2011 13:09:44 GMT
Request Headers
Host localhost:8080
User-Agent Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13
Accept */*
Accept-Language en-us,en;q=0.5
Accept-Encoding gzip,deflate
Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive 115
Connection keep-alive
Content-Type application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With XMLHttpRequest
Referer http://localhost:8080/MyApplication/
Content-Length 17
Cookie JSESSIONID=640868A479C40792F8AB3DE118AF12E0
Pragma no-cache
Cache-Control no-cache
Please guide me. What am i doing wrong?? HELP!!
As Peter had written in his comment, the cause of the problem is inability of Spring to load Jackson. It is not loaded by dependencies by default. After I've added the dependency
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<version>1.9.2</version>
</dependency>
the JSON was returned after typing the address in the browser, without any tricks with Accept headers (as it is supposed to do).
Tested on Tomcat 7.0.
You have incorrect response content type it supposed to be application/json.
You need to add jackson to your /lib directory.
and you should have
<mvc:annotation-driven />
In your serlvet-name.xml file.
In addition I recommend you to map your request as get and try to browse it with Google Chrome,to see if it returns correct result. It has very good json representation.
The problem is not on server side, but on the client one.
Take a look at the error message carefully: The requested resource (generated by server side) is only capable of generating content (JSON) not acceptable (by the client!) according to the Accept headers sent in the request.
Examine your request headers:
Accept */*
Try this way:
function getCities() {
jq(function() {
jq.post(
"getCities.html", // URL to post to
{ stateSelect: jq("#stateSelect").val() }, // Your data
function(data) { // Success callback
jq("#cities").replaceWith('<span id="cities">Testing</span>');
},
"json" // Data type you are expecting from server
);
});
}
This will change your Accept header to the following (as of jQuery 1.5):
Accept: application/json, text/javascript, */*; q=0.01
This will explicitly tell the server side that you are expecting JSON.
Using jQuery , you can set contentType to desired one (application/json; charset=UTF-8' here) and set same header at server side.
REMEMBER TO CLEAR CACHE WHILE TESTING.
I too had a similar problem while using the Apache HTTPClient to call few services. The problem is the client and not the server. I used a HTTPRequester with header accepting application/json and it worked fine.

Resources