REST на примере Spring MVC

REST очень популярная технология в последнее время, которая позволяет обмениваться данными. В этом уроке я покажу как реализовать правильный на моё мнение REST в Spring MVC.

Шаг 0. Что такое Web Service?

Web Service (Веб служба) — это технология позволяющая предоставлять общий доступ к определённым данным системы.

Существует несколько видов Web Service (в дальнейшем WS), но мы будем рассматривать в этом уроке только один из них, а именно WS REST.

REST (Representational State Transfer), «передача состояния представления» — стиль построения архитектуры распределенного приложения. Был описан и популяризован в 2000 году Роем Филдингом (Roy Fielding), одним из создателей протокола HTTP. Самой известной системой, построенной в значительной степени по архитектуре REST, является современная Всемирная паутина.

Такое разъяснение WS REST дает Википедия, но если чесно, то мне слабо понятно что это из выше указанного термина.

Если своими словами, то REST — это технология, которая обеспечит возможность предоставление доступа к данным внешним системам, а также она описывает набор правил, которые нужно соблюдать, чтобы реализовать WS REST.

Если например брать WS SOAP, который является технологией у которой есть стандарты, то REST в свою очередь не имеет стандартов, а всеголишь имеет набор общепринятых правил для его реализации.

Задача

Любая работа программиста состоит с составления задачи, которую он должен решить. Наша задача следуящая:

— Реализовать WS REST и протестировать его в боевых условиях с помощью тестового приложения.

 

Шаг 1. Создание проекта и подключение зависимостей

Создаем Maven проект называем его SpringRESTExam:

Теперь давайте подключим зависимости, так как мы будем реализовывать REST с помощью Spring Framework, то нам потребуются следующие зависимости:

<properties>
    <javax.servlet>3.0.1</javax.servlet>
    <spring.version>3.2.6.RELEASE</spring.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>${javax.servlet}</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

Мы подключили две зависимости, первая это Spring MVC, вторая это поддержка Servlet API.

Еще для удобства я использую следующие два плагина:

<plugins>
    <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
            <source>1.7</source>
            <target>1.7</target>
        </configuration>
    </plugin>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <configuration>
            <failOnMissingWebXml>false</failOnMissingWebXml>
        </configuration>
    </plugin>
</plugins>

Первый плагимы говорит о том, что проект будет компилироваться Java 7, а второй конфигурирует сборку war архива и говорит, что мы не требуемся в использовании web.xml файла.

 

Шаг 2. Создание сущности

Для того чтобы продемонстрировать REST в котором мы будем манипулировать данными мы создадим объект MyDataObject в который добавим все необходимые для наших целей поля.

package com.devcolibri.rest.domain;

import java.util.Calendar;

public class MyDataObject {

    private Calendar time;
    private String message;

    public MyDataObject(Calendar time, String message) {
        this.time = time;
        this.message = message;
    }

    public MyDataObject() {
    }

    public Calendar getTime() {
        return time;
    }

    public void setTime(Calendar time) {
        this.time = time;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

Я предпочел хранить в этом объекте время и сообщение.

И так что же нам надо сделать?

3732_2

Мы создадим веб-страницу на которой будет 4 кнопки, которые будут демонстрировать 4 метода протокола HTTP.

 

Шаг 3. Конфигурирование Spring

Прежде чем преступить к созданию REST нам нужно сконфигурировать Spring, это не сложно.

Для начало создадим класс WebAppConfig:

package com.devcolibri.rest.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.JstlView;
import org.springframework.web.servlet.view.UrlBasedViewResolver;

// Говорим, что это конфигурация
@Configuration
// Включаем MVC
@EnableWebMvc
// Указываем где искать контроллеры и остальные компоненты
@ComponentScan("com.devcolibri.rest")
public class WebAppConfig {

    @Bean
    public UrlBasedViewResolver setupViewResolver() {
        UrlBasedViewResolver resolver = new UrlBasedViewResolver();
        // указываем где будут лежать наши веб-страницы
        resolver.setPrefix("/pages/");
        // формат View который мы будем использовать
        resolver.setSuffix(".jsp");
        resolver.setViewClass(JstlView.class);

        return resolver;
    }

}

Теперь нужно зарегистрировать эту конфигурацию в Spring Context, для этого создадим еще один класс Initializer:

package com.devcolibri.rest.config;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

public class Initializer implements WebApplicationInitializer {
    private static final String DISPATCHER_SERVLET_NAME = "dispatcher";

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
        // регистрируем конфигурацию созданую высше
        ctx.register(WebAppConfig.class);
        // добавляем в контекст слушателя с нашей конфигурацией
        servletContext.addListener(new ContextLoaderListener(ctx));

        ctx.setServletContext(servletContext);

        // настраиваем маппинг Dispatcher Servlet-а
        ServletRegistration.Dynamic servlet =
                servletContext.addServlet(DISPATCHER_SERVLET_NAME, new DispatcherServlet(ctx));
        servlet.addMapping("/");
        servlet.setLoadOnStartup(1);
    }
}

Теперь можно приступать к созданию REST сервиса.

 

Шаг 4. Создание REST сервиса

Возможно вам уже приходилось работать с API, любым. Так вот мы сейчас с помощью REST создадим свой маленький API.

В виде REST сервиса будет выступать Spring Controller, поэтому создаем MainController:

package com.devcolibri.rest.controller;

import com.devcolibri.rest.domain.MyDataObject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.Calendar;
import java.util.GregorianCalendar;

@Controller
// мапим наш REST на /myservice
@RequestMapping(value = "/myservice")
public class MainController {

