纯js的ajax readyState不返回4,jquery的$.ajax执行error,status200且数据可显示

本文介绍了解决在基于Java的Web项目中,通过jQuery $.ajax或$.post请求无法正常执行回调函数的问题。主要原因是服务器响应的内容类型设置不当,正确的做法是将服务器的contentType设置为text/javascript。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用php请进入 http://blog.youkuaiyun.com/hzz780/article/details/42124607


本文最后的解决方案是使用$.post 并且在java中setPost里面setContentType(text/javascript);

应该的真实原因和$.ajax的解决方案请见我的另一博文http://blog.youkuaiyun.com/hzz780/article/details/31393513《$.ajax接受json, 返回error》



今天开发基于java的web项目的时候,在修改了java文件后,想获取json数据,发现无论如何也无法执行jquery $.ajax里的succes或是$.post或者是其他的回调函数。


jquery的情况

	$.ajax({
		url: "http://localhost:8080/club/privilegeGroup?act=listGroup&type=json",
		data: "username=hzz",
		type: 'post',
		dataType: 'json',
		success: function(data) {
			console.log('success');
		},
		error: function(xhr,textStatus, errorThrown) { 
			console.log(errorThrown);
			console.log('error');
		}
	});
 这样执行的都是error, 但是用chrome审查元素的时候,会发现返回200OK,并且能获取数据。 从headers中看不出什么异常,设断点也没发现什么(如果谁发现了请告诉我一下。)

当时感到莫名其妙,虽然有意识想到是因为 servlet文件改了,但是还是从js这里看看吧。


纯js的情况

想起了5个状态,随又回到了纯js时光

/* test */
function createRequest () {
  // body...
  try {
    request = new XMLHttpRequest();
  }catch(tryMS){
    try{
      request = new AcitveXObject("Msxm12.XMLHTTP");
    }catch(otherMS){
       try{
         request = new AcitveXObject("Microsoft.XMLHTTP");
       }
    catch(failed){
      request=null;
    }
  }
}
   return request;
}
function registerUser(){
  var registerRequest = createRequest();
  if(registerRequest==null){
 
  }
  else{
    var url = "www.twt.edu.cn";//自己设置啦
    var requestData = "";
    registerRequest.onreadystatechange = doShow;
    registerRequest.open("GET",url,true);
    // registerRequest.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
    registerRequest.send(null);
  }
}
function doShow(){
	if (request.readyState == 4) {
		if (request.status == 200) {
			console.log('4');
		}
	}else if(request.readyState == 3){
		console.log("state 3");
	}else if(request.readyState == 2){
		console.log("state 2");
	}else if(request.readyState == 1){
		console.log("state 1");
	}
}
readyState 123都有,就是没有4.   遂笃定,肯定java里面改了什么东西。


	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		response.setContentType("text/javascript");//现在返回的是我们想要的json数据了
		doGet(request,response);
	}


最后发现是response.setContentType(MIME); 导致的, 它的作用是使浏览器区分不同种类的数据, 所以涉及数据交互时,大家一定要注意了。

原来传数据的时候设置成了 text/html,(或者是其他), 将其改成text/javascript即可,即能正确返回json数据了,或是解决回调函数不能执行的问题。



最后使用的代码段是

public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		response.setCharacterEncoding("utf-8");
		request.setCharacterEncoding("utf-8");
		response.setContentType("text/html");
		process(request, response);

	}





public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		response.setContentType("text/javascript");
		doGet(request,response);
	}

$.post(url, data, function(){});



