Введение

Заголовки HTTP имеют основополагающее значение в процессе связи между клиентом и сервером. Они предоставляют ценную информацию о запросе или ответе, такую ​​как тип контента, набор символов, языковые предпочтения и т. д. Spring MVC, одна из широко используемых платформ Java, предоставляет простой и элегантный способ работы с заголовками HTTP с использованием аннотаций @RequestHeader и @ResponseHeader. В этом руководстве мы углубимся в то, как работают эти аннотации и как они могут улучшить наши веб-приложения.

Введение в HTTP-заголовки

Заголовки HTTP были неотъемлемой частью веб-коммуникаций с момента появления Интернета. Эти заголовки, являющиеся частью каждого HTTP-запроса и ответа, облегчают плавный обмен информацией и метаданными между клиентом и сервером. Каждый заголовок состоит из пары ключ-значение, структурированной как Name: Value.

Например:

Content-Type: text/html; charset=UTF-8

Здесь имя заголовка — «Content-Type», а его значение указывает, что содержимое представляет собой HTML-документ с кодировкой UTF-8.

Типы HTTP-заголовков

Заголовки HTTP можно разделить на следующие типы:

  • Общие заголовки. Они присутствуют как в запросах, так и в ответах, но не связаны напрямую с данными тела. Примеры: Cache-Control, Connection и Date.
  • Заголовки запросов. Эти заголовки предоставляют дополнительную информацию о ресурсе, который необходимо получить, или о самом клиенте. Некоторые общие заголовки запроса: User-Agent (предоставляет информацию о пользовательском агенте, выполняющем запрос) и Accept (указывает, какие типы мультимедиа может обрабатывать клиент).
  • Заголовки ответов.Как следует из названия, заголовки ответов предоставляют дополнительную информацию об ответе сервера. Примерами являются такие заголовки, как Location (используется в перенаправлениях) и Server (описывает программное обеспечение, используемое исходным сервером).
  • Заголовки объектов. Они используются для предоставления информации о теле ресурса, например его длины или типа. Content-Type (указывающее тип ресурса) и Content-Length (указывающее размер ответа) являются классическими примерами.
  • Пользовательские заголовки. Помимо стандартных заголовков, разработчики могут определять свои собственные заголовки. Пользовательские заголовки часто начинаются с «X-» (хотя это соглашение устарело), ​​например, X-Frame-Options.

Важность HTTP-заголовков

Заголовки HTTP — это не просто пассивные носители метаданных; они активно влияют на поведение веб-приложений:

  • Согласование. Заголовки можно использовать для согласования типа предоставляемого контента (HTML, JSON, XML) или используемой кодировки.
  • Безопасность. Некоторые заголовки играют решающую роль в повышении веб-безопасности. Заголовок Strict-Transport-Security гарантирует, что доступ к веб-сайту возможен только через HTTPS, а заголовок Content-Security-Policy помогает предотвратить атаки с использованием межсайтовых сценариев.
  • Производительность. Заголовки управляют поведением кэширования и могут существенно влиять на производительность веб-приложения. Правильное использование заголовков кэширования может снизить нагрузку на сервер и сократить время загрузки страницы.

Аннотация @RequestHeader

В Spring MVC аннотации упрощают процесс разработки веб-приложений, гарантируя, что разработчики могут сосредоточиться на базовой логике, а не на шаблоне. Одна из таких мощных аннотаций — @RequestHeader. Он привязывает значение HTTP-заголовка к параметру метода, обеспечивая плавную интеграцию данных заголовка в методы вашего контроллера.

Понимание @RequestHeader

По своей сути аннотация @RequestHeader — это способ получить информацию, передаваемую клиентом в HTTP-заголовке. Без этой функции разработчикам пришлось бы выполнять громоздкий процесс анализа необработанного HTTP-запроса для извлечения значений заголовка вручную.

Пример:

@RequestMapping("/fetchContentType")
public String getContentType(@RequestHeader("Content-Type") String contentType) {
    return "Content Type is: " + contentType;
}

В приведенном выше фрагменте кода метод getContentType автоматически получает значение заголовка Content-Type из входящего HTTP-запроса благодаря @RequestHeader.

Обработка нескольких заголовков

Могут быть сценарии, когда вам нужно получить более одного заголовка. Вместо объявления нескольких параметров вы можете использовать Map:

