tutorials / java-server / post

Post Requests

tutorial java server post

By now we should know that a client (such as a web browser) works by sending a request to a server. The server responds to that request with the content of a file (such as an HTML, image, or CSS file) or dynamically created content from a servlet or JSP. The client then renders that content to the user.

The requests we’ve used so far only retrieve content from a server. This tutorial shows you how to use other kinds of requests so clients can send data to a server as well.

GET Requests

So far, we’ve learned how to write HTML files, which a client (web browser) can request from a server. We’ve also learned how to write a servlet or JSP file that creates HTML content that is then sent to the client.

This type of request is called a GET request, because the client is getting stuff from the server. Everything we’ve done so far has been a GET request.

URLs and Query Parameters

We’ve already seen that it’s possible to send data to the server. We’ve seen how to use query parameters, like this:

http://localhost:8080/names.html?name=Ada

This URL would request the names.html file and pass it a name parameter wit ha value of Ada. We could then use client-side JavaScript or server-side Java to get that parameter.

We’ve also seen how to use URLs themselves to communicate data to the server:

http://localhost:8080/names/Ada

We could then use a servlet mapped to something like /names/* to allow clients to request any name. We could then use server-side Java code to parse the URL and get the name.

These approaches have a few potential drawbacks:

To work around these limitations, we can use a different kind of request.

POST Requests

If GET requests are the client asking for stuff from a server, POST requests are the client sending data to a server. One of the simplest ways to do this is to use a <form> tag in HTML content. Here’s an example:

<!DOCTYPE html>
<html>
	<head>
		<title>Enter Your Name</title>
	</head>
	<body>
		<h1>What's your name?</h1>
		<form action="/hello" method="POST">
			<input type="text" name="name" value="Ada">
			<br/><br/>
			<input type="submit" value="Submit">
		</form>
	</body>
</html>

This HTML uses a <form> tag with an action attribute that specifies a URL to send the data to. The method attribute specifies that the data should use a POST request. (Try changing this to method="GET" to see the query parameter approach mentioned above!)

Inside the <form> tag, the HTML contains one <input> tag of type text, which shows a text box. The name attribute specifies the name of the parameter to be sent, and the value attribute specifies the default value to display. Then another <input> tag of type submit shows a submit button. When we put this in our web app (which we’ll build in a second), you should see this:

simple name form

Now that we have a form that submits a POST request, we need to write server-side code that processes that request. We already know how to do this: by creating a servlet class. The only difference is that instead of caling the doGet() function, this form will end up calling the doPost() function.

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class GreetingServlet extends HttpServlet {

	@Override
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
		String name = request.getParameter("name");
		response.getOutputStream().println("<h1>Hello " + name + "!</h1>");
	}
}

This servlet specifies a doPost() function, which gets the name parameter and outputs a basic HTML greeting. Now we just need to add a web.xml file that maps the URL (the one we used as the action attribute in our form) to this servlet.

<web-app
	xmlns="http://xmlns.jcp.org/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
		http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
	version="3.1">

	<servlet>
		<servlet-name>GreetingServlet</servlet-name>
		<servlet-class>GreetingServlet</servlet-class>
	</servlet>

	<servlet-mapping>
		<servlet-name>GreetingServlet</servlet-name>
		<url-pattern>/hello</url-pattern>
	</servlet-mapping>

</web-app>

Putting it all together, if you’re using the console, your directory structure should look like this:

Or if you’re using Eclipse, your project should looke like this:

GreetingWebApp project

Eithe way, when we run our server, we can visit our index.html page, which renders a form. When the user submits that form, it creates a POST request that sends the user’s name to our servlet class.

greeting form submission

Postback

Remember that servlet classes can also contain a doGet() function to handle GET requests. We can use this to create a servlet that handles both GET and POST requests for the same URL. This approach of posting data to the same URL the form is accessed from is called a postback.

For example, let’s create a servlet class that handles a /chat URL. When it receives a POST request, it adds a message to the chat. When it receives a GET request, it renders all of the chats it has received- and adds a form that allows a user to send a POST request. That might sound complicated, but it looks like this:

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ChatServlet extends HttpServlet {
	
	private List<String> messages = new ArrayList<String>();

	@Override
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
		String name = request.getParameter("name");
		String message = request.getParameter("message");
		String chatMessage = name + ": " + message;
		messages.add(chatMessage);
		response.getOutputStream().println("<p>Your message has been received.</p>");
		response.getOutputStream().println("<p>Click <a href=\"/chat\">here</a> to go back to the chat.</p>");
	}
	
	@Override
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
		response.getOutputStream().println("<h1>Chat Web App</h1>");
		response.getOutputStream().println("<hr/>");
		
		for(int i = 0; i < messages.size(); i++){
			response.getOutputStream().println("<p>" + messages.get(i) + "</p>");
		}
		response.getOutputStream().println("<hr/>");
		
		response.getOutputStream().println("<form action=\"/chat\" method=\"POST\">");
		response.getOutputStream().println("<input type=\"text\" name=\"name\" value=\"Ada\">");
		response.getOutputStream().println("<input type=\"text\" name=\"message\" value=\"Happy coding!\">");
		response.getOutputStream().println("<input type=\"submit\" value=\"Send\">");
		response.getOutputStream().println("</form>");
	}
}

The doPost() function of our servlet gets the name and message parameters from the posted data, combines them into a chat message, and adds that to a List of String values. It then renders a message telling the user that their message was received, and gives them a link back to the chat.

The doGet() function loops through the List and renders each message as a <p> element. It then renders a <form> that contains name and message input text boxes, as well as a Send button. Notice that the action of the form is the same URL linked to in the message in the doPost() function. We’ll also use that URL in our web.xml file:

<web-app
	xmlns="http://xmlns.jcp.org/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
		http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
	version="3.1">

	<servlet>
		<servlet-name>ChatServlet</servlet-name>
		<servlet-class>ChatServlet</servlet-class>
	</servlet>

	<servlet-mapping>
		<servlet-name>ChatServlet</servlet-name>
		<url-pattern>/chat</url-pattern>
	</servlet-mapping>

</web-app>

This web.xml file maps the /chat URL to our ChatServlet class. When the user visits that URL in a browser, they’re sending a GET request, which renders the (empty at first) chat, as well as a form for submitting a new message. When the user submits that form, that sends a POST request to the same URL, which calls our doPost() function, stores the message, and shows a link back to the chat.

The end result looks like this:

basic chat app

Try opening the page in multiple browser windows:

basic chat app in two windows

Because we’re storing the chat messages on the server, we can have multiple clients chatting!

Redirecting After Post

The above examples work, but they’re a little annoying because the POST request is rendered as a separate page, and the user has to click a link to get back to the main chat page.

We could simple copy the code from the doGet() function into the doPost() function, that way the POST request would render the same thing as the GET request. Better yet, we could just call doGet(request, response); as the last line in our doPost() function. This doesn’t change the request to a GET request, but it would let us render the same thing.

However, that approach has a downside: if the user refreshes (which they have to do in order to see new chats from other users), they’ll see a message like this:

form resubmit error

Because the page is a result of a POST request, the client has to send the data again. This results in the same message being posted multiple times, which isn’t what we want.

Instead, we want to redirect the user back to the GET request after the POST request completes. We can do that using the response.sendRedirect() function:

@Override
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
		String name = request.getParameter("name");
		String message = request.getParameter("message");
		String chatMessage = name + ": " + message;
		messages.add(chatMessage);

		response.sendRedirect("/chat");
	}

The "/chat" parameter tells the client to redirect to the /chat URL. Now our web app works like this:

Note that we’re redirecting to the same URL because we want to trigger the same servlet, but you can redirect to any URL.

JSP

The above examples all use the response.getOutputStream().println() function to output HTML content just to keep the code simple. But remember from the MVC tutorial that this approach is a pain in the neck, and you should use JSP files to render your content instead. That’s still true!

For example, let’s use this JSP file to render our chat web app:

<%@ page import="java.util.List" %>
<!DOCTYPE html>
<html>
	<head>
		<title>Chat Web App</title>
	</head>
	<body>
		<h1>Chat Web App</h1>
		<% 
			List<String> messages = (List<String>) request.getAttribute("messages"); 
			for(int i = 0; i < messages.size(); i++){
				String message = messages.get(i);	
		%>
			<p><%= message %></p>
			
		<% } %>
		
		<hr/>
		
		<form action="/chat" method="POST">
		<input type="text" name="name" value="Ada">
		<input type="text" name="message" value="Happy coding!">
		<input type="submit" value="Send">
		</form> 
	</body>
</html>

This JSP file iterates over the messages to show each one in a <p> tag, and then includes the form at the bottom. Now our servlet looks like this:

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ChatServlet extends HttpServlet {
	
	private List<String> messages = new ArrayList<String>();

	@Override
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
		String name = request.getParameter("name");
		String message = request.getParameter("message");
		String chatMessage = name + ": " + message;
		messages.add(chatMessage);

		response.sendRedirect("/chat");
	}
	
	@Override
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
		request.setAttribute("messages", messages);
		request.getRequestDispatcher("/WEB-INF/chat.jsp").forward(request, response);
	}
}

Our doPost() function still adds a message to the messages list and then redirects back to the GET request. And now our doGet() function adds that list to the request and forwards to our JSP file, which renders the messages.

Polling with AJAX

Our chat web app is getting better, but it still has a big drawback: to see new messages, users have to refresh the page. There are various ways to improve this, but let’s go with a simple approach we’ve already learned about: AJAX!

Remember from the AJAX tutorial that AJAX allows us to write JavaScript code that requests content from a URL without navigating the browser to a new page. We can use this to request the chat messages without needing to refresh the page.

Let’s do this using a static HTML file that contains JavaScript code that uses AJAX to request the chat messages once per second:

<!DOCTYPE html>
<html>
<head>
	<title>Chat Web App</title>
	
	<script>
		function getChats(){
			var ajaxRequest = new XMLHttpRequest();
			ajaxRequest.onreadystatechange = function(){
			
				if(ajaxRequest.readyState == 4){
					//the request is completed, now check its status
					if(ajaxRequest.status == 200){
						document.getElementById("chats").innerHTML = ajaxRequest.responseText;
					}
					else{
						console.log("Status error: " + ajaxRequest.status);
					}
				}
				else{
					console.log("Ignored readyState: " + ajaxRequest.readyState);
				}
			}
			ajaxRequest.open('GET', '/chat');
			ajaxRequest.send();
			
			//refresh the chats in one second
			setTimeout(getChats, 1000);
		}
	
	</script>	
</head>
<body onload="getChats()">
	<h1>Chat Web App</h1>
	<hr />
	<div id="chats"></div>
	<hr />
	
	<form action="/chat" method="POST">
		<input type="text" name="name" value="Ada">
		<input type="text" name="message" value="Happy coding!">
		<input type="submit" value="Send">
	</form> 

</body>
</html>

This index.html file contains JavaScript that defines a getChats() function. That function uses AJAX to make a GET request to the /chats URL, and it adds the returned content to the chats <div> element. The code schedules this function once per second, which causes the page to automatically refresh the chat every second- without refreshing the page! This HTML also contains the form for submitting new chat messages.

Now we need to change our servlet to return the chat content:

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ChatServlet extends HttpServlet {
	
	private List<String> messages = new ArrayList<String>();

	@Override
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
		String name = request.getParameter("name");
		String message = request.getParameter("message");
		String chatMessage = name + ": " + message;
		messages.add(chatMessage);

		response.sendRedirect("/index.html");
	}
	
	@Override
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
		for(int i = 0; i < messages.size(); i++){
			response.getOutputStream().println("<p>" + messages.get(i) + "</p>");
		}
	}
}

Now our doGet() function just iterates over the messages and outputs each one in a <p> tag. It no longer needs to render the whole page- this is just the inner content that’s fethed using AJAX.

Also notice that the doPost() function redirects back to the /index.html URL, which is where our static HTML file is located.

Here it is in action:

auto-refreshing chat web app

This approach of using AJAX to poll for messages every second works and is simple, but it’s not great for scalability: what if you have a million users? You’ll be making a million requests to your server per second, and most of those requests will not return anything new. There are smarter ways to handle this, but we’ll leave that for a separate tutorial.

Posting with AJAX

We can also use AJAX to create a POST request. This approach has the benefit of not needing to refresh the page whenever we send a chat, which in our example means the user doesn’t have to enter their name every time they send a message.

To make a POST request using AJAX, we start by creating an XMLHttpRequest object:

var ajaxPostRequest = new XMLHttpRequest();

Then we call the open() function, giving it a parameter of POST and the URL we want to send the request to:

ajaxPostRequest.open("POST", "/chat");

We also have to call the setRequestHeader() function, which tells the server how we’re going to specify the data. In our case, we want to use the same format used by query parameters, which we mentioned above.

ajaxPostRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

Finally, we send the data in the format we just specified. In this case, we’re using the query parameter format, where parameters are assigned values in pairs separated by & ampersands:

var name = document.getElementById('name').value
var message = document.getElementById('message').value
ajaxPostRequest.send("name=" + name + "&message=" + message);

This sends the POST request to the server, which then calls our the doPost() function in our servlet.

Putting it all together, it looks like this:

<!DOCTYPE html>
<html>
<head>
	<title>Chat Web App</title>
	
	<script>
		function getChats(){
			var ajaxRequest = new XMLHttpRequest();
			ajaxRequest.onreadystatechange = function(){
			
				if(ajaxRequest.readyState == 4){
					//the request is completed, now check its status
					if(ajaxRequest.status == 200){
						document.getElementById("chats").innerHTML = ajaxRequest.responseText;
					}
					else{
						console.log("Status error: " + ajaxRequest.status);
					}
				}
				else{
					console.log("Ignored readyState: " + ajaxRequest.readyState);
				}
			}
			ajaxRequest.open('GET', '/chat');
			ajaxRequest.send();
			
			//refresh the chats in one second
			setTimeout(getChats, 1000);
		}
		
		function postChat(){
			console.log("posting chat");	
			var ajaxPostRequest = new XMLHttpRequest();
			ajaxPostRequest.open("POST", "/chat");
			ajaxPostRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
			var name = document.getElementById('name').value
			var message = document.getElementById('message').value
			ajaxPostRequest.send("name=" + name + "&message=" + message);
		}
	
	</script>	
</head>
<body onload="getChats()">
	<h1>Chat Web App</h1>
	<hr />
	<div id="chats"></div>
	<hr />
	
	<input id="name" type="text" name="name" value="Ada">
	<input id="message" type="text" name="message" value="Happy coding!">
	<input type="submit" value="Send" onclick="postChat()">

</body>
</html>

Notice that we no longer use the <form> tag, so the browser doesn’t send the POST request automatically when the button is clicked. Instead, we send the request ourselves using AJAX.

Now our chat web app looks like this:

AJAX posting chat web app

Notice how the page is no longer refreshed!

Other Approaches

Like everything else in programming, there are multiple ways to solve some of the problems introduced above. AJAX is one solution. We could also use cookies or sessions to solve some of the problems. We could also do something more advanced like using long polling (basically the same as above, except the server waits until a new message is sent to return the content) or WebSockets (instead of asking the server for messages, the server would send the messages to the client whenever there’s an update).

But the point of this tutorial was to introduce the idea of POST requests, so I wanted to keep everything else as simple as possible. But don’t be afraid to do more research and improve upon the ideas that were introduced here!

Other Request Types

We’ve talked about GET and POST request methods, and they’re by far the most common. But there are actually several other types of requests.

You can read more about them on MDN or W3Schools or Wikipedia. But for now you can just keep GET and POST in mind, as they’ll cover 99% of what you need to do at this point.

Homework