9

Spring MVC: Building Web Sites & RESTful services

 3 years ago
source link: https://www.marcobehler.com/guides/spring-mvc
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

MVC Basics: HttpServlets

When writing web applications in Java, with or without Spring (MVC/Boot), you are mostly talking about writing applications that return two different data formats:

  1. HTML → Your web app creates HTML pages that can be viewed in a browser.

  2. JSON/XML → Your web app provides RESTful services, that produce JSON or XML. Javascript-heavy websites or even other web services can then consume the data that these services provide.

  3. (Yes, there’s other data formats and use cases as well, but we’ll ignore them for now.)

How would you write such applications without any framework? Just with plain Java?

Answering this question is essential to really understanding Spring MVC, so do not just skip ahead because you think it has nothing to do with Spring MVC.

Answer

At the lowest level, every Java web application consists of one or more HttpServlets. They generate your HTML, JSON, or XML.

In fact, (almost) every single framework of the 1 million available Java web frameworks (Spring MVC, Wicket, Struts) is built on top of HttpServlets.

How to write HTML pages with HttpServlets

Let’s have a look at a super simple HttpServlet that returns a very simple, static, HTML page.

package com.marcobehler.springmvcarticle;

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

public class MyServletV1 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        if (req.getRequestURI().equals("/")) {
            resp.setContentType("text/html");
            resp.getWriter().print("<html><head></head><body><h1>Welcome!</h1><p>This is a very cool page!</p></body></html>");
        }
        else {
            throw new IllegalStateException("Help, I don't know what to do with this url");
        }
    }
}

Let’s break this down.

public class MyServletV1 extends HttpServlet {

Your servlet extends Java’s HttpServlet class.

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {

To handle (any) GET request, you need to override the doGet() method from the superclass. For POST requests you would override doPost(). Similarly, for all other HTTP methods.

if (req.getRequestURI().equals("/")) {

Your servlet needs to make sure that the URL that comes in is a request that it knows how to handle. For now, the servlet only handles "/", i.e.: it handles www.marcobehler.com, but NOT www.marcobehler.com/hello.

resp.setContentType("text/html");

You need to set the proper Content-Type on the ServletResponse to let the browser know what content you are sending. In this case, it’s HTML.

resp.getWriter().print("<html><head></head><body><h1>Welcome!</h1><p>This is a very cool page!</p></body></html>");

Remember: web sites are just HTML strings! So you need to generate an HTML string, any way you want, and send that back with the ServletResponse. One way of doing that is with the response’s writer.

After writing your servlet, you would register it with a servlet container, like Tomcat or Jetty. If you are using an embedded version of either servlet container, all the code needed to run your servlet would look like this:

package com.marcobehler.springmvcarticle;

import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Wrapper;
import org.apache.catalina.startup.Tomcat;

public class TomcatApplicationLauncher {

    public static void main(String[] args) throws LifecycleException {
        Tomcat tomcat = new Tomcat();
        tomcat.setPort(8080);
        tomcat.getConnector();

        Context ctx = tomcat.addContext("", null);
        Wrapper servlet = Tomcat.addServlet(ctx, "myServlet", new MyServletV2());
        servlet.setLoadOnStartup(1);
        servlet.addMapping("/*");

        tomcat.start();
    }
}

Let’s break this down.

Tomcat tomcat = new Tomcat();
tomcat.setPort(8080);
tomcat.getConnector();

You configure a new Tomcat server which will start on port 8080.

Context ctx = tomcat.addContext("", null);
Wrapper servlet = Tomcat.addServlet(ctx, "myServlet", new MyServletV2());

This is how you register your Servlet with Tomcat. This is the first part, where you simply tell Tomcat about your servlet.

servlet.addMapping("/*");

The second part is letting Tomcat know for what requests the servlet is responsible, i.e. the mapping. A mapping of /* means it’s responsible for any incoming request (/users, /register, /checkout).

tomcat.start();

That’s it. You run your main() method now, go to port 8080 in your favorite web browser (http://localhost:8080/), and you’ll see a nice HTML page.

So, essentially, as long as you keep extending your doGet() and doPost() methods, your whole web application could consist of just one servlet. Let’s try that out.

How to write JSON endpoints with HttpServlets

Imagine that apart from your (pretty empty) HTML index page, you now also want to offer a REST API for your soon-to-be-developed front end. So your React or AngularJS front end would call a URL like this:

/api/users/{userId}

That endpoint should return data in JSON format for the user with the given userId. How could we enhance our MyServlet to do this, again, no frameworks allowed?

package com.marcobehler.springmvcarticle;

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

public class MyServletV2 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        if (req.getRequestURI().equals("/")) {
            resp.setContentType("text/html");
            resp.getWriter().print("<html><head></head><body><h1>Welcome!</h1><p>This is a very cool page!</p></body></html>");
        } else if (req.getRequestURI().startsWith("/api/users/")) {

            Integer prettyFragileUserId = Integer.valueOf(req.getRequestURI().lastIndexOf("/") + 1);

            resp.setContentType("application/json");

            // User user = dao.findUser(prettyFragileUserId)
            // actually: jsonLibrary.toString(user)
            resp.getWriter().print("{\n" +
                    "  \"id\":" + prettyFragileUserId + ",\n" +
                    "  \"age\": 55,\n" +
                    "  \"name\" : \"John Doe\"\n" +
                    "}");
        } else {
            throw new IllegalStateException("Help, I don't know what to do with this url");
        }
    }
}

Let’s break this down.

} else if (req.getRequestURI().startsWith("/api/users/")) {

We add another if to our doGet method, to handle the /api/users/ calls.

Integer prettyFragileUserId = Integer.valueOf(req.getRequestURI().lastIndexOf("/") + 1);

We do some extremely fragile URL parsing. The last part of the URL is the userID, e.g. 5 for /api/users/5. We just assume here that the user always passes in a valid int, which you would actually need to validate!

resp.setContentType("application/json");

Writing JSON to the browser means setting the correct content-type.

// User user = dao.findUser(prettyFragileUserId)
// actually: jsonLibrary.toString(user)
resp.getWriter().print("{\n" +
        "  \"id\":" + prettyFragileUserId + ",\n" +
        "  \"age\": 55,\n" +
        "  \"name\" : \"John Doe\"\n" +
        "}");

Again, JSON is just text, so we can write that directly to the HTTPServletResponse. you would probably use a JSON library to convert our User Java object to this string, but for the sake of simplicity, I won’t show that here.

 The Confident Spring Professional

Just this week I published a fun and practical course that teaches you to confidently find your way around the Spring Ecosystem.

Interested in trying out the full first module?

The problem with our One-Servlet-To-Rule-Them-All Approach

While our servlet above works, there are quite a few problems on the horizon:

  1. Your Servlet needs to do a lot of manual HTTP-specific plumbing, checking request URIs, fumbling with strings, etc. In other words: it needs to know WHAT the users want to do.

  2. It then also needs to find the data for whatever you want to display. In other words: it needs to know the HOW. In our example above, that would be finding the user in a database, which we conveniently commented-out.

  3. It then also needs to convert that data to JSON or to HTML and set the appropriate response types.

Quite a lot of different responsibilities, eh? Wouldn’t it be nicer if you didn’t have to care about all that plumbing? No more request URI and parameter parsing, no more JSON conversions, no more servlet responses?

That’s exactly where Spring MVC comes in.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK