I'm unable to figure out what is going on here in this ultra simple example.
Problem Summary: I have a simple servlet that appears to run just fine if I drive it manually... by issuing its URL from the browser. By 'just fine' I mean: I can see in the browser HTML page whatever I write in the servlet response.
However, if I issue the very same URL via Ajax code, the servlet processes the request fine and even 'appears' to be writing out the response fine ... but, just that I do not see any response on the Ajax client code side and thus neither in my browser HTML page.
Further, if I make my XHR request syncrhonous, the browser error console shows the following exception:
Error: uncaught exception: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsIXMLHttpRequest.send]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: file:///home/sd/Desktop/test.html :: callServlet :: line 35" data: no]
Environment:
Browser: Firefox 3.5.3
Servlet container: Tomcat 6.0.20
OS: Linux / Fedora 11
Ajax code:
<!-- test.html -->
<html>
<head>
<script>
var req;
function $(id) {
return document.getElementById(id);
}
function servletCallback() {
var field = $("debugHtmlId");
field.innerHTML += "readyState='" + req.readyState + "'<br> ";
field.innerHTML += "status='" + req.status + "'<br> ";
field.innerHTML += "responseText='" + req.responseText + "' | <br> ";
}
req = new XMLHttpRequest();
req.onreadystatechange = servletCallback;
function callServlet() {
// With async mode off, I get the
// Exception listed above.
// req.open("GET", "http://localhost:8080/aaa/bbb?f=test", false);
req.open("GET", "http://localhost:8080/aaa/bbb?f=test", true);
req.send(null);
}
</script>
</head>
<body>
<input id="callserv" type="submit" value="Call Servlet" onclick="callServlet();" />
<span id="debugHtmlId"></div>
</body>
</html>
Servlet code:
// servlet code
public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse rsp)
throws ServletException, IOException {
rsp.setContentType("text/html");
String val = req.getParameter("f");
if(val.equals("test")) {
// Increment value.
++_count;
// Return value.
PrintWriter out = rsp.getWriter();
out.printf("%d\n", _count);
out.close();
// This shows up fine in servlet log.
System.out.printf("%d\n", _count);
}
}
// This variable is incremented and returned on each call to doGet().
private int _count = 0;
}
EDIT:
Including the result: Here's what I see for example as the value of my innerHTML of my debugHtmlId element.
readyState='1'
readyState='1'
readyState='2'
status='0'
responseText='' |
readyState='4'
status='0'
responseText='' |
Strange behavior: Notice also that my readystatechange handler is getting re-entered! I mean, I was expecting to see readyState='...' status='...' responseText='...' triads for every state change...
The problem was:
I had loaded the above HTML in my browser not from the Tomcat/web server but from my local file system. I thought, for development I won't need to deploy the HTML to the server.
Since what I originally wanted works fine now, I'm not so worried now about the exception I was getting in the sync mode of ajax.
I was working through this same problem. It's definitely a cross domain issue and in my case the javascript file was the same server as the servlet and i was downloading and and calling the script from an html page that was not. Just like Harry I was loading the html locally and getting a 0 status.
The solution for me was to add
resp.addHeader("Access-Control-Allow-Origin", "*");
to the SERVLET code - this allows the javascript to call the servlet from outside the domain. It was killing me since i knew the servlet was getting hit, the server debugger was logging the GET - but the response was empty and the status was 0. all of the code on the question will work fine, if you add that header to the servlets response.
Try this: Don't call out.close(); on the PrintWriter. Instead, call out.flush();.
I look at the code and did two things to work on my CentOS 5 Linux dev box with Tomcat5:
Modified the code and rename my servlet to eServlet:
req.open("GET", "http://localhost:8080/aaa/bbb?f=test", true);
to
req.open("GET", "eServelet?f=test", true);
Modified WEB-INF/web.xml and added servlet mapping
<servlet>
<servlet-name>eServletApps</servlet-name>
<servlet-class>eServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>eServletApps</servlet-name>
<url-pattern>/eServlet</url-pattern>
</servlet-mapping>
Compiled with command:
# javac -classpath $CATALINA_HOME/common/lib/servlet-api.jar eServlet.java
Loaded it and it Works without any problems
Related
I identified an issue with postdata not being sent through AJAX on my server. To debug it, I wrote the following fairly minimalistic piece of javascript to test a simple AJAX call :
function my_custom_ajax(target_page, target_element , postdata_contents) {
// Sending the XMLHttpRequest as postdata
var xhr = new XMLHttpRequest();
xhr.open("POST", target_page, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;");
xhr.setRequestHeader("Content-length", postdata_contents.length);
xhr.setRequestHeader("Connection", "close");
xhr.send(postdata_contents);
// Waiting for the request to return
xhr.onreadystatechange = return_data;
// If all went well, we update the target element
function return_data()
{
if(xhr.readyState === 4)
{
if(xhr.status === 200)
{
// We update the target element
document.getElementById(target_element).innerHTML = xhr.responseText;
}
// Throw error in case of 404 or such
else
document.getElementById(target_element).innerHTML = "XHR can't be loaded";
}
// Throw error in case request got interrupted or didn't work out
else
document.getElementById(target_element).innerHTML = "XHR error";
}
}
It is called with the following HTML :
<div onClick="my_custom_ajax('test_page.php?xhr','my_id','postdata_test');">
Click me
</div>
<div id="my_id">
xhr response will appear here
</div>
And calls a PHP page which contains only this :
exit(var_dump($_POST));
When running this piece of code in my Apache localhost or another Apache server I own, it does pass whatever is in postdata_contents as postdata. The exit(var_dump($_POST)); does show that it works properly, and does print the value of the postdata I passed to it.
However, when running this same piece of code on the Apache server where it does not work, all I get is « array(0) { } », as in, no postdata is passed according to the PHP file.
Here is Firefox's dev tool view of the request details (in french, sorry, but should be obvious what is what) :
The debug tool shows that the postdata contents are properly being sent :
However, the returned content show that the postdata was somehow not passed :
On my localhost and my other Apache server, everything is exactly the same until the very last step, where the postdata is properly passed (the var_dump message is styled but you can easily see the gist of it : postdata_test is part of $_POST) :
After hours of fiddling with the configuration of this Apache server and trying all of the debug methods and breakpoints I could think up, my nerves are too worked up to continue thinking about this rationally for now. As I have no option of using another server or just copypasting my local Apache configuration file on the new server, I defer this question to you all, hoping that somebody can figure it out or once encountered something similar.
Thanks in advance,
Eric B.
Solved it myself by accident, I had mod_dumpio activated on the server and it started working once I turned it off.
I do not know what mod_dumpio was doing to deny XHR POST but not generic HTTP POST, but at least that's solved.
Hope this will help someone else some day.
(on a sidenote, I realize the postdata query in my example was malformed, should have been « postdata_test= » instead of « postdata_test », so add that equal sign if you are stuck in my situation and want to run the same tests I did)
I have a program using jsp and servlets that checks the database for an email address. First the jsp page sends the email to the doPost method of the servlet, then the servlet checks the database, if email exists I use request.setAttribute set a success message and then forward it to the same jsp page, if it doesn't exist it forwards an error message.
The problem i am facing is that it refreshes the jsp page, whereas when I used php for another program i was able to display just by echo without refreshing the entire page.
You can use JQuery Ajax request to do this:
Servlet:
response.setContentType("text/plain");
response.setCharacterEncoding("UTF-8");
response.getWriter().write("good email or not?");
Jsp:
$('#emailButton').click(function() {
$.post('mySevlet', function(responseText) {
if (responseText == 'good'){
$('#statusDiv').text('you are good to go.');
}else{
$('#statusDiv').text('Stop Righ there!');
}
});
});
In the project we have a config singleton object, which holds configuration fetched from the server (ServerConfig). It is defined like
Ext.define('SI.ServerConfig', {
singleton: true,
constructor: function () {
this.callParent(arguments);
// Closure which holds the serverConfig after the synchronus request
var serverConfig;
Ext.Ajax.request({
url: 'example.de/getConfig.php',
method: 'POST',
async: false,
success: function(response){
var responseObj = Ext.JSON.decode(response.responseText);
serverConfig = responseObj.serverConfig;
}
});
//definition of setter/getter function for the serverConfig closure
this.get = function (optionName, defaultVal) {
if (Ext.isDefined(serverConfig[optionName])){
return serverConfig[optionName];
}
return defaultVal;
};
this.set = function (optionName, value) {
serverConfig[optionName] = value;
};
}
});
In the constructor we have a closure which holds after the synchrone Ajax request the server config object.
We need to make a synchrone request, because the server config values are needed in various other classes to provide config bevore creation.
With a setter and a getter function we provide access to the values defined in it.In every controller/view/model we need access to the server config, we require the singleton.
Ext.define('SI.view.TestView', {
extends: 'Ext.panel.Panel',
requires: ['SI.ServerConfig'],
// This fails most of the time in Firefox, but works every time in Chrome and IE 8+
title: SI.ServerConfig.get('testTitle')
});
But when we access the singleton in the config object in class definition, the server config singleton is not instantiated in Firefox all the time. In Chrome and in Internet Explorer 8+ it is working as expected.
So to be shure we have the singleton ready to use we tried the following. We moved the Application definition in the callback of an Ext.require. But this does not fix it for Firefox.
Ext.require([
'SI.ServerConfig'
], function () {
Ext.define('SI.Application', {
// ....
}); /
});
In the Firefox Debugger the following is logged:
Synchrone XMLHttpRequests am Haupt-Thread sollte nicht mehr verwendet werden,
weil es nachteilige Effekte für das Erlebnis der Endbenutzer hat.
Für weitere Hilfe siehe http://xhr.spec.whatwg.org/
From the XHR spezification:
Synchronous XMLHttpRequest outside of workers is in the process of being
removed from the web platform as it has detrimental effects to the end
user's experience. (This is a long process that takes many years.)
Developers must not pass false for the async argument when the JavaScript
global environment is a document environment. User agents are strongly
encouraged to warn about such usage in developer tools and may experiment with
throwing anInvalidAccessError exception when it occurs.
So synchrone requests will be removed in the future and only allowed in webworkers.
We need a solution for this.
The problem only occurs in developer mode, when we build it with sencha app build, it works in Firefox...
Thanks for any suggestions.
And-y
Update index.html -> index.php
I changed index.html into index.php like #colinramsay suggested and included the server config object before microloader is included.
Now the warning about Synchrone XMLHttpRequests is gone in Firefox.
But the problem when accessing the singleton in the config object in class definition still remains for the Firefox.
Another totally different approach is to change your application's root index.html to an index.php that does something like the following:
<!DOCTYPE HTML>
<html manifest="">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta charset="UTF-8">
<title></title>
<script type="text/javascript">
var SI = {};
<?php
echo 'SI.ServerConfig = { "testTitle": "some string" }';
?>
</script>
<!-- The line below must be kept intact for Sencha Cmd to build your application -->
<script id="microloader" type="text/javascript" src="bootstrap.js"></script>
</head>
<body></body>
</html>
This allows you to change your get code to something like:
function get(optionName, defaultVal) {
if (Ext.isDefined(SI.ServerConfig[optionName])){
return SI.ServerConfig[optionName];
}
return defaultVal;
}
You are using PHP to directly output your configuration to the HTML page as JSON before Ext JS and your application load.
Try this:
Ext.define('SI.view.TestView', {
extends: 'Ext.panel.Panel',
requires: ['SI.ServerConfig'],
constructor: function() {
this.callParent();
this.setTitle(SI.ServerConfig.get('testTitle'));
}
});
I suspect this is a load order issue. In your original code, Ext.define would run at the same time SI.ServerConfig, before the requires kicks in, so SI.ServerConfig might not have loaded via requires. By calling it in the constructor you can be sure that all of the requires have been fulfilled and so it should be available.
I have a download link on my page which works just fine but it doesn't refresh/redirects my page. Here's my code.
#RequestMapping(method = RequestMethod.POST, params = "exportToXML")
public String exportToXML(HttpServletResponse response, Model model, #ModelAttribute(FILTER_FORM) ScreenModel form,
BindingResult result, OutputStream out,
HttpSession session) throws IOException {
ZipOutputStream zipout;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
zipout = new ZipOutputStream(baos);
ZipEntry ze = new ZipEntry("file.xml");
zipout.putNextEntry(ze);
zipout.write(string.getBytes());
zipout.closeEntry();
zipout.close();
baos.close();
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-disposition", "attachment; filename=xx.zip");
response.getOutputStream().write(baos.toByteArray());
response.getOutputStream().close();
response.getOutputStream().flush();
return VIEW_NAME;
}
I've removed irrelevant pieces of code to make it a little bit shorter. I have also tried with #ResponseBody but it gives the same result as code above.
Any advice will be helpful
You can't download file and make refresh/redirect.
I'll try to explain causes. Request flow is illustrated here:
where yellow circle is your controller. When you return view name front controller looks for appropriate view template (simply jsp, tiles or other, depending on configured view resolver) gets response and write generated html (or not html) code to it.
In your case you perform actions:
response.getOutputStream().write(baos.toByteArray());
response.getOutputStream().close();
response.getOutputStream().flush();
After that actions spring can't open response and write refreshed page to it (because you do it before).
So you can change your method signature to:
public void exportToXML(HttpServletResponse response, Model model, #ModelAttribute(FILTER_FORM) ScreenModel form,
BindingResult result, OutputStream out,
HttpSession session) throws IOException {
and delete last "return VIEW_NAME". Nothing will change.
You can:
response.setHeader("Refresh", "1; url = index");
This refresh the page after 1 second after response on URL: "index".
It will not. The browser opens the ms-excel contentType in a new window or you get a download prompt. The page that initiated download never get a chance to handle the redirect or page transition.
If the download + page refresh is desired, a JavaScript function could initiate the download and direct the user to next page, that page could say 'your download will commence shortly' or something similar.
You could after download, call a javascript function to submit to you controller and them show a different page.
I used the following structure to solve my problem. The function submit the form and back, in other words, you download the file and refresh the previous link. Using this solution, you can even show and hide messages errors with some template render, in my case, I used Thymeleaf.
To make the code more readable, I removed the Thymeleaf tags.
JS file:
function submitAndBack(formId) {
let formDoc = document.getElementById(formId);
formDoc.submit();
sleep(1000).then(function() {
window.history.back();
});
}
function sleep(milliseconds) {
return new Promise(function (resolve) {
setTimeout(resolve, milliseconds);
});
}
HTML form:
<form id="myForm" method="POST" action="/some/url">
<label for="inputText">Some info</label>
<input id="inputText" name="inputText" type="text">
<button onclick="submitAndBack('myForm')">
Submit
</button>
</form>
From LottaLava answer, I got the idea to solve a similar problem.
In JavaScript, after form submission, I wait for one and a half-second and then reload the page. The waiting time is to time for the backend to download the file (here exportToXML) and return the response and then in JavaScript refresh the page.
form.submit();
sleep(1500).then(function() {
document.location.reload();
});
Here form.submit() calls the controllers action, in your case exportToXML.
The sleep function is as follows:
function sleep(milliseconds) {
return new Promise(function (resolve) {
setTimeout(resolve, milliseconds);
});
}
The sleep function refers to here
I have a JSP page called CreateProcessGroup.jsp and I use an annotation controller to map requests to CreateProcessGroup.htm to that page. But I'm having an interesting issue when I request the page from browser it works, when send a request using jQuery $.get method I get 404 (CreateProcessGroup.htm not found) is there a difference between two requests?
My JSP page just under WebContent dir and JS file under WEBContent/Jquery my function sending the request like below:
function SendCreateProcessGroupRequest()
{
var pid = $('#pid').val();
var description = $('#processGroupDescription').val();
var x = "/CreateProcessGroup.htm";
alert(x);
$.get(x, { pid: 62, description: description },
function(data){
alert("Data Loaded: " + data);
});
}
Do I need to give the URL as ../CreateProcessGroup.htm? Indeed I tried:
/CreateProcessGroup.htm
../CreateProcessGroup.htm
/../CreateProcessGroup.htm
../../CreateProcessGroup.htm
/../../CreateProcessGroup.htm
My guess is DispatcherServlet can not map Ajax requests to Controllers but this is stupid isn't it?
How can i get rid of the situation?
Thanks all.
Try this instead:
var x = "CreateProcessGroup.htm";
If the page you're requesting is beside the one making the request there's no need for a path in front, it will (by default) make a request to the same path just with that page/handler on the end.