复习电商笔记-38-JS跨越和JSONP跨域

*JS跨域

首页加载完后鼠标移动到一级栏目上,触发js调用,如下图所示可以看到返回的json串。但有个奇怪的地方,怎么串被category.getDataService()括起来和一般的json串不同呢?

首页菜单json串格式

什么是跨域?

我们经常会在页面上使用ajax请求访问其他服务器上的数据,此时,客户端会出现跨域问题。它是由于javascript语言安全限制中同源策略造成的。简单的说,同源策略是指一段脚本只能读取来自同一来源的窗口和文档的属性。这里同一来源是指主机名、协议和端口号的组合。

    例如:

实现跨域方案:

    方案一:拼接这样一个串返回。

    方案二:采用jsonp方式。

    方案一简单易实现,但通用性不强。所以我们采用方案二。

Jsonp解决跨域访问原理

jQuery不支持跨域访问

在后台jt-manage-web项目中创建test.json。

{
	"key":"jt good!"
}

创建一个test.htm文件

<!doctype html>
<html>
 <head>
 </head>
 <body>
	<script type="text/javascript" src="http://manage.jt.com/js/jquery-easyui-1.4.1/jquery.min.js"></script>
	<script type="text/javascript">
		$(function(){
			$.get("http://manage.jt.com/test.json",function(data){
				alert(data.key);
			});
		});
	</script>

 </body>
</html>

将test.htm文件拷贝到jt-web项目中

访问:http://www.jt.com/test.htm

XMLHttpRequest cannot load http://manage.jt.com/test.json. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://www.jt.com' is therefore not allowed access.

从www.jt.com去访问了manage.jt.com网站,所以跨域请求资源,因为在js设计时为了安全性设计为同源策略,跨域不能被访问,jquery也是js,遵循同源策略,所以jquery不允许跨域访问导致报错。

 

如何解决这个问题呢?

js虽然被设计为同源,但有个标签<script>标签例外,它可以跨域请求资源。请求资源中必须包含回调函数,相当于自定义了一个js函数,只不过写法特殊。

修改test.json

test({
	"key":"jt good!"
})

修改test.htm

<!doctype html>
<html>
 <head>
 </head>
 <body>
	<script type="text/javascript">
		//在test.json中回调
		function test(data){
			alert(data.key);
		}
	</script>

	<script type="text/javascript" src="http://manage.jt.com/test.json"></script>
 </body>
</html>

达到跨域请求的效果;

为什么<script>中的内容就可以跨域呢?

<script type="text/javascript">
		function test(data){
			alert(data.key);
		}

		var data = {key:"jt good!"}
		test(data);

		test({key:"jt good!"});
	</script>

从上面例子就可以看出<script src=“”></script>标签实现的就是将src请求返回的内容,变成js的语句。然后这个语句就是调用test函数,而参数就是json字符串,被js转成js的对象。

<script type="text/javascript" src="http://manage.jt.com/test.json"></script>获取到的是字符串,怎么能直接解析字符串呢?
var obj = eval("({'key':'ok'})");
fn(obj);

<script>返回的字符串被eval转换为js对象

SpringMVC支持Jsonp方式

整体配置方式

springmvc是通过<mvc:annotation-driven/>中注入了一个json的转换,然后只需要在类上加@ResponseBody注解,就可以自动将对象转换为json串。我们来扩展它。

    第一步:引入扩展类,继承MappingJackson2HttpMessageConverter:

package com.jt.common.spring.exetend.jackson;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonProcessingException;

public class CallbackMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {

    // 做jsonp的支持的标识,在请求参数中加该参数
    private String callbackName;

    @Override
    protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException,
            HttpMessageNotWritableException {
        // 从threadLocal中获取当前的Request对象
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
                .currentRequestAttributes()).getRequest();
        String callbackParam = request.getParameter(callbackName);
        if (StringUtils.isEmpty(callbackParam)) {
            // 没有找到callback参数,直接返回json数据
            super.writeInternal(object, outputMessage);
        } else {
            JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType());
            try {
   String result = callbackParam + "(" + super.getObjectMapper().writeValueAsString(object)
                        + ");";
                IOUtils.write(result, outputMessage.getBody(), encoding.getJavaName());
            } catch (JsonProcessingException ex) {
                throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
            }
        }

    }

    public String getCallbackName() {
        return callbackName;
    }

    public void setCallbackName(String callbackName) {
        this.callbackName = callbackName;
    }

}

第二步:加入依赖jar包

<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>${spring.version}</version>
		</dependency>

第三步:修改jt-manage-web的springmvc-config.xml

<!-- MVC注解驱动 -->
<mvc:annotation-driven>
	<!-- 采用自定义方案 -->
	<mvc:message-converters>
		<!-- 定义json转化器,支持json跨域 -->
		<bean
			class="com.jt.common.spring.exetend.jackson.CallbackMappingJackson2HttpMessageConverter">
			<!-- 跨域请求中的请求参数名 -->
			<property name="callbackName" value="callback"></property>
		</bean>
	</mvc:message-converters>
</mvc:annotation-driven>

这样扩展后,之前的代码丝毫不用改变。而且方法可以通用。

单独配置方式

import org.springframework.http.converter.json.MappingJacksonValue;
//检查 http://sso.jt.com/user/check/{param}/{type}
@RequestMapping("/check/{param}/{type}")
@ResponseBody
public Object check(String callback, @PathVariable String param,@PathVariable Integer type){
	try{
		Boolean b = userService.check(type, param);
		MappingJacksonValue mappingVal = new MappingJacksonValue(SysResult.oK(b));
		mappingVal.setJsonpFunction(callback);
		return mappingVal;
	}catch(Exception e){
		return SysResult.build(201, "检查用户、手机、邮箱出错!");
	}
}

问题:messageConverter扩展后引起的问题

上传文件时,会出现下面错误,如下图所示:

格式错误,是什么原因造成这个问题呢?

    是由于我们修改了<mvc:annotation-driven>,它就将所有的结果转换为json格式。那之前只<mvc:annotation-driven>怎么就可以?它默认会加入什么?查看spring-webmvc-4.1.3.RELEASE.jar下的AnnotationDrivenBeanDefinitionParser类的getMessageConverters方法,在521行它做了判断:

if (convertersElement == null || Boolean.valueOf(convertersElement.getAttribute("register-
defaults"))) {

如果用户指定了messageConverter,它不会再将给我们注册默认的converter。由于我们加了信息转换后,那就只能转换成json了。那它默认加了什么?查看525行可以看到:

RootBeanDefinition stringConverterDef = 
createConverterDefinition(StringHttpMessageConverter.class, source);

默认它加入了一个字符串格式转换。返回一个纯字符串。

    将它添加到jt-manage-web的springmvc-config.xml文件中:

<!-- MVC注解驱动 -->
<mvc:annotation-driven>
	<!-- 采用自定义方案 -->
	<mvc:message-converters>
		<!-- 定义文本转化器 -->
		<bean class="org.springframework.http.converter.StringHttpMessageConverter">
			<constructor-arg index="0" value="UTF-8" />
		</bean>
		<!-- 定义json转化器,支持json跨域 -->
		<bean
		class="com.jt.common.spring.exetend.jackson.CallbackMappingJackson2HttpMessageConverter">
			<!-- 跨域请求中的请求参数名 -->
			<property name="callbackName" value="callback"></property>
		</bean>
	</mvc:message-converters>
</mvc:annotation-driven>

重启,测试文件上传,可以正常使用了。

猜你喜欢

转载自blog.csdn.net/qq_40680190/article/details/84293058
今日推荐