    // этот метод будет принимать время методом GET и на его основе
    // отвечать клиенту
    @RequestMapping(value= "/{time}", method = RequestMethod.GET)
    @ResponseBody
    public MyDataObject getMyData(@PathVariable long time) {
        return new MyDataObject(Calendar.getInstance(), "Это ответ метода GET!");
    }

    // этот метод будет принимать Объект MyDataObject и отдавать его клиенту
    // обычно метод PUT используют для сохранения либо для обновления объекта
    @RequestMapping(method = RequestMethod.PUT)
    @ResponseBody
    public MyDataObject putMyData(@RequestBody MyDataObject md) {
        return md;
    }

    // этот метод будет методом POST отдавать объект MyDataObject
    @RequestMapping(method = RequestMethod.POST)
    @ResponseBody
    public MyDataObject postMyData() {
        return new MyDataObject(Calendar.getInstance(), "это ответ метода POST!");
    }

    // этот метод будет принимать время методом DELETE
    // и на его основе можно удалит объект
    @RequestMapping(value= "/{time}", method = RequestMethod.DELETE)
    @ResponseBody
    public MyDataObject deleteMyData(@PathVariable long time) {
        return new MyDataObject(Calendar.getInstance(), "Это ответ метода DELETE!");
    }
}

Данные REST не выполняет действий с БД либо другими WS он просто имитирует работу REST.

Значит в результате у нас будет одна ссылка:

http://localhost:8080/myservice — обращаясь по этой ссылке разными HTTP методами можно выполнять разные действия.

Например:

http://localhost:8080/myservice/101245648 (GET) — числа, это время в милисекундах.

http://localhost:8080/myservice (PUT) — но HTTP запрос должен содержать объект (ниже будет пример).

http://localhost:8080/myservice (POST) — позволит получить готовый объект.

http://localhost:8080/myservice/101245648  (DELETE) — позволит удалить объект по времени например.

Как вы видите обращаясь по одной и той же ссылке мы выполняем разные действия.

 

Шаг 5. Создаем веб-страницу для проверки нашего WS REST

Для этого я решил в этом же проекте создать простую JSP страницу в папке /pages/ назвал её index.jsp и в ней с помощью JQuery и технологии Ajax выполняю запросы к нашему WS.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Devcolibri.com exam REST</title>
</head>

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript">
    var prefix = '/devcolibri-rest/myservice';

    var RestGet = function() {
        $.ajax({
            type: 'GET',
            url:  prefix + '/' + Date.now(),
            dataType: 'json',
            async: true,
            success: function(result) {
                alert('Время: ' + result.time
                        + ', сообщение: ' + result.message);
            },
            error: function(jqXHR, textStatus, errorThrown) {
                alert(jqXHR.status + ' ' + jqXHR.responseText);
            }
        });
    }

    var RestPut = function() {
        var JSONObject= {
            'time': Date.now(),
            'message': 'Это пример вызова PUT метода'
        };

        $.ajax({
            type: 'PUT',
            url:  prefix,
            contentType: 'application/json; charset=utf-8',
            data: JSON.stringify(JSONObject),
            dataType: 'json',
            async: true,
            success: function(result) {
                alert('Время: ' + result.time
                        + ', сообщенеи: ' + result.message);
            },
            error: function(jqXHR, textStatus, errorThrown) {
                alert(jqXHR.status + ' ' + jqXHR.responseText);
            }
        });
    }

