У меня есть некий контроллер вида:
package controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/login")
public class Login {
@RequestMapping(value = "auth")
public String auth() {
return "login/auth";
}
}
Где login/auth
это путь до файла auth.ftl
. В качестве шаблонизатора использую FreeMaker
.
Что я хочу: у меня есть некий шаблон content.ftl
с содержанием:
<!DOCTYPE html>
<head>...</head>
<body>${content}</body>
В который должны передаваться данные из метода auth()
и уже после отображаться.
В целом я понимаю логику: наследуемся от какого-то класса шаблонизатора, сначала рендерим auth.ftl
, потом кидаем данные в content.ftl
, опять рендерним и отдаем данные уже в браузер. Но где взять пример? WebMvcConfigurerAdapter
?
Вариант 1: вклиниваемся в рендеринг
Если мы заставим Spring резолвить имя view в подконтрольный нам объект, то сможем рендерить его как захотим. Чтобы это сделать, подсунем Spring'у view resolver, который будет выдавать наши view:
// Наш view resolver, проксирующий вызовы к настоящему
@Bean
public ViewResolver viewResolver(FreeMarkerViewResolver freeMarkerViewResolver) {
FreeMarkerWrappedViewResolver viewResolver =
new FreeMarkerWrappedViewResolver(freeMarkerViewResolver);
// Так как в контексте будет и наш, и настоящий view resolver,
// нужно заставить Spring применять наш раньше настоящего.
// Этого можно добиться, установив order в значение поменьше.
viewResolver.setOrder(0);
return viewResolver;
}
// Настоящий view resolver. Его нужно создавать в контексте Spring'а, иначе он не работает
@Bean
public FreeMarkerViewResolver freeMarkerViewResolver() {
FreeMarkerViewResolver freeMarkerViewResolver = new FreeMarkerViewResolver();
freeMarkerViewResolver.setPrefix("");
freeMarkerViewResolver.setSuffix(".ftl");
return freeMarkerViewResolver;
}
Кастомный view resolver будет возвращать кастомные view. Используя настоящий FreeMarkerViewResolver
, мы резолвим базовый view и view, который хотим в него вставить:
class FreeMarkerWrappedViewResolver extends FreeMarkerViewResolver {
private FreeMarkerViewResolver freeMarkerViewResolver;
public FreeMarkerWrappedViewResolver(FreeMarkerViewResolver freeMarkerViewResolver) {
this.freeMarkerViewResolver = freeMarkerViewResolver;
}
@Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
View content = freeMarkerViewResolver.resolveViewName(viewName, locale);
View base = freeMarkerViewResolver.resolveViewName("base", locale);
return new WrappedView(content, base);
}
}
В кастомном view мы сначала рендерим контент в MockHttpServletResponse
, потом этот контент заворачиваем в модель, которую скармливаем базовому view и рендерим в настоящий HttpServletResponse
:
class WrappedView implements View {
private View nestedView;
private View wrappingView;
public WrappedView(View nestedView, View wrappingView) {
this.nestedView = nestedView;
this.wrappingView = wrappingView;
}
@Override
public String getContentType() {
return nestedView.getContentType();
}
@Override
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
MockHttpServletResponse mockResponse = new MockHttpServletResponse();
nestedView.render(model, request, mockResponse);
HashMap<String, Object> bodyModel = new HashMap<>();
bodyModel.put("content", mockResponse.getContentAsString());
wrappingView.render(bodyModel, request, response);
}
}
Готово! Теперь все view будут заворачиваться в наш темплейт.
Вариант 2: используем встроенные механизмы FreeMarker
В FreeMarker есть механизм наследования темплейтов. В base.ftl
задаём структуру страницы с плейсхолдерами:
<#macro content>
<!-- Этот контент нам предоставит наследник -->
</#macro>
<#macro page>
<html>
<body>
<p>Content is</p>
<p><@content/></p>
</body>
</html>
</#macro>
А в наследнике импортируем базовый темплейт и предоставляем значения плейсхолдеров:
<#include "base.ftl"/>
<#macro content>
<b>${message}</b>
</#macro>
<@page/> <!-- Не забываем включить непосредственно контент родителя -->
Второй вариант предоставляет больше гибкости: плейсхолдеров может быть больше одного (например, каждый наследник может независимо переопределить хедер, контент и футер). В первом варианте добиться такого будет проблематично. С другой стороны, первый вариант позволяет избежать постоянного копипаста импорта родительского темплейта. Что лучше подходит под ваши задачи - решать вам.
здравствуйте, может кто поделиться кодом многопоточного бинарного поиска числа в массиве, при помощи C++ MPI и/или Java ThreadsСпасибо
Встретил это в книге по Java Яков Файн "Программирование на Java
Мне нужно скачать html-документ, используя Jsoup, но при этом так, чтобы символы, которые написаны кодом остались как код (например, ")