如何解决 Ajax 跨域前端请求跨域问题不到的问题

Ajax跨域问题解决(Ajax JSONP)
Ajax跨域问题解决(Ajax JSONP)
因WEB安全原因,Ajax默认情况下是不能进行跨域请求的,遇到这种问题,自然难不倒可以改变世界的程序猿们,于是JSONP(JSON with Padding)被发明了,其就是对JSON的一种特殊,简单来说就是在原有的JSON数据上做了点手脚,从而达到可以让网页可以跨域请求。在现在互联网技术对“前后.
因WEB安全原因,Ajax默认情况下是不能进行跨域请求的,遇到这种问题,自然难不倒可以改变世界的程序猿们,于是JSONP(JSON with Padding)被发明了,其就是对JSON的一种特殊,简单来说就是在原有的JSON数据上做了点手脚,从而达到可以让网页可以跨域请求。在现在互联网技术对“前后分离”大规模应用的时期,JSONP可谓意义重大啊。
假设我们原来的JSON数据为 {"hello":"你好","veryGood":"很好"}那么对应的JSONP的格式就是 functionName({"hello":"你好","veryGood":"很好"}) ,其中“functionName”不是固定值,自己定义。
在SpringMVC中实现支持JSONP总结为如下几点:
response 响应类型为 application/javascript
进行json请求的URL中需要携带参数 jsonp 或 callback,并指定值。
如 http://mydomain/index.jsonp?callback=myfun
或 http://mydomain/index.jsonp?jsonp=myfun
其中 myfun 就为最终包裹在原有JSON外的函数名
如果你在配置文件中配置过 MappingJacksonJsonView 那么请修改使用 MappingJackson2JsonView
Controller 中的方法需要返回 ModelAndView 或者未使用 @ResponseBody 注解的返回 String 页面。也就是说最终怎么呈现结果,交由SpringMVC来给我们完成。
针对显式注解 @ResponseBody 的方法 (我们本来就是直接响应JSON的),我们需要做特殊处理,使用 MappingJacksonValue 进行封装处理。
说的有点抽象,下面看实际怎么做。当然我们的原则就是“不对原有已经实现的代码进行任何修改”。
本文代码以SpringBoot为例。
使用 WebMvcConfigurerAdapter 配置 ContentNegotiatingViewResolver ,代码如下:
@Configuration
public class MyWebAppConfigurer
extends WebMvcConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(MyWebAppConfigurer.class);
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(MediaType.TEXT_HTML)
.ignoreAcceptHeader(true);
* Configure ContentNegotiatingViewResolver
public ViewResolver contentNegotiatingViewResolver(ContentNegotiationManager manager) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager(manager);
// Define all possible view resolvers
List&ViewResolver& resolvers = new ArrayList&ViewResolver&();
resolvers.add(new JsonViewResolver());
resolver.setViewResolvers(resolvers);
JsonViewResolver.java
public class JsonViewResolver implements ViewResolver{
private MappingJackson2JsonV
public JsonViewResolver() {
view = new MMappingJackson2JsonView();
view.setPrettyPrint(true);
public View resolveViewName(String viewName, Locale locale) throws Exception {
MMappingJackson2JsonView.java这个类并不是必须的,我写出来也是为了说明如果遇到和我一样的问题时怎么解决,注意看代码中的注释说明
public class MMappingJackson2JsonView extends MappingJackson2JsonView {
* 排除JSON转换的时候 model 中自动加入的对象&br/&
* 如果你在项目中使用了 @ControllerAdvice , 要特别注意了,我们在这里就是要排除掉因为@ControllerAdvice自动加入的值
protected Object filterModel(Map&String, Object& model) {
Map&String, Object& result = new HashMap&String, Object&(model.size());
if (model != null) {
for (Map.Entry&String, Object& entry : model.entrySet()) {
if (!"urls".equals(entry.getKey())) {// 对我在项目中使用 @ControllerAdvice 统一加的值,进行排除。
result.put(entry.getKey(), entry.getValue());
return super.filterModel(result);
上面提到的 MappingJackson2JsonView 我们已经在代码中使用了。至于我还说到的 MappingJacksonValue 并不需要我们在哪里直接使用,其实 MappingJackson2JsonView 的源码中已经使用它做好了处理。我们只需要按上面说的在请求json的后面增加 jsonp 或 callback 参数即可。
那么如果我们对于使用 @ResponseBody 注解直接响应JSON的该如何处理呢?Follow Me ……
原理:ResponseBody 是通过 RequestResponseBodyMethodProcessor 来处理的,那我们就对这个类做一下包装处理。RequestResponseBodyMethodProcessor 实现自接口 HandlerMethodReturnValueHandler,又因为Spring内部,同一个类型只能用一个的原则,我们实现自己的 HandlerMethodReturnValueHandler 实现类后,其中将原来的 RequestResponseBodyMethodProcessor 的原有对象包装进去,当我们完成自己的处理后,再讲处理权交给包装的 RequestResponseBodyMethodProcessor 对象。
对 ResponseBody 还需要处理响应类型 (application/javascript)在Spring内部,先从 ContentNegotiationStrategy 的方法 resolveMediaTypes 中读取 requestMediaTypes ,然后再去匹配 MappingJackson2HttpMessageConverter 中所有支持的 MediaTypes ,从而确定最终响应的 contentType。代码层面的处理也就是 ContentNegotiationStrategy 的 resolveMediaTypes 与 MappingJackson2HttpMessageConverter 的 getSupportedMediaTypes 结果对比处理。为了满足我们JSONP的要求,requestMediaTypes 和 getSupportedMediaTypes 中都要包含 application/javascript
所以我们还要做如下2步处理:1、为 MappingJackson2HttpMessageConverter 添加 application/javascript 响应类型支持。2、包装 ServletPathExtensionContentNegotiationStrategy ,重写 resolveMediaTypes 方法,根据JSONP特性 (callback参数),自动确定 application/javascript 请求类型。
下面是代码:其中 ResponseBodyWrapHandler 和 ContentNegotiationStrategyWrap 为包装类,ResponseBodyProcessor 为统一处理类。
package org.springboot.sample.config.
import java.util.ArrayL
import java.util.L
import org.springframework.beans.factory.InitializingB
import org.springframework.beans.factory.annotation.A
import org.springframework.context.annotation.C
import org.springframework.http.MediaT
import org.springframework.http.converter.HttpMessageC
import org.springframework.http.converter.json.MappingJackson2HttpMessageC
import org.springframework.web.accept.ContentNegotiationM
import org.springframework.web.accept.ContentNegotiationS
import org.springframework.web.accept.ServletPathExtensionContentNegotiationS
import org.springframework.web.method.support.HandlerMethodReturnValueH
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerA
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerA
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodP
* 处理Spring默认加载好的类,在原有类上使用自定义类进行包装处理。
* @author 单红宇()
* @myblog http://blog.csdn.net/catoop/
* @create 日
@Configuration
public class ResponseBodyProcessor extends WebMvcConfigurerAdapter implements InitializingBean {
@Autowired
private RequestMappingHandlerA
@Autowired
private ContentNegotiationM
public void afterPropertiesSet() throws Exception {
List&HandlerMethodReturnValueHandler& returnValueHandlers = adapter.getReturnValueHandlers();
List&HandlerMethodReturnValueHandler& handlers = new ArrayList&&(returnValueHandlers);
decorateHandlers(handlers);
adapter.setReturnValueHandlers(handlers);
processContentNegotiationManager();
private void processContentNegotiationManager() {
// 处理JSONP的响应ContentType
List&ContentNegotiationStrategy& strategies = manager.getStrategies();
for (int i = 0; i & manager.getStrategies().size(); i++) {
if (manager.getStrategies().get(i) instanceof ServletPathExtensionContentNegotiationStrategy) {
strategies.set(i, new ContentNegotiationStrategyWrap(manager.getStrategies().get(i)));
manager = new ContentNegotiationManager(strategies);
private void decorateHandlers(List&HandlerMethodReturnValueHandler& handlers) {
for (HandlerMethodReturnValueHandler handler : handlers) {
if (handler instanceof RequestResponseBodyMethodProcessor) {
// 用自己的ResponseBody包装类替换掉框架的,达到返回Result的效果
ResponseBodyWrapHandler decorator = new ResponseBodyWrapHandler(handler);
int index = handlers.indexOf(handler);
handlers.set(index, decorator);
public void extendMessageConverters(List&HttpMessageConverter&?&& converters) {
for (HttpMessageConverter&?& httpMessageConverter : converters) {
// 为 MappingJackson2HttpMessageConverter 添加 "application/javascript"
// 支持,用于响应JSONP的Content-Type
if (httpMessageConverter instanceof MappingJackson2HttpMessageConverter) {
MappingJackson2HttpMessageConverter convert = (MappingJackson2HttpMessageConverter) httpMessageC
List&MediaType& medisTypeList = new ArrayList&&(convert.getSupportedMediaTypes());
medisTypeList.add(MediaType.valueOf("application/charset=UTF-8"));
convert.setSupportedMediaTypes(medisTypeList);
super.extendMessageConverters(converters);
package org.springboot.sample.config.
import java.util.A
import java.util.LinkedHashS
import java.util.S
import java.util.regex.P
import org.apache.commons.logging.L
import org.apache.commons.logging.LogF
import org.springframework.core.MethodP
import org.springframework.http.converter.json.MappingJacksonV
import org.springframework.util.StringU
import org.springframework.web.context.request.NativeWebR
import org.springframework.web.method.support.HandlerMethodReturnValueH
import org.springframework.web.method.support.ModelAndViewC
* ResponseBody 处理类
http://blog.csdn.net/catoop/
public class ResponseBodyWrapHandler implements HandlerMethodReturnValueHandler{
protected final Log logger = LogFactory.getLog(getClass());
private final HandlerMethodReturnValueH
private Set&String& jsonpParameterNames = new LinkedHashSet&String&(Arrays.asList("jsonp", "callback"));
* Pattern for validating jsonp callback parameter values.
private static final Pattern CALLBACK_PARAM_PATTERN = Pattern.compile("[0-9A-Za-z_\\.]*");
private String getJsonpParameterValue(NativeWebRequest request) {
if (this.jsonpParameterNames != null) {
for (String name : this.jsonpParameterNames) {
String value = request.getParameter(name);
if (StringUtils.isEmpty(value)) {
if (!isValidJsonpQueryParam(value)) {
if (logger.isDebugEnabled()) {
logger.debug("Ignoring invalid jsonp parameter value: " + value);
protected boolean isValidJsonpQueryParam(String value) {
return CALLBACK_PARAM_PATTERN.matcher(value).matches();
public ResponseBodyWrapHandler(HandlerMethodReturnValueHandler delegate){
this.delegate=
public boolean supportsReturnType(MethodParameter returnType) {
return delegate.supportsReturnType(returnType);
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
String jsonpParameterValue = getJsonpParameterValue(webRequest);
if (jsonpParameterValue != null) {
if (!(returnValue instanceof MappingJacksonValue)) {
MappingJacksonValue container = new MappingJacksonValue(returnValue);
container.setJsonpFunction(jsonpParameterValue);
returnValue =
delegate.handleReturnValue(returnValue,returnType,mavContainer,webRequest);
package org.springboot.sample.config.
import java.util.ArrayL
import java.util.A
import java.util.LinkedHashS
import java.util.L
import java.util.S
import java.util.regex.P
import org.apache.commons.logging.L
import org.apache.commons.logging.LogF
import org.springframework.http.MediaT
import org.springframework.util.StringU
import org.springframework.web.HttpMediaTypeNotAcceptableE
import org.springframework.web.accept.ContentNegotiationS
import org.springframework.web.context.request.NativeWebR
* 对 ServletPathExtensionContentNegotiationStrategy 进行包装
http://blog.csdn.net/catoop/
public class ContentNegotiationStrategyWrap implements ContentNegotiationStrategy {
protected final Log logger = LogFactory.getLog(getClass());
private final ContentNegotiationS
private Set&String& jsonpParameterNames = new LinkedHashSet&String&(Arrays.asList("jsonp", "callback"));
* Pattern for validating jsonp callback parameter values.
private static final Pattern CALLBACK_PARAM_PATTERN = Pattern.compile("[0-9A-Za-z_\\.]*");
private String getJsonpParameterValue(NativeWebRequest request) {
if (this.jsonpParameterNames != null) {
for (String name : this.jsonpParameterNames) {
String value = request.getParameter(name);
if (StringUtils.isEmpty(value)) {
if (!isValidJsonpQueryParam(value)) {
if (logger.isDebugEnabled()) {
logger.debug("Ignoring invalid jsonp parameter value: " + value);
protected boolean isValidJsonpQueryParam(String value) {
return CALLBACK_PARAM_PATTERN.matcher(value).matches();
public ContentNegotiationStrategyWrap(ContentNegotiationStrategy strategy) {
this.strategy =
public List&MediaType& resolveMediaTypes(NativeWebRequest request) throws HttpMediaTypeNotAcceptableException {
// JSONP 响应类型处理 ---- BEGIN
String jsonpParameterValue = getJsonpParameterValue(request);
if (jsonpParameterValue != null) {
List&MediaType& mediaTypes = new ArrayList&&(1);
mediaTypes.add(MediaType.valueOf("application/javascript"));
return mediaT
// JSONP 响应类型处理 ---- END
return this.strategy.resolveMediaTypes(request);
然后新建一个PageController来测试下效果:
@Controller
public class PageController {
private static final Logger log = LoggerFactory.getLogger(PageController.class);
* 默认页&br/&
* @RequestMapping("/") 和 @RequestMapping 是有区别的
* 如果不写参数,则为全局默认页,加入输入404页面,也会自动访问到这个页面。
* 如果加了参数“/”,则只认为是根页面。
* @author SHANHY
@RequestMapping(value = {"/","/index"})
public String index(Map&String, Object& model){
model.put("time", new Date());
model.put("message", "小单,你好!");
return "index";
* 响应到JSP页面page1
* @author SHANHY
@RequestMapping("/page1")
public ModelAndView page1(){
log.info("&&&&&&&& PageController.page1");
// 页面位置 /WEB-INF/jsp/page/page.jsp
ModelAndView mav = new ModelAndView("page/page1");
mav.addObject("content", hello);
@RequestMapping("/testJson")
@ResponseBody
public Map&String, String& getInfo(@RequestParam(required=false) String name,
@RequestParam(required=false) String name1) {
Map&String, String& map = new HashMap&&();
map.put("name", name);
map.put("name1", name1);
测试结果截图如下:请求JSONP数据
正常请求JSON数据
直接请求显示页面
至此,我们的服务端代码改造完毕,我们在 “不对原有业务代码进行任何修改的前提下” 完成了处理,接下来是在HTML页面中使用jQuery来请求JSONP实现跨域访问。
将下面的代码存储为一个普通的HTML页面,然后用浏览器打开就可以测试了,当然别忘了启动你的web服务:
&!DOCTYPE html&
&meta charset="utf-8"&
&script src="http://code.jquery.com/jquery-2.1.3.min.js"&&/script&
&script type="text/javascript"&
$(document).ready(function(){
$("#b1").click(function(){
url:'http://localhost:8080/myspringboot/testJson.json?name=Shanhy&name1=Lily',
type: "get",
async: false,
dataType: "jsonp",
jsonp: "callback", //服务端用于接收callback调用的function名的参数(请使用callback或jsonp)
jsonpCallback: "fun_jsonpCallback", //callback的function名称
success: function(json) {
alert(json.name); // 吐槽一下云栖社区,使用 a l e r t 提交不了文章,请自行修改为英文半角(吐槽时间: 13:37)。
error: function(){
alert('Request Error');
$("#b2").click(function(){
url:'http://localhost:8080/myspringboot/testJson.json?name=Shanhy&name1=Lily',
type: "get",
async: false,
//dataType: "jsonp",
//jsonp: "callback", //服务端用于接收callback调用的function名的参数(请使用callback或jsonp)
//jsonpCallback: "fun_jsonpCallback", //callback的function名称
success: function(json) {
alert(json.name);
error: function(){
alert('Request Error');
&div id="div1"&&h2&jQuery AJAX 的跨域请求&/h2&&/div&
&button id="b1"&JSONP请求 (预期结果为成功)&/button& &br/&
&button id="b2"&JSON请求 (预期结果为失败)&/button&
至此,相信已经满足应用的需求,对部署容器不需要做任何修改。不过还有另一种很简单的方法来支持Ajax的跨域请求,那就是在响应头中添加支持,如下:
// 指定允许其他域名访问(必须)
response.addHeader("Access-Control-Allow-Origin", "*");
// 响应类型(非必须)
response.addHeader("Access-Control-Allow-Methods", "POST");
// 响应头设置(非必须)
response.addHeader("Access-Control-Allow-Headers", "x-requested-with,content-type");
如果你前端使用到 ApacheServer、Nginx 那么也可以在他们的配置文件中直接配置,具体查一下资料即可。这里有一点要注意:Access-Control-Allow-Origin 的 * 是允许所有,如果要针对域名设置,直接指定域名即可,但是请注意这里你不可以用逗号分割的方式同时配置多个域名。如果你真的需要,可以参考如下代码:
List&String& domainList = new ArrayList&&();
domainList.add("http://www.domain1.com");
domainList.add("http://www.domain2.com");
domainList.add("http://localhost:8088");
String requestDomain = request.getHeader("origin");
log.info("requestDomain = " + requestDomain);
if(domainList.contains(requestDomain)){
response.addHeader("Access-Control-Allow-Origin", requestDomain);
response.addHeader("Access-Control-Allow-Methods", "GET");
response.addHeader("Access-Control-Allow-Headers", "x-requested-with,content-type");
实际应用中,根据自己的需要选择合适的方法。
用云栖社区APP,舒服~
【云栖快讯】青年们,一起向代码致敬,来寻找第83行吧,云栖社区邀请大神彭蕾、多隆、毕玄、福贝、点评Review你的代码,参与互动者将选取50位精彩回复赠送“向代码致敬”定制T恤1件,最终成为“多隆奖”的小伙伴还将获得由阿里巴巴提供的“多隆奖”荣誉证书和奖杯。&&
你好 源码能发我一份么
阿里云移动APP解决方案,助力开发者轻松应对移动app中随时可能出现的用户数量的爆发式增长、...
通过机器学习和数据建模发现潜在的入侵和攻击威胁,帮助客户建设自己的安全监控和防御体系,从而解...
是将源站内容分发至全国所有的节点,缩短用户查看对象的延迟,提高用户访问网站的响应速度与网站的...
为您提供简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效...
五四专家成长记忆没有更多推荐了,
不良信息举报
举报内容:
ajax跨域请求数据的解决方案
举报原因:
原文地址:
原因补充:
最多只允许输入30个字
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!ajax出现跨域问题的前提以及解决跨域问题的方法
ps:最近在本地访问远程服务器接口的时候,出现跨域的问题,以前的解决方案是后台会给支持跨域请求的问题,现在不给支持跨域的请求,那么如何在前端支持访问远程api接口呢?!,这里通过nginx反向代理来解决跨域问题。
什么情况下存在跨域问题? 如何解决跨域 1.通过nginx代理方式 2.通过node.js npm corsproxy代理解决跨域问题
什么情况下存在跨域问题?
1.本地html访问外部服务器资源。
2.同在本地,不在同一端口下也存在跨域。
如何解决跨域
1.通过nginx代理方式
设置nginx conf/nginx.conf文件进行反向代理
https://xxx.xx.xx.xx:port/
对应后台api url
location /ser/ {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
#proxy_set_header Host $
#proxy_set_header X-Real-IP $remote_
#proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_
proxy_set_header Cookie $http_
proxy_pass https://xxx.xx.xx.xx:port/;
proxy_cookie_path
proxy_cookie_domain domino.server nginx.
ajax请求url
/ser/user/login
为nginx中自定义的模块
proxy_cookie_path 与location 定义的别名
项目需放置在nginx目录下的html目录中.
2.通过node.js npm corsproxy代理解决跨域问题
通过npm 安装
npm install -g corsproxy-https
corsproxy 运行命令
出现上图所示即完成跨域:vcD4NCjxwPtTaYWpheMfrx/N1cmzM7bzTyc/S1MnPtdjWt7y0v8k6PC9wPg0KPHByZSBjbGFzcz0="brush:">
https://localhost:.18.110:8080/user/login
这样就完成了跨域访问.jquery&ajax&ie9及以下浏览跨域不执行问题
网站中搜索页面输入关键词后,通过 jquery.ajax 加载数据, chrome, firefox, IE10+
都可以顺利加载数据,但是IE9及以后版本不执
产生此问题的原因
jquery.ajax error 函数显示未执行 拒绝访问
在 jquery.ajax
调用前写&jQuery.support.cors
= true&(仅此法无法解决)
针对 拒绝访问
是由于浏览器安全机制导致的,解决方法为点击IE浏览器的的“工具-&Internet
选项-&安全-&自定义级别”将“其他”选项中的“通过域访问数据源”选中为“启用”或者“提示”,点击确定就可以了(但是此法需要用户自行设置不太现实)
(推荐)对于浏览器跨域&IE10+
才支持withCredentials属性,IE9-
不支持,跨域对象只能用XDomainRequest对象,而jQuery并不兼容XDomainRequest..&针对此方法网络上有解决的插件&jQuery-ajaxTransport-XDomainRequest
使用插件需要注意的事项
CORS requires
the&Access-Control-Allow-Origin&header
to be present in the AJAX response from the server.
Only GET or
When POSTing,
the data will always be sent with a&Content-Type&of&text/plain
Only HTTP or
Protocol must
be the same scheme as the calling page
asynchronous
[来自插件说明]
结合网站上的事项
网站上的请求为&GET
请求的服务端设置&header("Content-type:
text/ charset=utf-8"); header('Access-Control-Allow-Origin:
请求到的数据格式为&html
jquery.ajax
参数需要设置&dataType:
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

我要回帖

更多关于 ionic3 跨域请求问题 的文章

 

随机推荐