Why isn't my form data getting to my #RequestBody - javascript

I am using Spring. I have a controller that creates a page for the user, who then enters data. I need to capture that data and put in a database
My Controller
#Controller
#RequestMapping({"/pdc"})
public class PDCController {
// generate page to send to user
#RequestMapping(value = {"/{ffaAssgnId}"}, method = RequestMethod.GET)
public String getAssignment(Model model, #PathVariable Integer ffaAssgnId) {
Integer userContactId = userService.getUserContactId();
PDCAssgn assignment = pdcService.getAssgn(ffaAssgnId, userContactId);
List<MagValidate> statuses = validateService.getValidateByType("FAS");
Map<Integer, MagValidate> mapStatuses = new HashMap<>();
for (MagValidate status : statuses) {
mapStatuses.put(status.getValidateId(), status);
}
model.addAttribute("mapStatus", mapStatuses);
model.addAttribute("assignment", assignment);
return "pdc";
}
// get response from user
#RequestMapping(value = {""}, method = RequestMethod.POST)
public #ResponseBody AjaxResponse updateAssignment(#RequestBody PDCAssgn assignment) {
System.out.println(assignment.toString());
// try {
// pdcService.updateAssgn(assignment);
// } catch (Exception ex) {
// ex.printStackTrace();
// List<String> errors = new ArrayList<>();
// errors.add("Error saving DARF.");
// return new AjaxResponse("ERROR", null, errors);
// }
return new AjaxResponse("OK", null, null);
}
}
Part of my rendered form
<td><input id="wholesalerIssues0.assgnIssue.promoDealers" name="wholesalerIssues[0].assgnIssue.promoDealers" class="numberLarge" type="text" value=""/></td>
<td><input id="wholesalerIssues0.assgnIssue.dealersFound" name="wholesalerIssues[0].assgnIssue.dealersFound" class="numberLarge" type="text" value=""/></td>
<td><input id="wholesalerIssues0.assgnIssue.curDraw" name="wholesalerIssues[0].assgnIssue.curDraw" class="numberLarge" type="text" value=""/></td>
<td><input id="wholesalerIssues1.assgnIssue.promoDealers" name="wholesalerIssues[1].assgnIssue.promoDealers" class="numberLarge" type="text" value=""/></td>
<td><input id="wholesalerIssues1.assgnIssue.dealersFound" name="wholesalerIssues[1].assgnIssue.dealersFound" class="numberLarge" type="text" value=""/></td>
<td><input id="wholesalerIssues1.assgnIssue.curDraw" name="wholesalerIssues[1].assgnIssue.curDraw" class="numberLarge" type="text" value=""/></td>
Equivalent jsp
<td><form:input path="wholesalerIssues[${whStatus.index}].assgnIssue.promoDealers" cssClass="numberLarge" placeholder="${targetTitle.promoDealers}"/></td>
<td><form:input path="wholesalerIssues[${whStatus.index}].assgnIssue.dealersFound" cssClass="numberLarge" placeholder="${targetTitle.dealersFound}"/></td>
<td><form:input path="wholesalerIssues[${whStatus.index}].assgnIssue.curDraw" cssClass="numberLarge" placeholder="${targetTitle.curDraw}"/></td>
The javascript to send the data
function doSendA(e) {
e.preventDefault();
var form = $("#frm-assignment");
var formFields = $("#frm-assignment input:not([readonly])")
var formData = {}
$.each(formFields, function(i, v){
var input = $(v);
// populate form data as key-value pairs
// with the name of input as key and its value as value
formData[input.attr("name")] = input.val();
});
console.log(formData);
$.ajax({
type: "POST",
url: "${pageContext.request.contextPath}/pdc",
contentType: "application/json; charset=utf-8" ,
data: JSON.stringify(formData),
dataType: "json",
success : function(ajaxResponse) {
if (ajaxResponse.status !== "OK") {
hideLoading();
showToast("bad", ajaxResponse.errors[0]);
return;
}
hideLoading();
// location.reload();
showToast("good", "Your data was saved.");
},
error : function(res) {
hideLoading();
showToast("bad", "There was a problem saving your data.");
}
});
}
The console.log(formData);
{"wholesalerIssues[0].assgnIssue.promoDealers":"123","wholesalerIssues[0].assgnIssue.dealersFound":"","wholesalerIssues[0].assgnIssue.curDraw":"","wholesalerIssues[1].assgnIssue.promoDealers":"","wholesalerIssues[1].assgnIssue.dealersFound":"","wholesalerIssues[1].assgnIssue.curDraw":""}
The controller's System.out.println(assignment.toString());
magforce.model.PDCAssgn#4f71f251[wholesalerIssues=<null>]
I played around with printing out things from the request and found that there were no errors during the binding, but my resultant data object remains empty. I have done many Google queries, but haven't yet found anything that uses Spring's form tags (<form:form> , <form:input>, etc.) plus ajax json and Spring's #RequestBody.

