|
21 | 21 | import java.io.OutputStreamWriter;
|
22 | 22 | import java.io.Writer;
|
23 | 23 | import java.nio.charset.Charset;
|
| 24 | +import java.util.HashMap; |
24 | 25 | import java.util.Locale;
|
25 | 26 | import java.util.Map;
|
26 | 27 | import java.util.Optional;
|
|
47 | 48 | import org.springframework.util.Assert;
|
48 | 49 | import org.springframework.util.MimeType;
|
49 | 50 | import org.springframework.web.reactive.result.view.AbstractUrlBasedView;
|
| 51 | +import org.springframework.web.reactive.result.view.RequestContext; |
50 | 52 | import org.springframework.web.server.ServerWebExchange;
|
51 | 53 |
|
52 | 54 | /**
|
|
64 | 66 | * <p>Note: Spring's FreeMarker support requires FreeMarker 2.3 or higher.
|
65 | 67 | *
|
66 | 68 | * @author Rossen Stoyanchev
|
| 69 | + * @author Sam Brannen |
67 | 70 | * @since 5.0
|
68 | 71 | */
|
69 | 72 | public class FreeMarkerView extends AbstractUrlBasedView {
|
70 | 73 |
|
| 74 | + /** |
| 75 | + * Attribute name of the {@link RequestContext} instance in the template model, |
| 76 | + * available to Spring's macros — for example, for creating |
| 77 | + * {@link org.springframework.web.reactive.result.view.BindStatus BindStatus} |
| 78 | + * objects. |
| 79 | + * @since 5.2 |
| 80 | + * @see #setExposeSpringMacroHelpers(boolean) |
| 81 | + */ |
| 82 | + public static final String SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE = "springMacroRequestContext"; |
| 83 | + |
| 84 | + |
71 | 85 | @Nullable
|
72 | 86 | private Configuration configuration;
|
73 | 87 |
|
74 | 88 | @Nullable
|
75 | 89 | private String encoding;
|
76 | 90 |
|
| 91 | + private boolean exposeSpringMacroHelpers = true; |
| 92 | + |
77 | 93 |
|
78 | 94 | /**
|
79 | 95 | * Set the FreeMarker Configuration to be used by this view.
|
@@ -124,6 +140,19 @@ protected String getEncoding() {
|
124 | 140 | return this.encoding;
|
125 | 141 | }
|
126 | 142 |
|
| 143 | + /** |
| 144 | + * Set whether to expose a {@link RequestContext} for use by Spring's macro |
| 145 | + * library, under the name {@value #SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE}. |
| 146 | + * <p>Default is {@code true}. |
| 147 | + * <p>Needed for Spring's FreeMarker default macros. Note that this is |
| 148 | + * <i>not</i> required for templates that use HTML forms <i>unless</i> you |
| 149 | + * wish to take advantage of the Spring helper macros. |
| 150 | + * @see #SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE |
| 151 | + */ |
| 152 | + public void setExposeSpringMacroHelpers(boolean exposeSpringMacroHelpers) { |
| 153 | + this.exposeSpringMacroHelpers = exposeSpringMacroHelpers; |
| 154 | + } |
| 155 | + |
127 | 156 |
|
128 | 157 | @Override
|
129 | 158 | public void afterPropertiesSet() throws Exception {
|
@@ -180,6 +209,34 @@ public boolean checkResourceExists(Locale locale) throws Exception {
|
180 | 209 | }
|
181 | 210 | }
|
182 | 211 |
|
| 212 | + /** |
| 213 | + * Prepare the model to use for rendering by potentially exposing a |
| 214 | + * {@link RequestContext} for use in Spring FreeMarker macros and then |
| 215 | + * delegating to the inherited implementation of this method. |
| 216 | + * @since 5.2 |
| 217 | + * @see #setExposeSpringMacroHelpers(boolean) |
| 218 | + * @see org.springframework.web.reactive.result.view.AbstractView#getModelAttributes(Map, ServerWebExchange) |
| 219 | + */ |
| 220 | + @Override |
| 221 | + protected Mono<Map<String, Object>> getModelAttributes(Map<String, ?> model, |
| 222 | + ServerWebExchange exchange) { |
| 223 | + |
| 224 | + if (this.exposeSpringMacroHelpers) { |
| 225 | + if (model.containsKey(SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE)) { |
| 226 | + throw new IllegalStateException( |
| 227 | + "Cannot expose bind macro helper '" + SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE + |
| 228 | + "' because of an existing model object of the same name"); |
| 229 | + } |
| 230 | + // Make a defensive copy of the model. |
| 231 | + Map<String, Object> attributes = new HashMap<>(model); |
| 232 | + // Expose RequestContext instance for Spring macros. |
| 233 | + attributes.put(SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE, new RequestContext( |
| 234 | + exchange, attributes, obtainApplicationContext(), getRequestDataValueProcessor())); |
| 235 | + return super.getModelAttributes(attributes, exchange); |
| 236 | + } |
| 237 | + return super.getModelAttributes(model, exchange); |
| 238 | + } |
| 239 | + |
183 | 240 | @Override
|
184 | 241 | protected Mono<Void> renderInternal(Map<String, Object> renderAttributes,
|
185 | 242 | @Nullable MediaType contentType, ServerWebExchange exchange) {
|
|
0 commit comments