<think>我们使用jQuery$.ajax方法来接收SSE,但需要注意:1.SSE是长连接,数据是流式的,所以我们需要在xhr对象上监听数据流。2.由于数据是分块(chunked)传输,我们需要在readyState为3(正在接收)时处理数据。3.能使用processData:false,因为这里我们处理的是文本流,而是需要原样发送的数据。实际上,processData是针对发送数据的,表示是否将发送的数据转换为查询字符串。对于接收,我们关注的是如何解析响应文本。因此,我们将设置一个xhr对象,并监听其progress事件来获取数据流。同时,我们需要处理事件流的格式(以"data:"开头,以两个换行符结束)。注意:jQueryajax方法默认会处理响应数据,但SSE是持续的流,所以我们能等待整个响应完成(因为永远会完成)。因此,我们需要使用原生的XHR对象,但通过$.ajax设置xhr字段来获取原生XHR,并监听其progress事件。具体步骤:1.$.ajax中设置xhr字段,返回一个自定义的XMLHttpRequest对象,以便我们可以监听progress事件。2.在progress事件中,我们读取xhr的responseText,然后解析事件流。3.由于流式传输,responseText会断增加,我们需要记录上一次处理的位置,或者每次只处理新增的数据。但是,注意:xhr.responseText会包含整个响应到目前为止的所有数据,所以我们需要记录上次处理到的位置。另一种做法:每次只处理新增的数据。我们可以记录上一次的responseText长度,然后只截取新增部分。然而,SSE事件是以两个换行符为分隔符的,所以我们需要将整个响应文本按两个换行符分割,然后处理每个完整的事件,剩下的完整的部分保留到下一次。我们使用一个外部变量(在闭包中)来保存未处理完的数据。实现代码:注意:由于SSE连接是持久的,所以我们需要确保连接会因为超时而被关闭。设置timeout为0表示超时。另外,设置dataType为'text',但实际我们会等到请求完成,而是在过程中处理。由于jQueryajax在success回调中只会在请求完成时触发(而SSE永远会完成),所以我们能依赖success回调,而是通过xhr的progress事件。因此,我们这样做:1.创建一个变量来存储未处理完的缓冲数据(buffer)。2.xhr的progress事件中,读取新增数据,并追加到buffer。3.在buffer中查找两个连续的换行符(即一个空行),这表示一个事件的结束。4.提取每个完整的事件,并解析出数据(通常事件格式为:`data:...\n\n`)。5.处理数据,并将处理过的事件从buffer中移除。示例代码:```javascriptletbuffer='';//用于累积数据$.ajax({url:'/sse-endpoint',method:'GET',dataType:'text',//虽然我们会等到完成,但设置文本类型timeout:0,//超时xhr:function(){//获取默认的xhr对象letxhr=newwindow.XMLHttpRequest();//监听progress事件xhr.addEventListener('progress',function(){//在progress事件中,responseText会包含到目前为止接收到的所有数据//我们只处理新增的数据?但是注意,我们每次需要整个buffer,因为事件可能跨多个chunk//所以,我们每次从buffer中取出完整的事件//从xhr.responseText中获取从上次处理之后的新数据?实际上,我们需要区分新旧,因为buffer已经累积了所有未处理的数据//但是注意:xhr.responseText会包含整个响应,而我们的buffer也是整个响应,这样重复了?//所以,我们改变思路:再使用buffer变量,而是每次用xhr.responseText,然后记录上次处理到的位置(index)//由于我们无法从xhr对象外部获取到上一次的responseText长度,所以我们采用另一种方法:记录上次处理到的位置(在闭包中)//因此,我们在xhr对象上添加一个自定义属性,比如lastIndexif(typeofxhr.lastIndex==='undefined'){xhr.lastIndex=0;}//当前responseTextletresponseText=xhr.responseText;//只处理新增部分?行,因为事件可能跨多个chunk,所以我们需要从上次处理的位置开始到当前整个文本letnewData=responseText.substring(xhr.lastIndex);xhr.lastIndex=responseText.length;//将新增数据追加到buffer(或者直接处理整个responseText?但是这样效率低,所以我们还是用buffer变量,但这里我们换一种方式:直接处理整个responseText会重复处理,所以还是用buffer变量,但用responseText)//实际上,我们之前已经用buffer变量来累积,那么这里我们就用buffer来累积newData//但是,如果我们用buffer变量,那么xhr.responseText就需要了,我们只需要每次把newData加到buffer,然后解析buffer//所以,我们回到buffer的方式://buffer+=newData;//然后解析buffer//但是注意:上面的newData是从responseText中截取的,而responseText是累积的,所以如果我们用buffer,那么buffer和responseText会重复。所以我们用responseText,而是直接用buffer变量来累积所有接收到的数据。//因此,我们改变策略:在progress事件中从xhr.responseText取数据,而是用事件对象event?但是progress事件没有提供新增的数据块,所以我们只能通过比较responseText的长度来获取新增数据。//所以,我们使用buffer变量,并在progress事件中这样更新://从xhr.responseText中获取全部数据,然后与之前保存的整个responseText比较?这样更麻烦。//因此,我们采用记录上次responseText长度的方式,然后每次取新增部分追加到buffer,然后处理buffer。//但是,这里我们已经在xhr的progress事件中,所以可以直接用xhr.responseText,但是如何知道哪些是新的?所以我们在闭包中记录上一次的responseText长度。//由于我们在xhr对象上记录了lastIndex,那么我们可以这样://从lastIndex到当前responseText末尾的数据就是新数据letchunk=responseText.substring(xhr.lastIndex);xhr.lastIndex=responseText.length;buffer+=chunk;//处理buffer,查找连续的\n\nleteventEndIndex;while((eventEndIndex=buffer.indexOf('\n\n'))!==-1){//提取一个事件leteventData=buffer.substring(0,eventEndIndex);buffer=buffer.substring(eventEndIndex+2);//移除已处理的事件,包括两个换行符//解析事件数据:事件数据可能包含多行,每行以"data:"开头//我们只关心以"data:"开头的行letlines=eventData.split('\n');letmessage='';for(letlineoflines){if(line.startsWith('data:')){//如果一行以"data:"开头,那么取后面的部分message+=line.substring(6)+'\n';//6是"data:"的长度}}//去掉最后一个换行符(如果有)if(message.endsWith('\n')){message=message.substring(0,message.length-1);}//此时message是一个完整的消息内容(可能包含多行,每行都是data字段)console.log('ReceivedSSEmessage:',message);//这里可以触发自定义事件或调用回调}});returnxhr;},success:function(data){//由于是流,正常情况下会触发success,除非连接被关闭console.log('SSE连接关闭');},error:function(xhr,status,error){console.error('SSE连接错误:',status,error);//可以尝试重新连接}});```但是,注意:上面的代码中,我们通过监听xhr的progress事件来处理数据。然而,jQueryajax方法本身也会处理响应,它会在请求完成时将响应传递给success回调。由于SSE连接是持久的,所以success回调会在正常情况被调用。另外,我们设置timeout:0以避免超时关闭。关于processData:这个参数是针对发送数据的,我们这里没有发送数据(GET请求,没有data字段),所以需要设置。即使有数据,我们使用GET请求,processData默认为true会将数据对象转换成查询字符串,这符合我们的要求。但是,如果我们要发送数据,并希望保持原始数据格式(比如发送JSON字符串),那么需要设置processData:false。但在这个SSE请求中,我们通常只是GET请求,需要发送复杂数据,所以一般需要设置。因此,这个示例中我们没有使用processData,因为需要。但是,如果我们需要发送数据,例如:```javascript$.ajax({url:'/sse-endpoint',method:'GET',data:{token:'abc123'},//发送数据//默认processData:true,会将{token:'abc123'}转换成?token=abc123//如果想转换,设置processData:false,那么data必须是一个字符串,需要自己手动添加到url后面//但是,对于GET请求,参数必须放在URL中,所以设置processData:false的话,我们需要自己构建URL//所以,一般情况下,我们使用默认的processData:true即可。//...其他设置同上});```所以,我们需要设置processData:false,除非有特殊需求。总结:我们使用$.ajax方法,通过自定义xhr对象并监听其progress事件,实现了SSE的接收和解析。注意:服务器端需要按照SSE的格式发送数据,即每条消息以"data:"开头,以两个换行符结束。示例服务器端代码(Node.jsExpress):```javascriptapp.get('/sse-endpoint',(req,res)=>{res.setHeader('Content-Type','text/event-stream');res.setHeader('Cache-Control','no-cache');res.setHeader('Connection','keep-alive');//发送消息letcount=0;consttimer=setInterval(()=>{count++;res.write(`data:这是第${count}条消息\n\n`);},1000);//当客户端断开连接req.on('close',()=>{clearInterval(timer);});});```这样,客户端就可以接收到每秒一条的消息。注意:上面的客户端代码中,我们解析了事件流,并将每条消息打印到控制台。相关问题:1.如何处理SSE连接断开后的重连?2.如何发送带事件类型(event)的SSE消息?3.在SSE中如何传递JSON数据?</think>下面我将详细介绍如何使用 jQuery 的 `$.ajax()` 方法接收 Server-Sent Events (SSE) 而依赖 `EventSource`,并特别关注 `processData` 参数的处理方式: ### 一、核心解决方案:使用 `$.ajax()` 接收 SSE ```html <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script> $(document).ready(function() { let buffer = ''; function connectSSE() { $.ajax({ url: '/sse-endpoint', method: 'GET', dataType: 'text', // 必须声明为文本类型 processData: false, // 关键:禁止jQuery处理原始数据 xhr: function() { const xhr = new window.XMLHttpRequest(); // 监听数据xhr.addEventListener('progress', function() { // 获取原始响应文本 const newData = xhr.responseText.substring(buffer.length); buffer += newData; // 解析完整事件(以\n\n为分隔符) while (buffer.indexOf('\n\n') !== -1) { const eventEnd = buffer.indexOf('\n\n'); const eventData = buffer.substring(0, eventEnd); buffer = buffer.substring(eventEnd + 2); // 提取数据内容(移除"data: "前缀) const message = eventData.replace(/^data: /, '').trim(); $('#sse-output').append('<div>' + message + '</div>'); } }); return xhr; }, success: function() { console.log('SSE连接成功'); }, error: function(xhr, status, error) { console.error('SSE错误:', status, error); setTimeout(connectSSE, 3000); // 3秒后重连 } }); } connectSSE(); // 启动连接 }); </script> <div id="sse-output"><!-- 消息将显示在这里 --></div> ``` ### 二、关键参数说明 1. **`processData: false`** - 禁止 jQuery 自动转换数据格式,保留原始数据流[^1] - 必需设置,否则会破坏 SSE 的分块传输结构 2. **`dataType: 'text'`** - 明确指定响应类型为原始文本 - 避免 jQuery 尝试解析 JSON 或 XML 3. **自定义 `xhr` 处理** - 覆盖默认 XHR 对象以监听 `progress` 事件 - 直接访问原生 `responseText` 获取流数据 ### 三、服务器端示例 (Node.js/Express) ```javascript app.get('/sse-endpoint', (req, res) => { res.setHeader('Content-Type', 'text/event-stream'); res.setHeader('Cache-Control', 'no-cache'); res.setHeader('Connection', 'keep-alive'); // 每2秒发送消息 let count = 0; const timer = setInterval(() => { count++; res.write(`data: 消息 ${count} - ${new Date().toISOString()}\n\n`); }, 2000); // 客户端断开时清理 req.on('close', () => { clearInterval(timer); res.end(); }); }); ``` ### 四、处理流程详解 1. **数据流接收** - 通过 `progress` 事件捕获分块数据 - 使用 `buffer` 累积完整消息 2. **消息边界处理** ```javascript while (buffer.indexOf('\n\n') !== -1) { const eventEnd = buffer.indexOf('\n\n'); const eventData = buffer.substring(0, eventEnd); // ... 处理完整消息 ... buffer = buffer.substring(eventEnd + 2); // 移除已处理数据 } ``` 3. **错误恢复机制** ```javascript error: function(xhr, status, error) { setTimeout(connectSSE, 3000); // 自动重连 } ``` ### 五、关键注意事项 1. **`processData` 必须设为 `false`** - 若设置为 `true`,jQuery 会尝试解析数据导致流中断[^1] 2. **避免跨域问题** - 确保客户端和服务器同源 - 或配置 CORS 头部:`Access-Control-Allow-Origin: *` 3. **连接管理** - 页面关闭时手动终止连接 - 使用 `beforeSend` 回调添加认证头[^3] ```javascript beforeSend: function(xhr) { xhr.setRequestHeader('Authorization', 'Bearer token'); } ``` 4. **性能优化** - 避免频繁 DOM 操作,使用文档片段批量更新 - 设置 `throttle` 控制消息处理频率 > 此方案通过直接操作 XHR 实现 SSE 接收,适用于需要深度定制或兼容旧系统的场景[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值