I am having trouble connecting all the pieces that would allow xml sent from an ajax client to be unmarshalled into Java objects. I am using Jquery for the ajax client, Spring3, and JAXB. Here are the key components:
Ajax Client
function checkForNewActivities(activities) {
$j.ajax({
url: "upload/checkForNewActivities",
type: "POST",
async: true,
dataType: "xml",
data: ({activities:activities}),
contentType: "application/xml",
beforeSend: function() {
alert("sending ajax");
},
complete: function() {
alert("ajax sent");
},
success: function(data) {
alert("success");
},
error: function(jqXHR, textStatus, errorThrown) {
alert(textStatus + errorThrown);
}
});
}
Spring Config
<bean id="marshallingConverter" class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
<property name="marshaller" ref="jaxbMarshaller" />
<property name="unmarshaller" ref="jaxbMarshaller" />
<property name="supportedMediaTypes" value="application/xml"/>
</bean>
<bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
a bunch of java classes
</list>
</property>
</bean>
And here is the Spring Controller method that is the target:
#RequestMapping(value="/checkForNewActivities",headers="application/xml", method=RequestMethod.POST)
public #ResponseBody String uploadMultipleWorkouts(#RequestBody String activities) {
System.out.println(activities);
return "";
}
With the headers="application/xml" in the RequestMapping, this method never gets called. If I remove the headers param, then the method is called and dumping the activities to the console shows the escaped xml.
I am clearly missing how to connect this method to the spring config so that unmarshalling xml takes place.
The reason your println prints out escaped xml is because the data type of the variable activities is String. You'll want to create a class to hold that data which will be passed to the method so there is a structure into which JAXB may translate the xml.
For example, instead of
public #ResponseBody String uploadMultipleWorkouts(#RequestBody String activities) {
You would want
public #ResponseBody Activities uploadMultipleWorkouts(#RequestBody Activities activities) {
Where Activities is a class you have defined with appropriate getters and setters such that the xml data you're passing in can be assigned. If the request data and response data must be different then you can simply define two classes, Request and Response variants.
As for why using headers="application/xml" messes things up, try setting it to headers = {"content-type=application/xml"}
Related
help me
index.jsp
$("#btn-submit").click(function () {
var username=document.getElementById("username");
var password=document.getElementById("password");
$.ajax({
url:"login",
contentType: 'application/json;charset=utf-8',
dataType: 'text',
headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')},
data: {
username:username.value,
password:password.value
},
type: 'get',
success: function (response) {
if (response=="1") {
alert(response);
}
else alert(response);
},
error: function (x, e) {
console.log(e)
}
});
});
LoginController.java
#RequestMapping("/login")
#Controller
public class LoginController {
#Autowired
private UserService userService;
#RequestMapping(value = { "/login" }, method = RequestMethod.GET)
#ResponseBody
public int checkValid(#RequestParam("username") String username,#RequestParam("password") String password, HttpServletRequest request, HttpServletResponse response, Locale locale, Model model){
try {
if (userService.findByUserName(username).equals(hashPass(password))){
return 1;
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return 0;
}
return 0;
}
public String hashPass(String pass) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hashInBytes = md.digest(pass.getBytes(StandardCharsets.UTF_8));
// bytes to hex
StringBuilder sb = new StringBuilder();
for (byte b : hashInBytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
}
spring-config-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
http://www.springframework.org/schema/jdbc/spring-jdbc-4.3.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
/WEB-INF/pages/
.jsp
/resources/jdbc.properties
<!-- Enable Annotation based Declarative Transaction Management -->
<tx:annotation-driven proxy-target-class="true"
transaction-manager="transactionManager" />
<!-- Creating TransactionManager Bean, since JDBC we are creating of type
DataSourceTransactionManager -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="postsDAO" class="com.blog.dao.impl.PostsDAO">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<bean id="postsService" class="com.blog.service.impl.PostsService">
<property name="postsDAO" ref="postsDAO"/>
</bean>
<bean id="userDAO" class="com.blog.dao.impl.UserDAO">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<bean id="userService" class="com.blog.service.impl.UserService">
<property name="userDAO" ref="userDAO"/>
</bean>
I use tomcat 9
Error:Failed to load resource: the server responded http://localhost:8080/Blog_war_exploded/login?username=root&password=root with a status of 404 ()
Look at your error: You are accessing http://localhost:8080/Blog_war_exploded/login but you actually want to access http://localhost:8080/login.
The reason is that you specified your URL as login instead of /login, so it is relative to the current "directory" and not to the root.
Changing the code to use /login should fix it:
$.ajax({
url: "/login",
...
})
On a side note, it's not a good idea to this via GET requests - among other things, the password will be stored in the server log in clear text. You should use a POST request instead.
Update:
Also, it seems you are use two request mappings for /login on top of each other, so you'll end up with /login/login. Check out how to use #RequestMapping properly.
Try changing the second (method-level) one to #RequestMapping(value = { "/" }, method = RequestMethod.GET) or just #RequestMapping("/").
I think the issue is related to your RequestMapping definition on both controller level and method level.
the first login at the controller level, means if you want to access any services in this controller, your requests have to start with "/login"
#RequestMapping("/login")
#Controller
public class LoginController {
and the second login at the method level, means you want to call the /login service under /login.
#RequestMapping(value = { "/login" }, method = RequestMethod.GET)
#ResponseBody
public int checkValid(#RequestParam("username") String username,#RequestParam("password") String password, HttpServletRequest request, HttpServletResponse response, Locale locale, Model model){
So the valid URL to call the /login service under /login controller is: /login/login
and because of this, your url /login was not found
you can either remove the first /login at the controller level, or use the /login/login from your ajax request...
In my Spring configuration file:
<bean id="jacksonJsonProvider" class="org.codehaus.jackson.jaxrs.JacksonJsonProvider" />
<bean id="restJacksonProviderList" class="java.util.ArrayList">
<constructor-arg>
<list>
<ref bean="jacksonJsonProvider"/>
</list>
</constructor-arg>
</bean>
//......
<route id="RestMyRoute">
<from id="RestRequest" uri="cxfrs:/rest/MyService?resourceClasses=com.myself.services.MyService&bindingStyle=SimpleConsumer&providers=#restJacksonProviderList" />
<to uri="direct:doRoute" />
</route>
The Service interface:
#GET
#Path("/my/something/{id}")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#WebMethod
#WebResult(name = "getSomethingResponse")
public List<MySomething> getSomething(
#PathParam("id") #WebParam(name = "id") String id);
The code above works! I can send the get request to the URl and I get a JSON response.
Now, I do a small change: Instead of defining the web service's URL (and the route) by XML configuration, I define them by Java code:
public class MyRoute extends RouteBuilder {
private String uriRest = "cxfrs:/rest/MyService?resourceClasses=com.myself.services.MyService&bindingStyle=SimpleConsumer&providers=#restJacksonProviderList";
#Override
public void configure() throws Exception {
from(uriRest).
to("log:input").
to("direct:doRoute").
to("log:output");
}
}
When I hit the web service URL, I am getting 500 Internal Server Error and in the logs (Tomcat) I see JAXRSUtils ERROR No message body writer has been found for class java.util.ArrayList, ContentType: application/json
Actually the debugger tells me that defining the URI by Java code is recognized, since I do hit the code inside the route.
I saw this error in many answers here, basically they say to add a Json provider and assign it to the CXF endpoint.
Seems to me like it is what I have done. But it does not work.
Any idea what I am doing wrong here?
As peeskillet said, it's because there isn't a list of providers registered under the name restJacksonProviderList. You can get the JndiContext like this and bind a list to it in the configure method of your routebuilder:
JndiContext registry = (JndiRegistry) context.getRegistry();
registry.bind("restJacksonProviderList", Arrays.asList(new JacksonJsonProvider()));
Edit after comments:
Change & for & in your cxfrs uri definition, & is only needed in xml.
ViewResolver (my jsp is in the right folder as specified on prefix value):
<!-- Resolves views selected for rendering by #Controllers -->
<!-- to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
Servlet mapping:
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>*.fst</url-pattern>
</servlet-mapping>
Controller:
#Controller
public class HomeController {
private static final Logger logger =
LoggerFactory.getLogger(HomeController.class);
#RequestMapping("/home")
public ModelAndView home(String user, HttpServletRequest request) {
logger.info("Home controller has been executed");
ModelAndView mv = new ModelAndView();
mv.addObject("userName", user);
mv.addObject("controllerName", request.getRequestURI());
mv.setViewName("home");
return mv;
}
#RequestMapping(value = "/testAjax", method = RequestMethod.POST)
public String testAjax(#RequestParam("memberId") String id,
HttpServletRequest request, HttpServletResponse response,
Locale locale, Model model) {
logger.info("Text Ajax action has been executed. My Parameter is " + id);
return id;
}
}
After turning on Tomcat 8 server on STS IDE, accessing this web with this url http://localhost:8080/home.fst works okay.
But on the page, calling AJAX like below throws a 404 error:
$.ajax({
type: "POST",
url: "/testAjax.fst",
data: {"memberId" : "test"},
success: function (result) {
console.log(result)
}
});
This is console error log:
POST http://localhost:8080/testAjax.fst 404 (Not Found)
k.cors.a.crossDomain.send jquery-2.1.3.min.js:4
n.extend.ajaxhome.fst:11 (anonymous function) jquery-2.1.3.min.js:3
n.event.dispatch jquery-2.1.3.min.js:3
r.handle
Strange thing is that it calls testAjax controller just fine and there's no error log on server.
logger.info("Text Ajax action has been executed. My Parameter is " + id);
When textAjax action is invoked by my AJAX, the log is printed as well. I checked it out with debug point too (it broke alright).
What seems to be the matter??
Everything's good just Add #ResponseBody annotation in your method and also I suggest you to change your request method POST to GET
Spring
#RequestMapping(value = "/testAjax", method = RequestMethod.GET) //Made Change
#ResponseBody //added
public String testAjax(#RequestParam("memberId") String id, HttpServletRequest request, HttpServletResponse response, Locale locale, Model model) {
logger.info("Text Ajax action has been executed. My Parameter is " + id);
return id;
}
JQuery
$.ajax({
type: "GET", //Made Change
url:"/testAjax.fst",
data: {"memberId" : "test"},
success: function (result) {
console.log(result)
}
});
Currently I am on a legacy project of Spring mvc and annotations are not working on it.
I have to create an controller-action which will respond to an ajax call with JSON response.
I have created my controller by implementing Controller interface.
I am able to implement a jsp page with this but unable to tweek it for ajax call.
Taking help from link1 link2 link3
Xml configuration in action-servlet.xml
<bean id="jsonResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order"><value>1</value></property>
</bean>
<bean name="jsonView" class="org.springframework.web.servlet.view.json.JsonView">
<property name="contentType">
<value>text/html</value>
</property>
</bean>
<bean id="myOwnController" class="com.myapp.webapp.action.MyOwnController" />
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="interceptors">
<list>
<!-- list of inteceptor urls -->
</list>
</property>
<property name="mappings">
<props>
<prop key="/mytest">myOwnController</prop>
<!-- mapping for other controller actions -->
</props>
</property>
</bean>
Controller class
package com.myapp.webapp.action
public class myOwnController implements org.springframework.web.servlet.mvc.Controller
{
#Override
public ModelAndView handleRequest(HttpServletRequest request,HttpServletResponse response) throws Exception
{
Map<String, Object> model = new HashMap<String, Object>();
model.put("success", "true");
return new ModelAndView("jsonView", model);
}
}
ajax call js method (in Prototype.js)
function myAjaxMethod(){
new Ajax.Request('${pageContext.request.contextPath}/jsonView', {
method:'get',
onSuccess: function(transport) {
var response = transport.responseText || "no response text";
alert("Success! \n\n" + response);
},
onFailure: function() { alert('Something went wrong...'); }
});
}
Also I am unable to understand that how the url in ajax call will get to know the this is the controller that should be called.Do I am missing any mapping?
Please help.
Here is an example for, how to call a spring MVC controller from ajax function
This is all done using annotation driven environment.
1) Need to make application annotation driven first and give your controller package there.
2) Add #RequestMapping to the fn that is going to be called from ajax fn, and provide maping URL to ajax fn 'url' attribute.
changes in p[om.xml
need to add, following library to convert and send response to ajax success in form of json.
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
changes in spring conf file
<context:component-scan base-package="com.beingjavaguys.controller" />
<mvc:annotation-driven />
controller code:
#RequestMapping(value = "/save", method = RequestMethod.POST, headers = { "Content-type=application/json" })
public #ResponseBody
Response saveData(#RequestBody Employee employee) {
System.out.println("First Name: " + employee.getFirstName());
System.out.println("Last Name: " + employee.getLastName());
System.out.println("Email: " + employee.getEmail());
System.out.println("Phone: " + employee.getPhone());
return new Response("1", "Done");
}
ajax function syntax
function doAjaxPost() {
var formData = JSON.stringify({
firstName : $('#firstName').val(),
lastName : $('#lastName').val(),
email : $('#email').val(),
phone : $('#phone').val()
});
$.ajax({
type : "POST",
url : "save",
data : formData,
contentType : 'application/json',
success : function(data) {
if (data.status == '1')
$('#status').html('Form data sent !');
else
$('#status').html('Error in sending data !');
}
});
}
Hope this helps.
Use a button instead of submit to prevent page load on submit form and call ajax fn onClick.
Finally I got a solution(not via Spring but via a dirty logic).
Warning : Never work on old Spring version project.
Solution : I rendered whole html response in ajax call, filter my data and parse into json.
action-servlet.xml
<bean id="myOwnController" class="com.myapp.webapp.action.MyOwnController" />
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="interceptors">
<list>
<!-- list of inteceptor urls -->
</list>
</property>
<property name="mappings">
<props>
<prop key="/mytest.html">myOwnController</prop>
<!-- mapping for other controller actions -->
</props>
</property>
</bean>
Controller class
package com.myapp.webapp.action
public class myOwnController implement org.springframework.web.servlet.mvc.Controller
{
#Override
public ModelAndView handleRequest(HttpServletRequest request,HttpServletResponse response) throws Exception
{
Map<String, Object> model = new HashMap<String, Object>();
model.put("name", "davy jones");
return new ModelAndView("mytest", model);
}
}
Calling jsp Ajax call
function createFreshdeskUser(){
new Ajax.Request('${pageContext.request.contextPath}/mytest.html', {
method:'get',
onSuccess: function(transport) {
var divIdWordLength=16;
var htmlData=transport.responseText;
var startIndex=htmlData.indexOf("<div id=\"mydiv\">");
var endHtmlData=htmlData.substring(startIndex+divIdWordLength , htmlData.length);
var endIndex=endHtmlData.indexOf("</div>");
var finalHtmlData=endHtmlData.substring(0,endIndex);
var jsonObj=eval ("(" + finalHtmlData + ")");
alert(jsonObj.name);
},
onFailure: function() { alert('Something went wrong...'); }
});
}
mytest.jsp
<head>
</head>
<body>
<div id="mydiv">{'name':'${name}'}</div>
</body>
</html>
I'm requesting data from controller through ajax, but it can't convert json object to java object. I'm using jackson 2.2.3 and Spring 4.0.0. Could you help me find out where I did wrong? Thanks.
part of epscms-servlet.xml:
<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"></bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name= "messageConverters" >
<list>
<ref bean="jacksonMessageConverter" />
</list>
</property>
</bean>
ajax request:
var data = {
orderId:1,
parentId:0,
className:"test",
newsType:1
};
$.ajax({
url : "${pageContext.request.contextPath}/classification/add/batch",
type : "POST",
data : data,
dataType: "json",
contentType: 'application/json',
success : function(data) {
alert("success");
},
error : function(data, status){
alert(data + status);
}
}
);
controller:
#RequestMapping(value="/add/batch", method=RequestMethod.POST, consumes=MediaType.APPLICATION_JSON_VALUE)
public String batchAdd(#RequestBody Classification c){
return "failure";
}
Classification.java
public class Classification {
private int orderId;
private String className;
private int parentId;
private int newsType;
//getters and setters..
}
if i change the controller method to
public String batchAdd(#RequestBody String cla){
return "failure";
}
it work fine, and I can get the json string. Did anyone else meet this question before?
You may need to JSON.stringify() your data before posting it to the endpoint:
...
type : "POST",
data : JSON.stringify(data),
dataType: "json",
...
Here is some additional info on stringify. Depending on which browsers you need to support, you may also want to read this