@RequestMapping("/fetchHeaders")
public String getHeaders(@RequestHeader Map<String, String> headers) {
    return "Headers are: " + headers.toString();
}

Этот подход извлекает все заголовки из запроса и помещает их на карту.

Обработка дополнительных заголовков

Не все заголовки гарантированно присутствуют в каждом запросе. В таких случаях пригодится атрибут аннотации required:

@RequestMapping("/optionalHeader")
public String getOptionalHeader(@RequestHeader(value = "optional-header", required = false) String headerValue) {
    return headerValue != null ? "Header value is: " + headerValue : "Header not present.";
}

Здесь, если «optional-header» не является частью запроса, ошибка не будет выдана, и метод корректно ее обработает.

Значения по умолчанию

Для заголовков, которые могут быть необязательными, Spring предоставляет возможность установить значение по умолчанию. Если заголовок в запросе отсутствует, используется значение по умолчанию:

@RequestMapping("/defaultHeaderValue")
public String getDefaultHeader(@RequestHeader(value = "X-Custom-Header", defaultValue = "default-value") String headerValue) {
    return "Header value is: " + headerValue;
}

В приведенном выше примере, если «X-Custom-Header» не найден во входящем запросе, для headerValue будет установлено значение «default-value».

Преобразование типа

Возможность преобразования типов Spring MVC может автоматически преобразовывать значения заголовков в нужные типы данных:

@RequestMapping("/fetchDate")
public String getDate(@RequestHeader("Date") Date date) {
    return "Date header as a Date object: " + date.toString();
}

Если заголовок «Дата» отправлен в допустимом формате даты, Spring автоматически преобразует его в объект Date.

Аннотация @ResponseHeader

Хотя аннотация @RequestHeader предназначена исключительно для чтения входящих заголовков, когда дело доходит до установки заголовков в ответе HTTP, Spring MVC использует немного другой подход. Вопреки тому, что следует из названия, в Spring MVC нет прямой аннотации @ResponseHeader. Однако разработчики могут использовать такие инструменты, как HttpServletResponse и более элегантный ResponseEntity, для достижения желаемого результата.

Использование HttpServletResponse

Традиционный подход Servlet API к настройке заголовков ответов осуществляется через объект HttpServletResponse. Этот объект предоставляет методы для управления и изменения ответа, отправляемого клиенту.

@RequestMapping("/setHeaderUsingServlet")
public void setHeaderUsingServlet(HttpServletResponse response) {
    response.setHeader("X-Custom-Header", "CustomHeaderValue");
    response.getWriter().write("Header has been set!");
}

В этом примере для ответа устанавливается «X-Custom-Header», а затем в тело ответа записывается простое сообщение.

Использование ResponseEntity

В то время как HttpServletResponse предлагает простой способ установки заголовков, класс ResponseEntity предлагает более сложный и объектно-ориентированный подход, особенно полезный в приложениях RESTful.

@RequestMapping("/setHeaderUsingEntity")
public ResponseEntity<String> setHeaderWithEntity() {
    HttpHeaders headers = new HttpHeaders();
    headers.set("X-Custom-Header", "CustomHeaderValue");
    
    return new ResponseEntity<>("Body with custom header", headers, HttpStatus.OK);
}

ResponseEntity обеспечивает способ представления всего HTTP-ответа, включая статус, заголовки и тело. В приведенном выше примере мы создаем новый ответ с настраиваемым заголовком и возвращаем его.

Установка нескольких заголовков

Если вы хотите установить в ответе несколько заголовков, HttpServletResponse и ResponseEntity сделают это довольно просто:

Использование HttpServletResponse:

@RequestMapping("/setMultipleHeadersServlet")
public void setMultipleHeadersServlet(HttpServletResponse response) {
    response.setHeader("X-Custom-Header-1", "Value1");
    response.setHeader("X-Custom-Header-2", "Value2");
    response.getWriter().write("Multiple headers have been set!");
}

Использование ResponseEntity:

@RequestMapping("/setMultipleHeadersEntity")
public ResponseEntity<String> setMultipleHeadersEntity() {
    HttpHeaders headers = new HttpHeaders();
    headers.set("X-Custom-Header-1", "Value1");
    headers.set("X-Custom-Header-2", "Value2");
    
    return new ResponseEntity<>("Body with multiple headers", headers, HttpStatus.OK);
}

