【Spring-MVC AJAX请求】
前言
准备筹划一个自己的个人网站,初步的架构设计采用SSH(Spring-MVC,Spring,Hibernate),
在这里顺便记录一下设计和开发过程,以备参考。后续会陆续更新文档,如有任何问题,
欢迎各位不吝指出,共同学习。
环境
开发环境 | 版本 |
---|---|
eclipse | 4.3.2 |
maven | 3.2.1 |
Spring-MVC | 3.2.3.RELEASE |
Spring | Spring |
Hibernate | 3.2.5 |
十三哥将来的架构是后台不渲染页面,只返回JSON数据,所以在此详细
介绍下SpirngMVC对ajax请求的处理。
【添加处理JSON数据的jar包】
pom.xml添加spring-mvc的依赖
<!-- jack core annotation databind begin--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.2.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.2.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.2.2</version> </dependency> <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-mapper-lgpl</artifactId> <version>1.9.12</version> </dependency> <!-- jack core annotation databind end--> <!-- Fastjson begin(这个可以不需要,因为Fastjson要比Jackson的效率高, 当自己组织JSON数据时可以使用)--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.1.30</version> </dependency> <!-- Fastjson end-->
【Controller代码】
package com.xbs.ready.ssh.controller;
import com.alibaba.fastjson.JSON;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
*
* @author xbs
*/
@Controller
@RequestMapping("hello")
public class HelloController {
/**
* ajax请求不需要返回页面,只需要得到response中的数据即可,所以方法签名为void即可
*
* @param request
* @param response
*/
@RequestMapping(value = "ajax", method = RequestMethod.POST)
public void ajaxDatas(HttpServletRequest request, HttpServletResponse response) {
String jsonResult = getJSONString(request);
renderData(response, jsonResult);
}
private String getJSONString(HttpServletRequest request) {
//故意构造一个数组,使返回的数据为json数组,数据更复杂些
List<Map<String, Object>> datas = new ArrayList<Map<String, Object>>(5);
Map<String, Object> map1 = new HashMap<String, Object>(10);
//可以获得ajax请求中的参数
map1.put("a", request.getParameter("a"));
map1.put("b", request.getParameter("b"));
map1.put("c", request.getParameter("c"));
datas.add(map1);
//故意构造一个数组,使返回的数据为json数组,数据更复杂些
Map<String, Object> map2 = new HashMap<String, Object>(10);
map2.put("a", "11");
map2.put("b", "22");
map2.put("c", "33");
datas.add(map2);
String jsonResult = JSON.toJSONString(datas);
return jsonResult;
}
/**
* 通过PrintWriter将响应数据写入response,ajax可以接受到这个数据
*
* @param response
* @param data
*/
private void renderData(HttpServletResponse response, String data) {
PrintWriter printWriter = null;
try {
printWriter = response.getWriter();
printWriter.print(data);
} catch (IOException ex) {
Logger.getLogger(HelloController.class.getName()).log(Level.SEVERE,
null, ex);
} finally {
if (null != printWriter) {
printWriter.flush();
printWriter.close();
}
}
}
}
【HTML代码】
html文件路径:.../webapp/views/index.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html >
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<!--<script type="text/javascript" src="../static/js/jquery-1.7.2.min.js">
</script>-->
<!--JS的地址可以写成下面这样,将来部署的时候,这些静态文件就可以单独部署了,
不依赖于后台路径-->
<script type="text/javascript" src="http://localhost:8080/sshdemo
/static/js/jquery-1.7.2.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
ajaxRequest();
});
function ajaxRequest() {
$.ajax({
url: "http://localhost:8080/sshdemo/hello/ajax",
type: "POST",
dataType: "json",
data: {
"a": 1,
"b": 2,
"c": 3
},
async: false,
success: function(data) {
alert("success");
$.each(data, function(index, element) {
alert(element.a);
alert(element.b);
alert(element.c);
});
},
error: function() {
alert("error");
}
});
}
</script>
</head>
<body>
<div>Hello World!</div>
</body>
</html>
访问路径:http://localhost:8080/sshdemo/views/index.html
(或者:http://localhost:8080/sshdemo/)
使用@ResponseBody注解
Spring的官方文档中这样说:
The @ResponseBody annotation is similar to @RequestBody. This annotation can be put on a
method and indicates that the return type should be written straight to the HTTP response body
(and not placed in a Model, or interpreted as a view name).
意思是:@ResponseBody与@RequestBody差不多。@ResponseBody可以用在方法上,这样说
明方法的返回结果会被直接写在HTTP的response内。既然写在了response的body内,那么就不是
一个Model或者视图的名字。从而判断出返回的肯定不是页面了。
Sping官网给的例子如下:
@RequestMapping(value = "/something", method = RequestMethod.PUT)
@ResponseBody
public String helloWorld() {
return "Hello World";
}
如果上面的代码中没有使用@ResponseBody注解,那么返回的就是一个页面,页面名字是【Hello
World】(例如:Hello World.jsp)。 但是使用了@ResponseBody注解后,response的body内会
写入Hello World这个字符串,ajax当然也就可以获取到了。
Spring文档给出了一段说明:
As with @RequestBody, Spring converts the returned object to a response body by using an
HttpMessageConverter.
意思是:使用@RequestBody时,Spring会使用HttpMessageConverter将返回结果写到response的
body里。所以,如果有想了解这方面原理的朋友,可以去官网看看 http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/remoting.html#rest-message-conversion
Spring提供的HttpMessageConverter大致有以下这几个:
StringHttpMessageConverter,FormHttpMessageConverter,ByteArrayMessageConverter,
MarshallingHttpMessageConverter,MappingJacksonHttpMessageConverter,
SourceHttpMessageConverter,BufferedImageHttpMessageConverter
使用ResponseEntity
Spring的官方文档这样说:
A HttpEntity<?> or ResponseEntity<?> object to provide access to the Servlet response HTTP
headers and contents. The entity body will be converted to the response stream using
HttpMessageConverters.
意思是:HttpEntity或者ResponseEntity可以访问Servlet的响应头和正文。使用
HttpMessageConverters将实体写到响应流中。这样的话,ajax就可以获得后台的响应数据了。
HttpEntity支持泛型,可以通过HttpEntity获得请求中的参数。
【Controller代码】
@RequestMapping("entity")
public ResponseEntity<Map> handle(HttpEntity<String> requestEntity) {
//获得ajax的请求数据
String body = requestEntity.getBody();
System.out.println("requestParams:" + body);
//构造一个map作为返回数据,前端会接收到JSON格式的数据
Map<String, Object> map = new HashMap<String, Object>(10);
map.put("a", "11");
map.put("b", "22");
map.put("c", "33");
return new ResponseEntity<Map>(map, HttpStatus.OK);
}
【AJAX代码】
//url = "http://localhost:8080/sshdemo/hello/entity"; function responseEntity(url) { $.ajax({ url: url, type: "POST", dataType: "json", data: { "a": 1, "b": 2, "c": 3 }, async: false, success: function(data) { alert("success"); alert(data.a); alert(data.b); alert(data.c); }, error: function() { alert("error"); } }); }
【问题说明】
问题1:当使用ResponseEntity处理ajax请求时,出现406 Not Acceptable
HTTP Status 406 -
type Status report
message
description The resource identified by this request is only capable of
generating responses with characteristics not acceptable according to
the request "accept" headers.
Apache Tomcat/7.0.34
解决方案:这个问题与SpringMVC的版本和配置有关。
修改springMVC-servlet.xml中spring-mvc的文档版本为3.2,增加
ContentNegotiationManagerFactoryBean。
原配置文件:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
修改为:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- 增加以下配置,这样可以使ajax的请求返回JSON数据,
否则返回text/html格式,那么会报406 -->
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager" />
<bean id="contentNegotiationManager"
class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean" />
另外,pom.xml中一定要添加jackson的依赖,springMVC通过使用jackson把返回结果转换成JSON格式
的数据。
在以上的三种处理ajax请求的方式中(1:方法声明层void 2:使用@ResponseBody
3:使用ResponseEntity),极力推荐使用@ResponseBody这种方式。
因为@ResponseBody不用修改配置文件为3.2,不依赖于jackson包。