I am building an autosave feature in my Play app using JQuery in the model and processing the request in the controller.
I originally posted (Post 1) this:
Play Framework JQuery AJAX REST Post call returns Bad Request error
but that was marked as a possible duplicate of this (Post 2):
play framework routes trouble (400 bad request)
I received no answers on my original post (Post 1), so I was instructed to repost my question.
I followed Post 2 and removed the parameter I was passing since it was a possible issue with my request, based on Post 2. The documentation referenced in Post 2 was in Scala (and for an older version of Play - I am using 2.5) and my app in is in Java, so it did not really help me. I found this in the Play documentation:
https://www.playframework.com/documentation/2.5.x/JavaJsonActions
I read and followed that document and updated my code.
However, I still am receiving the Bad Request (400) or Not Found (404) error. I don't receive any detailed messages, so I am not sure where I am making a mistake.
Here is what is returned in the Chrome console:
addptp:414 POST http://localhost:9000/autosave 404 (Not Found)
send @ jquery-3.2.1.min.js:4
ajax @ jquery-3.2.1.min.js:4
(anonymous) @ addptp:414
each @ jquery-3.2.1.min.js:2
each @ jquery-3.2.1.min.js:2
autosave @ addptp:413
(anonymous) @ addptp:185
Here is the JQuery in the view:
var timer;
var restURL = window.location.protocol + "//" + window.location.hostname + (window.location.port == "" ? "" : (":" + window.location.port));
$(document).ready(function() {
// JSON REST Autosave...
timer = setInterval(function() {
autosave();
}, 6000);
});
function autosave() {
alert("Autosave");
$('form').each(function() {
$.ajax({
type: "POST",
url: restURL + "/autosave",
data: $(this).serialize(),
dataType: "json",
contentType: "application/x-www-form-urlencoded; charset=utf-8",
success: function(data) {
alert("Success!");
},
error: function(data) {
alert("Error!");
}
});
});
}
Here is my controller code in Java:
@BodyParser.Of(BodyParser.FormUrlEncoded.class)
public Result restAutosaveAdultPTP() {
// Let's get the current request in JSON and parse...
Map<String, String[]> json = request().body().asFormUrlEncoded();
if (json == null) {
return badRequest("Expecting Json data");
} else {
return ok("json: " + json);
}
}
Here is my routes
file entry:
POST /autosave controllers.Application.restAutosaveAdultPTP()
Here is the request and response info...
General:
Request URL:http://localhost:9000/autosave
Request Method:POST
Status Code:404 Not Found
Remote Address:[::1]:9000
Referrer Policy:no-referrer-when-downgrade
Response Headers:
HTTP/1.1 404 Not Found
Content-Length: 26917
Content-Type: text/html; charset=utf-8
Date: Thu, 25 Jan 2018 18:34:15 GMT
Request Headers:
POST /autosave HTTP/1.1
Host: localhost:9000
Connection: keep-alive
Content-Length: 2739
Accept: application/json, text/javascript, */*; q=0.01
Origin: http://localhost:9000
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://localhost:9000/addptp
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: PLAY_SESSION=a37c3087c9d5d3f4129057c964ca9a90eef80fe5-SESSION_ID=kfs7q8vohk3ammlfcnfka0j1p9&email=dzeller44%40gmail.com&userTimeout=1516880046558
Payload
legacy_Provider_Id=&new_Provider_Id=&provider_Name=&alternate_Name_DBA=&setting_Type=&waivers_Served=&total_Number_Served=&total_Number_on_Waivers=&street_Address=&apartment=&city=&state=&zip=&phone=&email1=&email2=&contact_Person=&updates=&main_PTP_Address=&duplicate_docs=&other_Rights_NonCompliance_Description=&other_Remedy_for_Rights_Description=&details_for_Rights_Action_Plan=&remedy_Rights_Incurred_Cost_Description=&remedy_Rights_One_Time_Costs=&remedy_Rights_Recurring_Annual_Costs=&person_for_Rights_Action_Plan=&status_Rights_Action_Plan=---+Select+Status+---&other_Choice_NonCompliance_Description=&other_Remedy_for_Choice_Description=&detail_for_Choice_Action_Plan=&remedy_Choice_Incurred_Cost_Description=&remedy_Choice_One_Time_Costs=&remedy_Choice_Recurring_Annual_Costs=&person_for_Choice_Action_Plan=&status_of_Choice_Action_Plan=---+Select+Status+---&other_Integration_NonCompliance_Description=&other_Remedy_for_Integration_Description=&detail_for_Integration_Action_Plan=&remedy_Integration_Incurred_Cost_Description=&remedy_Integration_One_Time_Costs=&remedy_Integration_Recurring_Annual_Costs=&person_for_Integration_Action_Plan=&status_for_Integration_Action_Plan=---+Select+Status+---&other_Remedy_for_Institutional_Factors_Description=&detail_for_Institutional_Action_Plan=&remedy_Institutional_Incurred_Cost_Description=&remedy_Institutional_One_Time_Costs=&remedy_Institutional_Recurring_Annual_Costs=&person_for_Institutional_Action_Plan=&status_of_Institutional_Action_Plan=---+Select+Status+---&ptp_Edited_Date=&ptp_Status_Notes=&update_Due=&compliance_Status=---+Select+Status+---&ensures_Rights=---+Select+Status+---&optimizes_Autonomy=---+Select+Status+---&selected_by_Person=---+Select+Status+---&options_in_PC_Plan=---+Select+Status+---&facilitates_Choice=---+Select+Status+---&integrated_in_Community=---+Select+Status+---&enforceable_Lease=---+Select+Status+---&eviction_Protections=---+Select+Status+---&bedroom_Privacy=---+Select+Status+---&bedroom_Locks=---+Select+Status+---&choice_of_Roommates=---+Select+Status+---&freedom_to_Decorate=---+Select+Status+---&free_Schedule_Access_to_Food=---+Select+Status+---&visitors_any_Time=---+Select+Status+---&physically_Accessible=---+Select+Status+---&modifications_PC_Plan=---+Select+Status+---&setting_NonInstitutional=---+Select+Status+---&heightened_Scrutiny_Description=&cdphe_Lead=&site_Visit_Status=---+Select+Status+---&site_Visit_Date=&site_Visit_Team=&documents_Detail=&findings_from_Documents=&summary_of_Findings=&promising_Practices=&additional_Notes=&follow_up_Visit_Status=---+Select+Status+---&additional_Notes_2=&follow_up_Date=&follow_up_Team=&documents_Detail_2=&findings_from_Documents_2=&summary_of_Findings_2=&promising_Practices_2=
As noted in the comments below, why is it looking for json and not a form-url-encoded in the Request Headers:
Accept: application/json, text/javascript, */*; q=0.01
I appreciate any direction or help you can provide.
The problem is that $(this).serialize()
is encoding the data in URL-encoded notation but the @BodyParser.Of(BodyParser.Json.class)
annotation and the request().body().asJson()
code in your controller show that you're expecting JSON encoding.
Docs for serialize
: https://api.jquery.com/serialize/
The
.serialize()
method creates a text string in standard URL-encoded notation.
Instead of using JSON's serialize
method you probably want to use something like JavaScript's stringify
: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
The JSON.stringify() method converts a JavaScript value to a JSON string
Alternatively you can change your Play code to expect a URL-encoded value. See the docs about FormUrlEncoded
here: https://www.playframework.com/documentation/2.6.x/JavaBodyParsers
FormUrlEncoded
: Parses the body as a form.
If you change the Play behaviour, remember to also change the contentType
you send so it's application/x-www-form-urlencoded
instead of application/json
.