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
Related
I try to pass an object to th:onclick.
When I pass a string(afficherDetails() function), everything is ok
When I pass an object(afficherDetails2() function), in the called function the object seems ok but it is empty.
function afficherDetails(employee) {
console.log("afficher Details");
document.getElementById("detailledFirstNameDataLabelId").textContent = employee.firstName;
document.getElementById("detailledLastNameDataLabelId").textContent = employee.lastName;
document.getElementById("detailledAddressDataLabelId").textContent = employee.address;
document.getElementById("detailledTitleDataLabelId").textContent = employee.title;
document.getElementById("detailledManagerDataLabelId").textContent = employee.manager;
}
function afficherDetails2(name) {
console.log("afficher Details");
document.getElementById("detailledFirstNameDataLabelId").textContent = name;
}
<td><button th:data-parameter1="${employee}" th:onclick=" afficherDetails(this.getAttribute('data-parameter1')) ">details</button></label></td>
<!--td><button th:data-parameter1="${employee.firstName}" th:onclick=" afficherDetails2(this.getAttribute('data-parameter1')) ">details</button></label></td-->
</tr>
Is it a correct behavior ? Can't we pass a complex object and we can only pass simple object?
thanks for your answer
Short answer:
You can pass a complex object to a HTML attribute - but it will be reduced to a string by the object's toString() method.
Therefore, in your case, an attempt to do the following in JavaScript...
var something = employee.firstName;
...will do nothing because the function is passed a string not an object - and therefore employee.firstName will be undefined in JavaScript.
Longer answer:
Bear in mind a couple of points:
A HTML attribute expects to contain a string:
<button th:data-parameter1="SOME VALUE IN HERE" ... >
So, the attribute data-parameter1 will be populated by Thymeleaf using a string.
All Thymeleaf processing happens on the server. Thymeleaf removes all its processing directives from the template and replaces them with valid HTML. Your JavaScript does not have access to the original Java object - just to whatever representation of that object was added to the HTML by Thymeleaf.
Let's assume you use something such as:
th:data-parameter1="${employee.firstName}"
Assuming employee.firstName evaluates to a string (John) then that is what Thymeleaf will use to produce this:
data-parameter1="John"
But if you try this:
th:data-parameter1="${employee}"
Assuming employee is your custom Java bean, then Thymeleaf will call its toString() method to use as the string.
If you have not defined a toString() method in your Employee class, then the underlying Object.toString() method will be used - and you will see something like the following - a string representation of the unique object, based on the object's name and hash code:
data-parameter1="org.yourpackage.Employee#bcb8097"
You can provide your own implementation of toString() in your Employee class to provide more useful information. But it has to be a string which can be placed in a HTML attribute.
For example, if you pass an ArrayList to the button:
List<String> names = Arrays.asList("John", "Mary");
and:
th:data-parameter1="${names}"
then your HTML button will contain this:
data-parameter1="[John, Mary]"
because [John, Mary] is the result of how ArrayList has implemented its toString() method.
You can send a certain Java objects directly to JavaScript - see JavaScript serialization. But that is probably off-topic for this question.
One extra note: In the following:
th:onclick="afficherDetails(this.getAttribute('data-parameter1'));"
You are using th:onclick - but there are no Thymeleaf expressions in the attribute, so there is nothing for Thymeleaf to process. You can just use:
onclick="afficherDetails(this.getAttribute('data-parameter1'));"
Given the following ASP.NET WebAPI, I am trying to send a test POST using Fiddler, but can't get it to work. Whatever I send, I always just see the No data sent to service message.
Imports System.Web.Http
Imports System.Net.Http
Imports System.Net
Namespace HelloWebApiDemo
Public Class MyApiController
Inherits ApiController
Public Function [Get]() As HttpResponseMessage
Return Request.CreateResponse(HttpStatusCode.OK, "Hello")
End Function
Public Class MyXmlData
Public Property UserName As String
Public Property Password As String
Public Property SomeData As String
End Class
Public Function Post(<FromBody> x As MyXmlData) As HttpResponseMessage
If x Is Nothing Then
Return Request.CreateResponse(HttpStatusCode.InternalServerError, "No data sent to service")
End If
Dim u As String = String.Empty
Dim p As String = String.Empty
Dim d As String = String.Empty
If Not String.IsNullOrEmpty(x.UserName) Then
u = x.UserName
End If
If Not String.IsNullOrEmpty(x.Password) Then
p = x.Password
End If
If Not String.IsNullOrEmpty(x.SomeData) Then
d = x.SomeData
End If
Return Request.CreateResponse(HttpStatusCode.OK, String.Format("You posted {0}, {1} with a string that is {2} characters in length", u, p, d.Length.ToString))
End Function
End Class
End Namespace
In Fiddler, my POST looks like this:
Can anyone please advise on what I'm doing wrong? I used Content-Type: text/xml hoping that ASP.NET would be able to decipher it correctly.
Update
New screen grab following input:
Keep the Content-Type: text/xml request header and change the XML to like this.
<MyApiController.MyXmlData
xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.datacontract.org/2004/07/HelloWebApiDemo.HelloWebApiDemo">
<Password>somepassword</Password>
<SomeData>somedata</SomeData>
<UserName>bob</UserName>
</MyApiController.MyXmlData>
Final XML snippet that was successfully deserialized:
<MyApiController.MyXmlData
xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.datacontract.org/2004/07/_WebApplication1.HelloWebApiDemo">
<Password>Password</Password>
<SomeData>Data here</SomeData>
<UserName>Some Username</UserName>
</MyApiController.MyXmlData>
_WebApplication1 is the name of the solution. That must've been what was missing.
For those who have the ability to do this when faced with the same issue, another option is to use JSON. I am a huge fan of XML, but unfortunately, it seems things are far more stacked against XML's usage in scenarios like this, because in this case, the standard WebApi parser requires all that hideous extra stuff, including intimate knowledge of backend namespaces and types, as well as a full XML spec namespace (* see rant below).
{
"SomeData": "R2D2",
"UserName": "Johny",
"Password": "password",
"Num": 1013,
"IsCool": true
}
*( Rant, please ignore if you want to: Why require this, oh ye good gentleman who made WepApi and other such frameworks? why not allow the XML to be liberated, freely receivable without all this 'ugly'? If included, it could still specify something useful, if one needs to, but why must the rest of us include this ugliness? It seems things have been (arbitrarily, though perhaps more for historical baggage reasons, sorry you poor XML creature, you're stuck with yesterday's baggage though it need not be) stacked against XML's usage, including that, even with all this, a standard parser likely would not allow you to post the XML with the values as XML attributes. )
You can mark your entity class with DataContract attribute setting namespace to an empty string:
<DataContract(Namespace := "")>
Public Class MyXmlData
End Class
After that xmlns parameters will not be necessary.
I'm calling a REST service searchFavoriteCompany that in turn calls another service A.
Service A already returns JSON. But since my return type of searchFavoriteCompany my search results are then returned with escape characters:
"{\"responseHeader\":{\"status\":0,\"QTime\":2,\"params\":{\"facet\":\"false\",\"fl\":\"id,title,friendlyurl,avatar,locpath,objectid,objecttype\",\"indent\":\"off\",\"start\":\"0\",\"q\":\"title_search:*castle*\",\"wt\":\"json\",\"fq\":\"userid:\\\"C325D42C-A777-4275-BDD2-D7810A8AB9AB\\\"\",\"rows\":\"10\",\"defType\":\"lucene\"}},\"response\":{\"numFound\":2,\"start\":0,\"docs\":[{\"title\":\"Castle A\",\"objecttype\":1,\"friendlyurl\":\"castle-a\",\"avatar\":\"6_887_castle-a.JPG\",\"objectid\":6},{\"title\":\"Castle B\",\"objecttype\":1,\"friendlyurl\":\"castle-b\",\"avatar\":\"794_360_13j-Castle-by-night.jpg\",\"objectid\":794}]}}\u000a"
I don't know how to make sure my JSON results are returned without these escape characters.
Imyservice.vb
Namespace RestService
<ServiceContract()>
Public Interface Imyservice
<OperationContract()> _
_
Function searchFavoriteCompany(ByVal q As String, ByVal uuid As String) As String
End Interface
End Namespace
iservice.svc.vb
Namespace RestService
Public Class iservice
Implements Imyservice
Public Function searchFavoriteCompany(ByVal q As String, ByVal uuid As String) As String Implements Imyservice.searchFavoriteCompany
Dim req As HttpWebRequest = HttpWebRequest.Create("http://localhost/getjson") <---- this service already returns JSON data
Dim Resp As HttpWebResponse = req.GetResponse()
Dim reader As StreamReader = New StreamReader(Resp.GetResponseStream)
Dim responseString As String = reader.ReadToEnd()
Return responseString
End Function
End Class
End Namespace
Instead of returning the responsestring I also tried:
HttpContext.Current.Response.Write(responseString)
return ""
But then I get the error 'Object reference not set to an instance of an object'
And I checked: responsestring contains a value, so that is not the problem.
If you can't pass the output directly to the response, your best bet is to go back to traditional methods, and return a type-safe object from your method. You've got the JSON that the original service passes you, and your end-goal is to return that exact object back to your caller.
Assuming you've got a complete JSON object in your example - you can first go to json2csharp to take your JSON and create classes out of it. It uses C#, but you can easily convert it to VB using something like Telerik's Code Converter.
If you follow these steps, you'll end up with several classes, including one called RootObject, which contains properties for responseHeader and response, and the other classes needed. You can rename the classes as you see fit, and put them into proper namespaces - just leave the property names alone, so they match the JSON.
Once you've got this RootObject class (or whatever you rename it to), you can deserialize the JSON you got from the original service into an object of type RootObject, then your WebAPI method would simply return a RootObject. Just like any other WCF call, the framework will take care of serializing that object back into JSON (or XML) for the caller.
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!
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