handle form post with a array of items in spring MVC - spring-mvc

I'm trying to send some data from the client side to the server, and have it processed into a file download.
I'm using a simple HTML form because I want to initialize a file download (and not AJAX).
one of the form fields is an array of items. (the other two are name and description strings).
I'm serializing this field to a string (JSON.stringify) before submitting the form.
on the server side I tried a million techniques (#ModelAttribute vs. #RequestBody, different jackson mapping bean configurations) to either convert this to a single type or to three separate types (String + String + List/Array).
the examples I found were only for AJAX...
can anyone supply a working example or a description of one?
=======
Update:
I've implemented a workaround by JSON.stringify-ing the collection and passing it in one of the inputs,
and on the server side I have:
#RequestMapping(method = RequestMethod.POST, value = "exportSectionsToExcel")
public HttpEntity<byte[]> createExcelWorkBook(#ModelAttribute ExportSectionsListForm exportSectionsListForm) {
Section[] sectionObjects = gson.fromJson(exportSectionsListForm.getSections(), Section[].class);
...
with ExportSectionsListForm object containing strings only:
public class ExportSectionsListForm {
private String name;
private String url;
private String rssUrl;
private String sections;
...
(omitting ctor, getters and setters)
additionally, I found this promising link:
http://viralpatel.net/blogs/spring-mvc-multi-row-submit-java-list/
but didn't try it - seems like I'll need to dynamically generate input elements for this to work, but it might actually be the right solution. has anyone tried this?

The #ModelAttribute tag will try to build the object based on form postings. Since you are serializing your form values to JSON, this wont work. #RequestBody simply gives you a String representing the request body. So, you could get the String representing the JSON being passed in, then demarshal the JSON using Jackson of FlexJSON (or whatever JSON library you use). I am not sure this is the best approach, though.
I would question why you need to serialize the form to JSON to begin with. Spring handles forms with Lists/Maps just fine. Simply submit the form using the #ModelAttribute, making your "array" and List, or whatever you are expecting, on the Controller. So, if I am interpreting your example correctly, my ModelAttribute would look like:
public class ExportSectionsFormBean {
private String name;
private String url;
private String rssUrl;
private List<String> sections;
/* getters/setters */
}
Then my Controller method would look like:
#RequestMapping(method = RequestMethod.POST, value = "exportSectionsToExcel")
public HttpEntity<byte[]> createExcelWorkBook(#ModelAttribute ExportSectionsFormBean exportSectionsFormBean ) {
/* Do whatever with your */
}
On the form side, using the Spring JSTL tags, simply make your "sections" fields look like:
<form:input path="sections[0]" />
<form:input path="sections[1]" />
Or, if you'd rather use HTML, then
<input type="text" name="sections[0]" id="sections0" />
<input type="text" name="sections[1]" id="sections1" />
Which is what gets generated by the above JSTL tags. As long as the values for "sections" is put in the HTTP request as 'section[#]=value', you are all set.

I have been working on the same issue. And if i have several inputs witht eh same name such as:
<input name="somename"/>
<input name="somename"/>
<input name="somename"/>
and i have a form mapped to my method like this:
#ModelAttribute("ReturnsAndExchangesForm") ReturnsAndExchangesForm returnsAndExchangesForm
and in that form i have getters and setters for a property named:
String[] somename , spring is passing those values into that array nicely!

Related

What the difference between [FromRoute] and [FromBody] in a Web API?

What the difference between [FromRoute] and [FromBody] in a Web API?
[Route("api/Settings")]
public class BandwidthController : Controller
{
// GET: api/Settings
[HttpGet]
public IEnumerable<Setting> GetSettings()
{
return _settingRespository.GetAllSettings();
}
// GET: api/Settings/1
[HttpGet("{facilityId}", Name = "GetTotalBandwidth")]
public IActionResult GetTotalBandwidth([FromRoute] int facilityId)
{
if (!ModelState.IsValid)
{
return HttpBadRequest(ModelState);
}
}
}
Also for PUT:
// PUT: api/Setting/163/10
[HttpPut]
public void UpdateBandwidthChangeHangup([FromRoute] int facilityId, int bandwidthChange)
{
_settingRespository.UpdateBandwidthHangup(facilityId, bandwidthChange);
}
Can I use [FromBody]?
FromBody
Specifies that a parameter or property should be bound using the request body.
When you use FromBody attribute you are specifying that the data is coming from the body of the request body and not from the request URL/URI. You cannot use this attribute with HttpGet requests, only with PUT,POST,and Delete requests. Also you can only use one FromBody attribute tag per action method in Web API (if this has changed in mvc core I could not find anything to support that).
FromRouteAttribute
Summary: Specifies that a parameter or property should be bound using route-data from the current request.
Essentially it FromRoute will look at your route parameters and extract / bind the data based on that. As the route, when called externally, is usually based on the URL. In previous version(s) of web api this is comparable to FromUri.
[HttpGet("{facilityId}", Name = "GetTotalBandwidth")]
public IActionResult GetTotalBandwidth([FromRoute] int facilityId)
So this would try to bind facilityId based on the route parameter with the same name.
Complete route definition: /api/Settings/GetTotalBandwidth/{facilityId}
Complete received url: /api/Settings/GetTotalBandwidth/100
Edit
Based on your last question, here is the corresponding code assuming you want 163 to be bound to facilityId and 10 to bandwidthChange parameters.
// PUT: api/Setting/163/10
[HttpPut("{facilityId}/{bandwidthChange}")] // constructor takes a template as parameter
public void UpdateBandwidthChangeHangup([FromRoute] int facilityId, [FromRoute] int bandwidthChange) // use multiple FromRoute attributes, one for each parameter you are expecting to be bound from the routing data
{
_settingRespository.UpdateBandwidthHangup(facilityId, bandwidthChange);
}
If you had a complex object in one of the parameters and you wanted to send this as the body of the Http Request then you could use FromBody instead of FromRoute on that parameter. Here is an example taken from the Building Your First Web API with ASP.NET Core MVC
[HttpPut("{id}")]
public IActionResult Update([FromRoute] string id, [FromBody] TodoItem item);
There are also other options in MVC Core like FromHeader and FromForm and FromQuery.
Confusion with [FromRoute] versus [FromBody]
Microsoft has done a good job with ASP.NET trying to connect Internet technology into their frameworks and systems. But in the process they often confuse developers by trying to patch their technology solutions on top of what are very simple HTTP and HTML technologies. People walk away assuming their solutions represent how the World Wide Web works...when it simply does not. Routing URL solutions in both their .NET Framework and Core version of ASP.NET are still confusing to many.
Hopefully this helps...
When you create a basic "route" in ASP.NET Core, you are binding your your browser's URL Address Path to a specific Controller Class and its child Method() inside a ASP.NET compiled application. Binding means that Middleware, they designed, sniffs the URL address and tries to take it apart and then match the pieces to code inside your website.
Special Controller-ControllerBase classes handle this process inside ASP.NET using Conventional Routing Templates and Routing Attributes mapped to child Methods inside your classes. That includes the parameters of those methods. This then "routes" all URL browser address path requests to a specific set of code inside your web application in ASP.NET.
Unfortunately, there are too many ways to configure this binding. In the past, it was a bunch of random combinations of Route Templates to controllers & methods, with additional code to map a url to parameters. But that was not always clear for things like query strings. ASP.NET Core has tried to clean up that mess with these new Attribute Routing decorations like [FromRoute], etc.
So how do they work?
[Route("/mypath/{myid}"]
public string MyMethod(int myid){...}
The above matches this browser URL address on the Web:
http://example.com/mypath/5
In its simplest form, ASP.NET MVC or WebAPI uses a "route" text string to map a specific method parameter name to a matching URL path. [FromRoute] helps by binding explicitly a methods parameter name to a matching part of the URL in {}:
[Route("api/[controller]")]// "/api/test"
public class TestController : ControllerBase
{
[HttpGet("{facilityId}")]// "/api/test/26"
public IActionResult GetTotalBandwidth([FromRoute] int facilityId)
{
...
}
}
The [FromRoute] attribute decoration, however, is optional as Middleware in ASP.NET by default tries to map that for you out of the box. If you leave [FromRoute] off, it will try and map your parameter name to the route template name inside {}, as so:
[Route("api/[controller]")]// "/api/test"
public class TestController : ControllerBase
{
[HttpGet("{facilityId}")]// "/api/test/26"
public IActionResult GetTotalBandwidth(int facilityId)
{
...
}
}
[FromQuery], however, only binds to Querystring URL parameters, which lie outside the URL route, according to Microsoft. But I have always considered them as part of the URL. In past iterations of ASP.NET this was left out so people had to fudge a way to grab these values inside the method using Request.QueryString. So this new feature fixes this issue:
[Route("api/[controller]")]// "/api/test"
public class TestController : ControllerBase
{
[HttpGet("{facilityId}")]// "/api/test/26?num=5"
public IActionResult GetTotalBandwidth(int facilityId,[FromQuery] int num)
{
...
}
}
[FromBody] is Completely Different!
Capturing data from a POST has always been tricky. ASP.NET helps solve that by allowing the route URL mapping system to function as normal in listening for the POST and URL request, but using an additional method parameter attribute system to bind to that data coming in using [FromBody]. In the case of POST HTTP VERB types, POST is unique, as a POST has nothing to do with URL mapping. But in this case ASP.NET is using the attribute parameter binding system to grab that form field data for you.
Let's first see how POST form fields work.
When you send HTML form data using a "method=post" like so:
<!doctype html>
<html xml:lang="en-us" lang="en-us">
<head></head>
<body>
<form id="f1" name="f1" method="post" action="">
<input type="text" id="field1" name="field1" size="20" value="" />
<button id="mybutton" name="mybutton" value="submit">Submit</button>
</form>
</body>
</html>
...that sends the form field data packaged inside the "body" of the Request like so as a POST:
HTTP HEADER
Content-Type: text/html
Last-Modified: Fri, 27 Jan 2023 17:45:32 GMT
HTTP BODY
field1=hello&mybutton=submit
Notice the form field POST data is inside a special "body" or payload section of the request post (field1=hello&mybutton=submit). To get that data, you need to use [FromForm](for traditional name-value form POST data) or [FromBody] (special JavaScript JSON Post data only) in ASP.NET parameter decorations, like so:
// Note: In the example below, "field1" only captures one field.
[Route("api/[controller]")]// "/api/test"
public class TestController : ControllerBase
{
[HttpPost]// "/api/test"
public string MyPostMethod([FromForm] string field1)
{
...
}
}
[FromForm] captures regular HTML POST form fields (one field or all fields) as name-value pairs in the WebAPI POST endpoint.
[FromBody] captures the form fields but only as JSON data, requiring an additional content-type of 'application/json' added to the POST request sent to the server. Because HTML Forms cannot do that, you have to use JavaScript when using [FromBody].
So, [FromBody] is almost always used with a POST and has nothing to do with binding a Route Template as part of the URL like [FromRoute] or [FromQuery] might do, but simply captures any POST data typically found inside a POST request form submission sent inside the Http Request package.
Hope that helps!

spring mvc-adding a object to the request

I am new to spring-mvc and have a basic question.
I have a controller that reads the parameters that are sent in from a jsp and adds an object called userInfo to the ModelAndView and passes on to another jsp. The second jsp displays the vaious properites of the userInfo.
How do I send back the userInfo object to the controller?
<td><input type="hidden" name="userInfo" value="${requestScope.userInfo}"/></td>
I try to read the userInfo in the controller as follows:
request.getAttribute("userInfo")
However, this is null.
What is the best way for me to do this?
Thanks
HTML <form> <input> elements are sent as url-encoded parameters. You need to access them with HttpServletRequest#getParameter(String)
request.getParameter("userInfo");
Or use the #RequestParam annotation on a handler method parameter
#RequestMapping(...)
public String myHandler(#RequestParam("userInfo") String userInfo) {
...
}
Note however, that this won't send back the object, it will send back a String which is the toString() value of the object because that is what is given with ${requestScope.userInfo}.
Consider using session or flash attributes to have access to the same object in future requests.

SpringMVC form:options items attribute: what exactly is it expecting?

I'm still new to SpringMVC (and jstl for that matter). I'm trying to populate options in a select from a list of objects. I've found a way to do it using c:forEach, but I keep thinking there HAS to be a way to make the form:options method work.
I've browsed around, and about the closest thing I can find to official documentation on the items attribute is here >> http://static.springsource.org/spring/docs/2.0.x/reference/spring-form.tld.html#spring-form.tld.options
It says the items attribute is for
"The Collection, Map or array of objects used to generate the inner 'option' tags"
My confusion is what kind of Collection, Map, or array of objects it's looking for. What format do they need to be in? Is it looking for a Collection or array of type String specifically? Can I use
List<MyObject>
and if so, what would MyObject have to have in it in order for this to be valid (i.e. methods, variables)?
Currently, when I try to use MyObject, I get an exception that says -
ConverterNotFoundException: No converter found capable of converting from type com.example.MyObject to type java.lang.String
Do I need to make a converter? Where would that go? How would that work? I've googled that error message and haven't really turned up anything specific to what I'm trying to do... (Most are results about Roo)
the MyObject class looks like this:
public class MyObject{
private String company;
private Customer customer;
private Address customerAddress;
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public Address getCustomerAddress() {
return customerAddress;
}
public void setCustomerAddress(Address customerAddress) {
this.customerAddress = customerAddress;
}
}
and I'm trying to use it as such:
<form:select path="myObjectList">
<form:option value="0"/>
<form:options items="myObjectList" />
</form:select>
Does anyone know specifically what is incorrect about this method? Or, should I be using a
List<String>
to accomplish what I'm doing?
EDIT here's the stack trace >> http://pastebin.com/2c5XBCmG
The Spring Documentation says this about the items attribute of the form:options tag:
The items attribute is typically populated with a collection or array
of item objects. itemValue and itemLabel simply refer to bean
properties of those item objects, if specified; otherwise, the item
objects themselves will be stringified. Alternatively, you may specify
a Map of items, in which case the map keys are interpreted as option
values and the map values correspond to option labels. If itemValue
and/or itemLabel happen to be specified as well, the item value
property will apply to the map key and the item label property will
apply to the map value.
In a nutshell, if you need to use a List of your Custom Beans as the items attribute you need to use also the itemValue and itemLabel attributes. Personally, I'll prefer using Maps -LinkedHashMap instances speciffically- for populating my select tags, but that's a matter of taste.
Adapting an example from the Spring Documentation, your code should look like this:
<form:select path="commandAttribute">
<form:option value="-" label="--Please Select"/>
<form:options items="${countryList}" itemValue="company" itemLabel="company"/>
</form:select>
I'm using the company attribute as both itemValue and itemLabel, but you're free to choose the attributes that fit your requirements.
Usualy I am doing it with spring tag like this :
<springform:select path="myObjectList" id="selected_company">
<springform:option value="0" label="--- Select One ---"></springform:option>
<springform:options items="${myObjectList}" itemValue="company" itemLabel="company"></springform:options>
</springform:select>
don't forget including the namespace declaration :
xmlns:springform="http://www.springframework.org/tags/form"

ASP.Net JSON Web Service Post Form Data

I have a ASP.NET web service decorated with System.Web.Script.Services.ScriptService() so it can return json formatted data. This much is working for me, but ASP.Net has a requirement that parameters to the web service must be in json in order to get json out.
I'm using jquery to run my ajax calls and there doesn't seem to be an easy way to create a nice javascript object from the form elements. I have looked at serialiseArray in the json2 library but it doesn't encode the field names as property name in the object.
If you have 2 form elements like this
<input type="text" name="namefirst" id="namefirst" value="John"/>
<input type="text" name="namelast" id="namelast" value="Doe"/>
calling $("form").serialize() will get you the standard query string
namefirst=John&namelast=Doe
calling JSON.stringify($("form").serializeArray()) will get you the (bulky) json representation
[{"name":"namefirst","value":"John"},{"name":"namelast","value":"Doe"}]
This will work when passing to the web service but its ugly as you have to have code like this to read it in:
Public Class NameValuePair
Public name As String
Public value As String
End Class
<WebMethod()> _
Public Function GetQuote(ByVal nvp As NameValuePair()) As String
End Function
You would also have to wrap that json text inside another object nameed nvp to make the web service happy. Then its more work as all you have is an array of NameValuePair when you want an associative array.
I might be kidding myself but i imagined something more elegant when i started this project - more like this
Public Class Person
Public namefirst As String
Public namelast As String
End Class
which would require the json to look something like this:
{"namefirst":"John","namelast":"Doe"}
Is there an easy way to do this? Obviously it is simple for a form with two parameters but when you have a very large form concatenating strings gets ugly. Having nested objects would also complicate things
The cludge I have settled on for the moment is to use the standard name value pair format stuffed inside a json object. This is compact and fast
{"q":"namefirst=John&namelast=Doe"}
then have a web method like this on the server that parses the query string into an associate array.
<WebMethod()> _
Public Function AjaxForm(ByVal q As String) as string
Dim params As NameValueCollection = HttpUtility.ParseQueryString(q)
'do stuff
return "Hello"
End Sub
As far a cludges go this one seems reasonably elegant in terms of amount of code, but my question is: is there a better way? Is there a generally accepted way of passing form data to asp.net web/script services?
You are just having a formatting crisis.
To properly call this code:
Public Class NameValuePair
Public name As String
Public value As String
End Class
<WebMethod()> _
Public Function GetQuote(ByVal nvp As NameValuePair()) As String
End Function
You need to send a json string that looks like this:
'{"nvp": {"name": "john", "value": "foo"}}'
Do not use jQuery to serialize ScriptService arguments. Use the standard json2.js.
Try this:
Form
...
<input type="text" name="name" id="name" value="John"/>
<input type="text" name="value" id="value" value="Foo"/>
...
Script:
var myNvp = {name: $('#name').val(), value:$('#value').val()};
var data = JSON.stringify({nvp: myNvp});
// data is what you post to the service.
I just wrote this off the top of my head but it looks right to me.
Let me know if you have any other questions.
I havn't found anything better than what i was already thinking with passing the serialized string as a parameter.
input:
{"q":"namefirst=John&namelast=Doe"}
webservice:
<WebMethod()> _
Public Function AjaxForm(ByVal q As String) as string
Dim params As NameValueCollection = HttpUtility.ParseQueryString(q)
'do stuff
return "Hello"
End Sub
This seems the cleanest and simplest option

Spring Binding issue

I have some objects like the two below
public class SavedSearch {
String title;
ArrayList<SearchParameters> params;
}
public class SearchParameter {
String field;
int operator;
String match;
}
On the JSP page in the input form, I use
<input type="text" name="title">
and when I breakpoint inside the FormController, the SavedSearch object has title filled in.
But the ArrayList is always empty. It's not Spring's fault that it can't read my mind, but how do I indicate that field, operator and match are part of params? I tried naming them paramsField, paramsOperator, paramsMatch but no luck.
I know this is not a hard question, but I'm a bit stumped.
for binding a List you must use special wrapper instead of ArrayList: AutoPopulatingList from Spring or LazyList from Apache Commons Collections.
some examples:
using LazyList
using AutoPopulatingList

Resources