1. 关于SpringMVC请求路径的说明
- 默认条件下 SpringMVC只能拦截前缀型请求. www.jt.com/item/562379
- 如果请求路径添加了后缀,则后缀会误当做请求参数,参与运算.
562379.html一起当做参数来使用,导致参数异常.
开启后缀类型匹配
说明:开启后缀类型匹配标识 如果请求 以.html/.do/.action等操作结尾时依然会被SpringMVC正确的拦截,并且后缀不参与参数的传递
- 配置类位置
- 配置类代码
@Configuration
public class MvcConfigurer implements WebMvcConfigurer{
//开启匹配后缀型配置
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(true);
}
}
为什么要在请求的结尾添加.html的后缀
添加.html的目的是为了让搜索引擎更加容易的记录页面.提高网站的曝光率.
搜索引擎的规则: 搜索引擎一般会记录公司中.html结尾的页面之后记录在数据库中并且为其创建索引提高用户的检索效率
2. HttpClient
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
#最大连接数
http.maxTotal = 1000
#并发数
http.defaultMaxPerRoute = 20
#创建连接的最长时间
http.connectTimeout=5000
#从连接池中获取到连接的最长时间
http.connectionRequestTimeout=500
#数据传输的最长时间
http.socketTimeout=5000
#提交请求前测试连接是否可用
http.staleConnectionCheckEnabled=true
编辑HttpClientConfig配置类
@Configuration
@PropertySource(value="classpath:/properties/httpClient.properties")
public class HttpClientConfig {
@Value("${http.maxTotal}")
private Integer maxTotal; //最大连接数
@Value("${http.defaultMaxPerRoute}")
private Integer defaultMaxPerRoute; //最大并发链接数
@Value("${http.connectTimeout}")
private Integer connectTimeout; //创建链接的最大时间
@Value("${http.connectionRequestTimeout}")
private Integer connectionRequestTimeout; //链接获取超时时间
@Value("${http.socketTimeout}")
private Integer socketTimeout; //数据传输最长时间
@Value("${http.staleConnectionCheckEnabled}")
private boolean staleConnectionCheckEnabled; //提交时检查链接是否可用
//定义httpClient链接池
@Bean(name="httpClientConnectionManager")
public PoolingHttpClientConnectionManager getPoolingHttpClientConnectionManager() {
PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager();
manager.setMaxTotal(maxTotal); //设定最大链接数
manager.setDefaultMaxPerRoute(defaultMaxPerRoute); //设定并发链接数
return manager;
}
//定义HttpClient
/**
* 实例化连接池,设置连接池管理器。
* 这里需要以参数形式注入上面实例化的连接池管理器
@Qualifier 指定bean的ID为参数赋值
*/
@Bean(name = "httpClientBuilder")
public HttpClientBuilder getHttpClientBuilder(@Qualifier("httpClientConnectionManager")PoolingHttpClientConnectionManager httpClientConnectionManager){
//HttpClientBuilder中的构造方法被protected修饰,所以这里不能直接使用new来实例化一个HttpClientBuilder,可以使用HttpClientBuilder提供的静态方法create()来获取HttpClientBuilder对象
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
httpClientBuilder.setConnectionManager(httpClientConnectionManager);
return httpClientBuilder;
}
/**
* 注入连接池,用于获取httpClient
* @param httpClientBuilder
* @return
*/
@Bean
public CloseableHttpClient getCloseableHttpClient(@Qualifier("httpClientBuilder") HttpClientBuilder httpClientBuilder){
return httpClientBuilder.build();
}
/**
* Builder是RequestConfig的一个内部类
* 通过RequestConfig的custom方法来获取到一个Builder对象
* 为请求设定超时时间
* @return
*/
@Bean(name = "builder")
public RequestConfig.Builder getBuilder(){
RequestConfig.Builder builder = RequestConfig.custom();
return builder.setConnectTimeout(connectTimeout)
.setConnectionRequestTimeout(connectionRequestTimeout)
.setSocketTimeout(socketTimeout)
.setStaleConnectionCheckEnabled(staleConnectionCheckEnabled);
}
/**
* 使用builder构建一个RequestConfig对象
* @param builder
* @return
*/
@Bean
public RequestConfig getRequestConfig(@Qualifier("builder") RequestConfig.Builder builder){
return builder.build();
}
}
关闭超时链接
//该类的作用 定期将超时的链接自动关闭.
@Component //交给spring容器管理
public class HttpClientClose extends Thread{
@Autowired
private PoolingHttpClientConnectionManager manage;
private volatile boolean shutdown;
//开关 volatitle表示多线程可变数据,一个线程修改,其他线程立即修改
public HttpClientClose() {
///System.out.println("执行构造方法,实例化对象");
//线程开启启动
this.start();
}
@Override
public void run() {
try {
//如果服务没有关闭,执行线程
while(!shutdown) {
synchronized (this) {
wait(5000); //等待5秒
//System.out.println("线程开始执行,关闭超时链接");
//关闭超时的链接
PoolStats stats = manage.getTotalStats();
int av = stats.getAvailable(); //获取可用的线程数量
int pend = stats.getPending(); //获取阻塞的线程数量
int lea = stats.getLeased(); //获取当前正在使用的链接数量
int max = stats.getMax();
//System.out.println("max/"+max+": av/"+av+": pend/"+pend+": lea/"+lea);
manage.closeExpiredConnections();
}
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
super.run();
}
//关闭清理无效连接的线程
@PreDestroy //容器关闭时执行该方法.
public void shutdown() {
shutdown = true;
synchronized (this) {
//System.out.println("关闭全部链接!!");
notifyAll(); //全部从等待中唤醒.执行关闭操作;
}
}
}
编辑工具API
//如果需要使用spring容器注入对象,则本身应该交给容器管理
@Service
public class HttpClientService {
@Autowired
private CloseableHttpClient httpClient;
@Autowired
private RequestConfig requestConfig;
/**
* 工具API作用:
* 为了用户简化操作而定义该工具API.
* 目的:用户通过如下方法实现远程url调用并且获取响应结果.
* String result = httpClientService.doGet(url,xx,xx);
*
* 编辑工具API考虑的问题:
* 1.参数问题 (1). url请求地址 (2).Map<String,String> (3) 设定字符集编码格式
* 返回值问题: String类型
*
* @param url
* @param params
* @param charset
* @return
*/
public String doGet(String url,Map<String,String> params,String charset) {
//1.校验字符集编码格式
if(StringUtils.isEmpty(charset)) {
//如果用户没有指定字符集,则设定默认值
charset = "UTF-8";
}
//2.封装get请求的参数.
//2.1 get请求时没有参数 http://manage.jt.com/getXXXX
//2.2 get请求有参数 http://manage.jt.com/getXXX?id=1&name=xxx&
if(params != null) {
url += "?";
//遍历map 动态获取key=value
for (Map.Entry<String,String> entry : params.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
url += (key+"="+value+"&");
}
//http://manage.jt.com/getXXX?id=1&name=xxx&
//去除多余的&符号
url = url.substring(0, url.length()-1);
}
//3.准备请求对象
HttpGet httpGet = new HttpGet(url);
httpGet.setConfig(requestConfig); //设定请求超时时间
//4.利用httpClient发起请求
try {
HttpResponse httpResponse = httpClient.execute(httpGet);
//获取状态码信息
int status =
httpResponse.getStatusLine().getStatusCode();
if(status == 200) { //请求正确
//获取返回值实体
HttpEntity httpEntity = httpResponse.getEntity();
String result = EntityUtils.toString(httpEntity,charset);
return result;
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return null;
}
//实现httpClient POST提交
public String doPost(String url,Map<String,String> params,String charset){
String result = null;
//1.定义请求类型
HttpPost post = new HttpPost(url);
post.setConfig(requestConfig); //定义超时时间
//2.判断字符集是否为null
if(StringUtils.isEmpty(charset)){
charset = "UTF-8";
}
//3.判断用户是否传递参数
if(params !=null){
//3.2准备List集合信息
List<NameValuePair> parameters =
new ArrayList<>();
//3.3将数据封装到List集合中
for (Map.Entry<String,String> entry : params.entrySet()) {
parameters.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
//3.1模拟表单提交
try {
UrlEncodedFormEntity formEntity =
new UrlEncodedFormEntity(parameters,charset); //采用u8编码
//3.4将实体对象封装到请求对象中
post.setEntity(formEntity);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
//4.发送请求
try {
CloseableHttpResponse response =
httpClient.execute(post);
//4.1判断返回值状态
if(response.getStatusLine().getStatusCode() == 200) {
//4.2表示请求成功
result = EntityUtils.toString(response.getEntity(),charset);
}else{
System.out.println("获取状态码信息:"+response.getStatusLine().getStatusCode());
throw new RuntimeException();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
public String doPost(String url){
return doPost(url, null, null);
}
public String doPost(String url,Map<String,String> params){
return doPost(url, params, null);
}
public String doPost(String url,String charset){
return doPost(url, null, charset);
}
}
2.1 HttpClient业务调用流程
3. 跨域实现策略-jonsp
jQuery实现JSONP
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JSONP测试</title>
<script type="text/javascript" src="http://manage.jt.com/js/jquery-easyui-1.4.1/jquery.min.js"></script>
<script type="text/javascript">
$(function(){
alert("测试访问开始!!!!!")
$.ajax({
url:"http://manage.jt.com/web/testJSONP",
type:"get", //jsonp只能支持get请求
dataType:"jsonp", //dataType表示返回值类型
//jsonp: "callback", //指定参数名称
//#jsonpCallback: "hello", //指定回调函数名称
success:function (data){ //data经过jQuery封装返回就是json串
alert("ajax跨域成功!!!!!");
alert(data.itemId);
alert(data.itemDesc);
//转化为字符串使用
//var obj = eval("("+data+")");
//alert(obj.name);
}
});
})
</script>
</head>
<body>
<h1>JSON跨域请求测试</h1>
</body>
</html>
package com.jt.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.fasterxml.jackson.databind.util.JSONPObject;
import com.jt.Util.ObjectMapperUtil;
import com.jt.pojo.ItemDesc;
/**
* 京淘后台模块,用来测试JSONP访问
*/
@Controller
@ResponseBody
public class JSONPcontroller {
public JSONPObject testJSONP2(String callback) {
ItemDesc itemDesc = new ItemDesc();
itemDesc.setItemDesc("测试");
itemDesc.setItemId(1000l);
System.out.println(itemDesc);
return new JSONPObject(callback,itemDesc);
}
@RequestMapping("/web/testJSONP")
public String testJSONP(String callback) {
ItemDesc itemDesc = new ItemDesc();
itemDesc.setItemDesc("测试数据");
itemDesc.setItemId(1000L);
String json = ObjectMapperUtil.toJSON(itemDesc);
//需要手动的拼接json串
return callback+"("+json+")";
}
}
访问www.jt.com/JSONP,前台向后台发起跨域Ajax请求
3. 关于同源策略的说明
4. 跨域实现策略-cors
CORS是一个W3C标准,全称是"跨域资源共享"(CORss-origin resource sharing)。CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制
在jt-common中添加cors配置类.实现服务器跨域
package com.jt.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CorsMvcConfig implements WebMvcConfigurer{
//重写关于服务器跨域访问的策略
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") //标识所有的请求可以跨域
.allowedOrigins("http://www.jt.com") //允许哪个网址访问
.allowedMethods("GET", "POST", "DELETE", "PUT", "OPTIONS","HEAD") //请求类型
.allowCredentials(true) //是否允许携带cookie
//1.如果需要进行跨域访问,首先试探性的发起请求.(询问)
//如果服务器允许跨域则在一定的时间之内无需再次试探 默认30分钟
.maxAge(3600); //校验请求的有效期
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>测试JSON跨域问题</title>
<script type="text/javascript" src="http://manage.jt.com/js/jquery-easyui-1.4.1/jquery.min.js"></script>
<script type="text/javascript">
/*$(){}结构必然是jQuery函数类库导入后才能正确执行*/
$(function(){ //是因为jQuery类库定义了该函数(函数名称),所以可以被人调用
alert("我执行了跨域访问");
//利用jQuery发起AJAX请求
$.get("http://manage.jt.com/cors.json",function(data){
console.log(data);
alert(data.itemDesc);
})
})
</script>
</head>
<body>
<h1>JSON跨域请求测试</h1>
</body>
</html>
package com.jt.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.fasterxml.jackson.databind.util.JSONPObject;
import com.jt.pojo.ItemDesc;
@Controller
@ResponseBody
public class CorsPcontroller {
@RequestMapping("/cors.json")
public ItemDesc testJSONP() {
ItemDesc itemDesc = new ItemDesc();
itemDesc.setItemDesc("测试数据");
itemDesc.setItemId(1000L);
System.out.println(itemDesc);
return itemDesc;
}
}