Not a real answer, but putting it here as an expansion to my comment. The goal here is to show you how you could test your endpont.
Your controller is exposing an endpoint. If you start up your application you have a URL you can access, in this case at "http://localhost:8080/pdc" (unless you are prefixing your URL, but based on what your are posting this should be your URL). If you are on a *NIX system you have curl at your disposal. If you are on Windows you might want to give Postman a try. To use curl on your POST endpoint, a call to your endpoint would be something like this. Of course I don't know your object structure so make sure to adjust the JSON according to the structure of your object.
curl -i -H "Accept: application/json" -H "Content-Type: application/json" \
-X POST -d \
'{
"yourObject":{
"someArray":[
{
"aDate":{
"year":2019,
"month":10,
"day":1
},
"someValue":4,
"otherVaule":10000,
"anId":1
}
],
"someOtherValue":100000,
"someStatus":"DRAFT"
}
}' http://localhost:8080/pdc
By testing your endpoint using a tool like this you can isolate the problem you are having. Once you have a successful cal to your endpoint you will at least know that your endpoint is in order and that any problems will lie in your JavaScript code.

Related

Sending photos and multiple data to the controller with ajax

I am sending the selected photo to my controller in asp.net core with ajax and javascript. Thanks to the code below:
<form id="form" name="form" style="display:none;" method="post" enctype="multipart/form-data">
<div class="row">
<div class="col-md-12">
<input onchange="uploadFiles('files');" class="form-control" type="file" id="files">
</div>
</div>
</form>
My javascript and ajax codes:
var sliderFotografEkle = "#Url.Action("sliderFotoKayit", "slider")";
function uploadFiles(inputId) {
var input = document.getElementById(inputId);
var files = input.files;
var formData = new FormData();
for (var i = 0; i != files.length; i++) {
formData.append("files", files[i]);
}
$.ajax(
{
url: sliderFotografEkle,
data: formData,
processData: false,
contentType: false,
type: "POST",
success: function (data) {
alert("Files Uploaded!");
}
}
);
}
It only takes formData in the data part. I want to send a second data. It doesn't send. Probably
processData: false,
contentType: false, from what I gave. but if i don't give them it doesn't send the photo this time. Please help me.
this is where the parameters are given in the controller:
[HttpPost]
public async Task<IActionResult> sliderFotoKayit(slider item, List<IFormFile> files)
I already tested your code on ASP.NET Core 3.1 and found no problem uploading one file at a time.
As I understand, you problem is that you cannot choose and send multiple files at once.
For choosing and sending multiple files at once, your html input tag must have a multiple attribute. Otherwise, it only allows you to choose one file at a time.
<input onchange="uploadFiles('files');" class="form-control" type="file" id="files" multiple>
Updated after the author provided more context:
For any additional value you want to send in your form data, just you append(key: string, value: any) method.
Updated JS with an additional int field:
function uploadFiles(inputId) {
var input = document.getElementById(inputId);
var files = input.files;
var formData = new FormData();
for (var i = 0; i != files.length; i++) {
formData.append("files", files[i]);
}
formData.append("id", 3); // append additional int field here
$.ajax(
{
url: sliderFotografEkle,
data: formData,
processData: false,
contentType: false,
type: "POST",
success: function (data) {
alert("Files Uploaded!");
}
}
);
}
</script>
Controller method signature to receive files and an additional int field:
[HttpPost]
public async Task<IActionResult> HandleFileUpload(int id, List<IFormFile> files)
Some more details:
When you send form data to the server, request will have content-type: multipart/form-data;. As one HTTP request can just have one content-type, there are 2 choices:
The easy way: Add new fields to form, either through input fields in html or formData.append(key, value) in js. This is recommended because:
content-type: multipart/form-data; can include both files and any other types that HTML form support.
content-type: multipart/form-data; has built-in support in ASP.NET, so form content will automatically bind to controller method parameters.
The hard way: Serialize formdata into string, then return content-type: application/json;. Do this if you don't have control over the server, usually in cases when you have to conform to the API of another server. This is clearly more flexible, but also more complicated.
You will be able to have the data in the form:
{
"formData": serializedFormData,
"id": id
}
The biggest drawback is that the server then have to manually deserialize the form data.