Варианты использования заголовков ответов

Установка заголовков ответа — это не просто стилистический выбор, но может существенно повлиять на поведение приложения:

  • Управление кэшированием. Заголовки, такие как Cache-Control, могут указывать клиенту, как кэшировать ответ, что потенциально повышает производительность приложения.
  • Безопасность. Заголовки, такие как X-Frame-Options или Strict-Transport-Security, могут обеспечивать соблюдение политик безопасности, делая веб-приложения более безопасными.
  • Расположение контента. Если вы обеспечиваете загрузку файлов, заголовок Content-Disposition можно использовать, чтобы предложить имя файла и указать, должен ли браузер отображать файл или рассматривать его как вложение.

Практическое применение HTTP-заголовков

Заголовки HTTP, хотя иногда их упускают из виду, играют ключевую роль в формировании поведения и характеристик веб-приложений. При эффективном использовании они могут превратить базовое веб-приложение в высокооптимизированную, безопасную и удобную для пользователя платформу. Вот несколько практических приложений и примеры использования HTTP-заголовков в Spring MVC.

Согласование содержания

Согласование контента позволяет серверу обслуживать различные версии документа (или, в более общем плане, представления ресурса) на основе заголовков запроса от клиента. Например, заголовок Accept позволяет клиенту указать формат ответа, который он желает получить.

Весной MVC:

@RequestMapping(value = "/negotiateContent", produces = {"application/json", "application/xml"})
public ResponseEntity<User> getUser() {
    User user = new User("John", 30);
    return new ResponseEntity<>(user, HttpStatus.OK);
}

Вышеупомянутый метод контроллера может обслуживать объект User в формате JSON или XML, в зависимости от значения заголовка Accept во входящем запросе.

Повышение безопасности

Заголовки HTTP могут значительно повысить уровень безопасности вашего приложения:

  • Параметры X-Frame:Этот заголовок может предотвратить атаки кликджекинга, контролируя, разрешено ли браузеру отображать страницу в форматах <frame>, <iframe>, <embed> или <object>.
response.setHeader("X-Frame-Options", "DENY");
  • Strict-Transport-Security: Этот заголовок гарантирует, что весь обмен данными с пользовательским агентом будет осуществляться через HTTPS. Его можно установить как:
response.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");

Управление кэшированием на стороне клиента

Заголовки HTTP управляют тем, как (или даже реализуется ли) кэширование на стороне клиента, влияя на производительность:

  • Cache-Control: управляет всеми механизмами кэширования, как использовать ответ.
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
  • Срок действия:. Указывает дату/время, после которого ответ считается устаревшим.
response.setHeader("Expires", "Tue, 03 Jul 2001 06:00:00 GMT");

Включение CORS

Заголовки совместного использования ресурсов между источниками (CORS) позволяют указать, каким источникам разрешено читать ресурс на веб-странице.

Используя аннотацию Spring @CrossOrigin:

@CrossOrigin(origins = "https://allowed-origin.com")
@RequestMapping("/corsEnabledEndpoint")
public ResponseEntity<String> handleCORS() {
    return new ResponseEntity<>("CORS-enabled response", HttpStatus.OK);
}

Вышеуказанный метод контроллера будет принимать запросы только от https://allowed-origin.com.

Обработка загрузок файлов

Заголовки HTTP могут облегчить загрузку файлов:

  • Content-Disposition: указывает, должно ли содержимое отображаться внутри или рассматриваться как вложение.
response.setHeader("Content-Disposition", "attachment; filename=\"filename.jpg\"");

Это предложит пользователю загрузить файл с указанным именем.

Заключение

Заголовки HTTP играют ключевую роль в повышении функциональности и эффективности веб-приложений. Spring MVC предоставляет простые способы работы с этими заголовками, используя механизмы аннотаций @RequestHeader и ответов, такие как HttpServletResponse и ResponseEntity. Независимо от того, занимаетесь ли вы согласованием контента, языковыми настройками или создаете собственное поведение на основе заголовков, Spring MVC поможет вам.

  1. Документация Spring MVC
  2. HTTP-заголовки MDN
  3. @CrossOrigin весной