    var RestPost = function() {
        $.ajax({
            type: 'POST',
            url:  prefix,
            dataType: 'json',
            async: true,
            success: function(result) {
                alert('Время: ' + result.time
                        + ', сообщение: ' + result.message);
            },
            error: function(jqXHR, textStatus, errorThrown) {
                alert(jqXHR.status + ' ' + jqXHR.responseText);
            }
        });
    }

    var RestDelete = function() {
        $.ajax({
            type: 'DELETE',
            url:  prefix + '/' + Date.now(),
            dataType: 'json',
            async: true,
            success: function(result) {
                alert('Время: ' + result.time
                        + ', сообщение: ' + result.message);
            },
            error: function(jqXHR, textStatus, errorThrown) {
                alert(jqXHR.status + ' ' + jqXHR.responseText);
            }
        });
    }
</script>

<body>

    <h3>Это простой пример использования REST c помощью Ajax</h3>

    <button type="button" onclick="RestGet()">Метод GET</button>
    <button type="button" onclick="RestPost()">Метод POST</button>
    <button type="button" onclick="RestDelete()">Метод DELETE</button>
    <button type="button" onclick="RestPut()">Метод PUT</button>

</body>
</html>

Выглядеть эта страничка будет следующим образом:

 

Шаг 6. Деплоим и тестируем

Собираем проект:

Теперь запускам Tomcat либо в моём случае Glassfish4 и деплоим:

На этом все :) Возникнут вопросы, задавайте их в коментариях.

Урок создан: 11 января 2014 | Просмотров: 52120 | Автор: Александр Барчук | Правила перепечатки


Добавить комментарий

Добавить комментарий

Ваш e-mail не будет опубликован.

Комментарии:

  • 21 января 2014 в 08:58

    Дмитрий

    Спасибо за статью.
    Не могли бы Вы написать статью про работу с REST с использованием Spring Security. Всё таки сейчас без аутентификации и авторизации врят ли делают REST сервисы.

    • 21 января 2014 в 09:06

      Александр Барчук

      Вполне с вами согласен :) Но делают, например API какие-то. Да вот какраз работаю над этим уроком, как только его доделаю опубликую :)
      А вы чтобы не пропустить публикацию урока, подпишитесь на форме сайта :)

      • 21 января 2014 в 10:24

        Дмитрий

        Спасибо.
        Еще интересна статья про Cross Origin Requests. Для REST это тоже важно.

  • 10 июня 2014 в 18:51

    Лёха

    Добрый день.

    Подскажите, какой частью кода отдается сам файл index.jsp? На tomcat не смог запустить весь код
    Буду очень благодарен

  • 25 октября 2014 в 00:27

    Erik

    Попробовал пример c Jetty и Tomcat стартовая страница отображается. Но при нажатии на кнопку.
    WARNING: No mapping found for HTTP request with URI [/devcolibri-rest/myservice/1414185917916] in DispatcherServlet with name ‘dispatcher’

  • 08 декабря 2014 в 15:18

    Alexander

    Хорошее руководство, но в нем пропущена информация о создании контроллера IndexController. Без него не удавалось запустить проект, а когда посмотрел, как сделано в исходниках автора и внес соответствующие коррективы в свой проект, все заработало.

  • 11 июня 2015 в 17:47

    Евгений

    Не хватило:

    jstl
    jstl
    1.2

  • 17 июня 2015 в 17:39

    qwe

    где класс JstlView и что в нем должно быть?

    • 17 июня 2016 в 08:51

      Денис

      При запуске проекта .

      java.lang.NoClassDefFoundError: javax/servlet/jsp/jstl/core/Config

      • 07 июля 2016 в 17:34

        Max_Gradus

        У всех у кого вылетает подобный exception включите в pom.xml еще одну зависимость

        jstl
        jstl
        1.2

  • 25 июня 2016 в 13:52

    Аноним

    а где прописывается, что MyDataObject должен конвертироваться в json?

  • 02 августа 2016 в 20:16

    Dmytro

    Автору 3- (т.к. есть недароботки, + код в некоторых местах не оптимален). Если на сервере не лежит библиотека jstl — то либо добавляем в папку lib, либо в Мавене добавляем зависимость. Также пропущен стартовый контроллер(«/») — правда, он есть в прилагаемом коде, но на данной станице его нет. Также необходимо добавить. + Необходимо вынести javascript в отдельный файл. И т.д. и т.п.