Getting null in spring-boot REST request param from HTML form

I've an HTML form where I'm getting 2 inputs, which is submitted to a spring boot REST api. But in this simple application, I'm still receiving null as request in backend api.
Form
<div>
<label>alphaID</label>
<div>
<input id="alphaID" name="alphaID" type="text"/>
</div>
</div>
<div>
<label class="col-md-4 control-label">Domain Name</label>
<div class="col-md-4">
<input id="domain" name="domain" type="text"/>
</div>
</div>
Upon submit, I'm calling ajax call, like:
function formSubmit() {
$("#productForm").submit(function(e) {
e.preventDefault();
var requestJson = createRequestJSON();
var url = config.myurl;
$.ajax({
url: url,
type : "POST",
data: JSON.stringify(requestJson),
success: function( data, textStatus, jQxhr ) {
console.log("sucess: " + data);
},
error: function( jqXhr, textStatus, errorThrown ){
console.log( "error: " + errorThrown );
}
});
});
}
The backend is a spring-boot application with REST call:
#RequestMapping(value = "/validate", method = RequestMethod.POST,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public Map<String, List<String>> validate(#Valid MyDTO myDTO) {
System.out.println(myDTO.getId()); // GETTING null
System.out.println(myDTO.getDomain()); // GETTING null
}
MyDTO.java
public class MyDTO {
#JsonProperty("alpha_id")
private String alphaID;
#JsonProperty("domain")
private String domain;
....
}
Change your Content-Type to
consumes = MediaType.APPLICATION_JSON_VALUE
Add #RequestBody annotation
public Map<String, List<String>> validate(#Valid #RequestBody MyDTO myDTO)
Make sure you are calling proper URL and sending proper content-type from your browser request too.
It might be interesting to see if your requestJson actually has the correct format in order for the MyDTO to consume it.
You also don't have to Json.stringify you data. When you do this, you basically just send an string to the backend. The backend does not know that it hast to parse this string to get a valid document. You either just send the JavaScript object directly in the data property or you change the API to expect a String and parse it in the function later.

Issue with my Ajax & JQuery using Spring MVC in java

My jQuery AJAX implementation does not work properly, so if i want add, delete, update a product, or retrieve all sites, it simply does not react to my clicks.
Here is my simple web-page that allows me to click but i donot get any result.
When someone wants to submit the form with the specified ID, all form fields are assigned to appropriate variables. After that, a new JSON document is generated based on the form field variables. Then the AJAX call is performed. It is directed to URL which is specified in the action attribute of form tag. The JSON is used as a data which needs to be processed.
You can downlaod my project from here
I get there errors:
localhost:8080/api/sites Failed to load resource: net::ERR_CONNECTION_REFUSED
2localhost:8080/api/sites/ Failed to load resource: net::ERR_CONNECTION_REFUSED
From my Java class:
#RequestMapping(method = RequestMethod.GET, value = "/api/sites")
public List<Site> getAllSites(){
return siteService.getAllSites();
}
#RequestMapping(method = RequestMethod.POST, value = "/api/sites")
public void addSite(#RequestBody Site site){
siteService.addSite(site);
}
A webpage to replicate the problem:
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
</head>
<body>
<hr><p> New page </p>
<input name="search" type="text" maxlength="100" id="search"/>
<button onclick="getAllSites()"> Show All </button>
<hr>
<hr>
<p> Id: <input name="search" type="text" maxlength="100" id="id"/></p>
<p> First name: <input name="search" type="text" maxlength="100" id="name"/></p>
<button onclick="addSite()"> Save </button>
<div id="site"></div>
<script>
function addSite()
{ var data = {
id: document.getElementById("id").value,
name: document.getElementById("name").value
}
$.ajax({
url: "http://localhost:8080/api/sites",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
type: "POST",
dataType: "json",
data: JSON.stringify(data)
});
}
function getAllSites()
{
$("#site").html("");
$.getJSON("http://localhost:8080/api/sites/", function(data)
{
for (var i in data) {
$('#site').append("<p>ID: " + data[i].id + "</p>")
$('#site').append("<p>Name: " + data[i].name + "</p>")
}
});
}
</script>
</body>
</html>
Assuming that the ajax implementation is in a file for example: mySites.html and you need to test it directly from browser or from other site try this:
Go to SiteController.class
If you need to test your api outside of your localhost:8080 then add #CrossOrigin annotation to the specific method of SiteController.class, for example in this case I'm enabling CrossOrigin for two specific methods:
Method 1
#CrossOrigin(origins = "*")
#RequestMapping(method = RequestMethod.GET, value = "/api/sites")
public List<Site> getAllSites(){
return siteService.getAllSites();
}
Method 2
#CrossOrigin(origins = "*")
#RequestMapping(method = RequestMethod.POST, value = "/api/sites")
public void addSite(#RequestBody Site site){
siteService.addSite(site);
}
For more information about #CrossOrigin see the following documentation from spring site #CrossOrigin Document
My conclusion of this answer is because the first time that I ran the ajax implementation I got the No 'Access-Control-Allow-Origin' header exception from console developer, wich means that for security reasons the browser block the ajax request when it is outside of the rest api host.
All the rest of the components (backend java classes, spring) worked perfect after I get imported to my development environment.

How model bind Javascript FormData with Asp.net Controllers Model

Is it possible to automatically bind ASP.NET controllers model with an ajax request that submits data as FormData.
in my provided example I'm required to use HttpContext.Current.Request.Form["property_name"]
to receive data because if I provide a model that is identical to the submitted form data, all values are equal to null;
or does ASP.NET model binding only work on JSON requests ?
Simple code bellow:
View:
#using (Html.BeginForm("Post", "Test", FormMethod.Post, new { #class="test-form"}))
{
<input type="text" name="firstName"/>
<input type="text" name="lastName"/>
<button type="submit">Submit</button>
}
Scripts:
<script>
$('.test-form').on('submit', function (e) {
e.preventDefault();
var formData = new FormData(this);
$.ajax({
url: "#Url.Action("TestPost", "Test")",
method: "POST",
data: formData,
processData: false,
success: function(e){
}
});
});
</script>
Controller:
[HttpPost]
public ActionResult TestPost()
{
var firstname = HttpContext.Current.Request.Form["firstName"];
var lastName = HttpContext.Current.Request.Form["lastName"];
return PartialView("TestPost");
}
Does Not Work Controller:
public class User
{
public string firstName { get; set; }
public string lastName { get; set; }
}
[HttpPost]
public ActionResult TestPost(User model) //model values are null
{
return PartialView("TestPost");
}
When you use a FormData object with ajax the data is sent as multipart/form-data and the content type header is set automatically for you with the correct boundary.
You can override the content type and set tit to whatever you want, which is what happens here.
You might be thinking well I didn't do it, well you good friend jQuery did it for you. It set the default content type for $.ajax for you (application/x-www-form-urlencoded) which pretty much craps up the request.
To stop this action i.e. to stop jQuery from setting a content type header you have to set the contentType parameter to false.

asp.net mvc get value from Html.TextBoxFor()

I'm new to MVC(Asp.net) as well as JavaScript. sorry for this primary question.
<body>
<div>
<%--<% Html.BeginForm(); %>--%>
<table>
<tr>
<td><%:Html.LabelFor(t=> t.CustomerID) %></td>
<td><%:Html.TextBoxFor(t => t.CustomerID, new { ID = "txtCustomerID" })%></td>
</tr>
<tr>
<td><%:Html.LabelFor(t=> t.CustomerName) %></td>
<td><%:Html.TextBoxFor(t => t.CustomerName, new { ID = "txtCustomerName" })%></td>
</tr>
<tr>
<td>
<input type="submit" value="Submit" onclick="CheckName()" />
</td>
</tr>
</table>
<%--<% Html.EndForm(); %>--%>
</div>
</body>
I need to get t.CustomerName value using JavaScript. I tried as below and it gives me errors.
<head runat="server">
<script type="text/javascript">
function CheckName() {
var value = document.getElementById('<%=t.CustomerName%>').value;
alert(value);
}
function SubmitData() {
var obj = {};
obj.CustomerID = $("#txtCustomerID").val();
obj.CustomerName = $("#txtCustomerName").val();
$.ajax({
url: "CreateNewRetailCustomer?jsonObject=" + JSON.stringify(obj),
type: 'POST',
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: function (result) {
if (result.status == "successful") {
document.location.href = '../';
}
}
});
}
</script>
<title>Create Wholesale Customer</title>
</head>
i wrote my controller coding as below :
public ActionResult CreateRetailCustomer()
{
return View();
}
[HttpPost]
public ActionResult CreateRetailCustomer(RetailCustomer retCust)
{
new CustomerService.CustomerServiceClient().SaveRetailCustomer(retCust);
return RedirectToAction("Index");
}
[HttpPost]
public JsonResult CreateRetailCustomer(string jsonObject)
{
var retailCustomer = new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize<Models.RetailCustomer>(jsonObject);
new CustomerService.CustomerServiceClient().SaveRetailCustomer(retailCustomer);
return Json(new { status = "successful" }, JsonRequestBehavior.AllowGet);
}
[HttpGet]
public JsonResult GetCustomerList()
{
var custlist = new CustomerService.CustomerServiceClient().GetCustomer().Select(m => new { CustomerId = m.CustomerId, CustomerName = m.CustomerName});
return Json(custlist, JsonRequestBehavior.AllowGet);
}
error:
An error occurred during the compilation of a resource required to
service this request. Please review the following specific error
details and modify your source code appropriately.
I searched and I found similar question asp.net mvc get value from Html.textbox() here. but it doesn't discussed about how to get value from Html.TextBoxFor so how can I do this?
var value = document.getElementById('<%=t.CustomerName%>').value;
This line is causing the error. ASP.NET MVC doesn't generate random client side ids like in Web Forms. They will be the same property name in your model. This line is what you are looking for:
var value = document.getElementById('CustomerName').value;
Also HTML helpers like TextBoxFor() take some delegates as a parameter. t variable you are trying to use is meaningful in the context of a lambda expression(a short way to create delagates and expressions). If you want to reach a model property, use Model variable.
A clean way to get the ID is to use IdFor(). This allows hierarchical IDs to be resolved and avoids hardcoding the string name for the property into your code.
function CheckName() {
var value = document.getElementById('<%:Html.IdFor(t => t.CustomerName) %>').value;
alert(value);
}

Categories