FreeMarker 模板将表达式中的所有内容评估为 null 或缺失

问题描述 投票:0回答:1

感谢每一个帮助。我找不到我犯的错误。模板在最初的“${error}”时不会被 FreeMarker 进一步处理。我阅读了文档并与其他人进行了比较。但它以这个 stackstrace 结束,我真的不知道我的具体错误是什么:

我收到了这个堆栈跟踪,但“错误”在 ModelMap 中填充为字符串对象:

FreeMarker template error (DEBUG mode; use RETHROW in production!): The following has evaluated to null or missing: ==> error [in template "freemarker/api-error.ftl" at line 128, column 62] ---- Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)?? ---- ---- FTL stack trace ("~" means nesting-related): - Failed at: ${error} [in template "freemarker/api-error.ftl" at line 128, column 60] ---- Java stack trace (for programmers): ---- freemarker.core.InvalidReferenceException: [... Exception message was already printed; see it above ...] at freemarker.core.InvalidReferenceException.getInstance(InvalidReferenceException.java:134) at freemarker.core.EvalUtil.coerceModelToTextualCommon(EvalUtil.java:481) at freemarker.core.EvalUtil.coerceModelToStringOrMarkup(EvalUtil.java:401) at freemarker.core.EvalUtil.coerceModelToStringOrMarkup(EvalUtil.java:370) at freemarker.core.DollarVariable.calculateInterpolatedStringOrMarkup(DollarVariable.java:104) at freemarker.core.DollarVariable.accept(DollarVariable.java:63) at freemarker.core.Environment.visit(Environment.java:335) at freemarker.core.Environment.visit(Environment.java:341) at freemarker.core.Environment.process(Environment.java:314) at freemarker.template.Template.process(Template.java:383) at org.springframework.web.servlet.view.freemarker.FreeMarkerView.processTemplate(FreeMarkerView.java:391) at org.springframework.web.servlet.view.freemarker.FreeMarkerView.doRender(FreeMarkerView.java:304) at org.springframework.web.servlet.view.freemarker.FreeMarkerView.renderMergedTemplateModel(FreeMarkerView.java:255) at org.springframework.web.servlet.view.AbstractTemplateView.renderMergedOutputModel(AbstractTemplateView.java:181) at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:316) at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1406) at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1150) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:965) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) at javax.servlet.http.HttpServlet.service(HttpServlet.java:529) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) at javax.servlet.http.HttpServlet.service(HttpServlet.java:623) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:209) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:352) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:117) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:126) at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:120) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:131) at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:85) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:164) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at edu.remad.springconfig.security.filters.TenantFilter.doFilter(TenantFilter.java:27) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:151) at org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:129) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:227) at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:221) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:118) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:361) at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:225) at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:190) at org.springframework.security.web.debug.DebugFilter.invokeWithWrappedRequest(DebugFilter.java:90) at org.springframework.security.web.debug.DebugFilter.doFilter(DebugFilter.java:78) at org.springframework.security.web.debug.DebugFilter.doFilter(DebugFilter.java:67) at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354) at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:168) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:481) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:130) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:670) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:390) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:928) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1794) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.base/java.lang.Thread.run(Thread.java:833)

控制器:

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;

import edu.remad.springconfig.globalexceptions.Error;
import edu.remad.springconfig.globalexceptions.ErrorInfo;
import edu.remad.springconfig.globalexceptions.ErrorUtils;
import edu.remad.springconfig.globalexceptions.HttpStatus401Exception;
import edu.remad.springconfig.globalexceptions.HttpStatus403Exception;
import edu.remad.springconfig.globalexceptions.HttpStatus404Exception;
import edu.remad.springconfig.globalexceptions.HttpStatus500Exception;

@Controller
@RequestMapping(HttpStatusExceptionController.REQUEST_MAPPING_EXCEPTIONS)
public class HttpStatusExceptionController {

    private static final String GET_MAPPING_401 = "/401";
    private static final String GET_MAPPING_403 = "/403";
    private static final String GET_MAPPING_404 = "/404";
    private static final String GET_MAPPING_500 = "/500";
    private static final String GET_API_ERROR = "/api-error";
    public static final String REQUEST_MAPPING_EXCEPTIONS = "/exceptions";
    
    @GetMapping(value = GET_API_ERROR)
    public String getApiError(@ModelAttribute("model") ModelMap model) {
        String url = REQUEST_MAPPING_EXCEPTIONS + GET_MAPPING_500;
        ErrorInfo errorInfo = new ErrorInfo(url, Error.HTTP_500_ERROR, "Test HTTP Status 500", "HTTP 500 thrown");
        HttpStatus500Exception ex = new HttpStatus500Exception("Upps here happened a Http Status 500 error", new Throwable(), errorInfo);
        model.addAllAttributes(ErrorUtils.fillExceptionModelMap(ex));
        String template = ex.getTemplate();
        
        return template;
    }
}

api-error.ftl:

<html>
    <head>
        <title>
        </title>
        <style>
        .modal-container {
        margin: 250 auto;
        z-index: 1000;
        background-color: transparent;
        display: flex;
        opacity: 1;
        pointer-events: none;
        transition: opacity 250ms ease;
        }
        
        .modal-section {
        margin: auto;
        width: 90%;
        max-width: 40rem;
        background: linear-gradient(135deg, hsla(212, 98%, 73%, 0.96),hsla(199, 74%, 73%, 0.74));
        border-radius: 1.5rem;
        box-shadow: 0 1rem 2rem #000a;
        }
        
        .modal-header {
        display: flex;
        justify-content: space-between;
        background-color: hsla(212, 73%, 50%, 0.74);
        border-radius: 1.5rem 1.5rem 0 0;
        border: 4px solid hsla(212, 85%, 72%, 0.84);
        border-bottom: none;
        }
        
.modal-title {
  margin: 0;
  color: black;
}

.modal-close {
  color: transparent;
  display: block;
  overflow: hidden;
  background-image:
    linear-gradient(
      to top right,
      transparent 48%,
      black 48%,
      black 52%,
      transparent 52%
    ),
    linear-gradient(
      to top left,
      transparent 48%,
      black 48%,
      black 52%,
      transparent 52%
    );
}

.modal-close:hover,
.modal-close:focus {
  background-image:
    linear-gradient(
      to top right,
      transparent 46%,
      black 46%,
      black 54%,
      transparent 54%
    ),
    linear-gradient(
      to top left,
      transparent 46%,
      black 46%,
      black 54%,
      transparent 54%
    );
}

.modal-content {
  color: black;
  border-radius: 0 0 1.5rem 1.5rem;
  border: 4px solid #f7baf744;
  border-top: none;
}
        
        .modal-header,
        .modal-content {
        padding: 1.5rem;
        }
        
        .modal-container:target {
        opacity: 0;
        pointer-events: all;
        }
        
.button { 
  text-decoration: none;
  display: inline-block;
  padding: 0.5rem 1.25rem;
  color: white;
  background: #4c90b2;
  border: 1px solid #2d566b;
  border-radius: 0.5rem;
  background-image:
    linear-gradient(
      to bottom,
      hsla(200, 40%, 80%, 0.4),
      transparent,
      hsla(200, 40%, 20%, 0.6)
    );
  transition: background-color 0.5s ease;
}

.button::hover,
.button::focus {
  background-color: transparent;
}
        
        body {
  background-color: rgba(0,0,0,0.65);
        }
        </style>
    </head>
    <body>
        <div class="modal-container">
            <section class="modal-section">
                <header class="modal-header">
                   <h2 class="modal-title">${error}</h2>
                   <a href="#" class="modal-close">close</a>
                </header>
                <div class="modal-content">
            <br/>
            <p>${code}</p>
            <p>url: ${url}</p>
            <br />
            <p>${message}</p>
            
            <p><a class="button" href="mailto:${email}">${email}</a></p>
                </div>
            </section>
        </div>
    </body>
</html>

错误实用程序:

import org.springframework.ui.ModelMap;

public final class ErrorUtils {

    private ErrorUtils() {
    }

    public static ModelMap fillExceptionModelMap(HttpStatusException exception) {
        ModelMap modelMap = new ModelMap();
        modelMap.addAttribute("code", exception.getCode());
        modelMap.addAttribute("email", exception.getEMail());
        modelMap.addAttribute("localizedMessage", exception.getLocalizedMessage());
        modelMap.addAttribute("message", exception.getMessage());
        modelMap.addAttribute("url", exception.getUrl());
        modelMap.addAttribute("error", exception.getError().getError());
        modelMap.addAttribute("httpStatus", exception.getError().getHttpStatus().name());

        return modelMap;
    }
}

HttpStatusException 是一个接口:

import org.springframework.http.HttpStatus;

public interface HttpStatusException {

    String getUrl();

    Error getError();

    String getAdditionalText();

    HttpStatus getHttpStatus();

    String getCode();

    String getTemplate();

    String getEMail();

    String getMessage();

    String getLocalizedMessage();

    ErrorInfo getErrorInfo();
    
    Throwable getCause();
    
    String getNestedErrorMessage();
}

具体实现是这样的

public class HttpStatus500Exception extends HttpStatusCodeException implements HttpStatusException {
    private final String message;
    private final ErrorInfo errorInfo;
    private final Throwable cause;

    public HttpStatus500Exception(String message, Throwable cause, ErrorInfo info) {
        super(info.getError().getHttpStatus());
        this.message = message;
        this.cause = cause;
        errorInfo = info;
    }

    private static final long serialVersionUID = 1L;

    @Override
    public String getUrl() {
        return errorInfo.getUrl();
    }

    @Override
    public Error getError() {
        return errorInfo.getError();
    }

    @Override
    public String getAdditionalText() {
        return errorInfo.getAdditionalText();
    }

    @Override
    public HttpStatus getHttpStatus() {
        return errorInfo.getError().getHttpStatus();
    }

    @Override
    public String getCode() {
        return errorInfo.getError().getCode();
    }

    @Override
    public String getTemplate() {
        return errorInfo.getError().getTemplate();
    }

    @Override
    public String getEMail() {
        return errorInfo.getError().geteMail();
    }

    @Override
    public ErrorInfo getErrorInfo() {
        return errorInfo;
    }

    @Override
    public Throwable getCause() {
        return cause;
    }

    @Override
    public String getNestedErrorMessage() {
        return message;
    }
    
    @Override
    public String getMessage() {
        return errorInfo.getError().getMessage();
    }
}

错误信息:

public class ErrorInfo {
    
    private final String url;
    private final Error error;
    private final String additionalText;
    private final String localizedMessage;
    
    public ErrorInfo(String url, Error error, String additionalText, String localizedMessage) {
        this.url = url;
        this.error = error;
        this.additionalText = additionalText;
        this.localizedMessage = localizedMessage;
    }
    
    public String getUrl() {
        return url;
    }

    public Error getError() {
        return error;
    }

    public String getAdditionalText() {
        return additionalText;
    }

    public String getLocalizedMessage() {
        return localizedMessage;
    }
}

错误:

import org.springframework.http.HttpStatus;

public enum Error {
    HTTP_500_ERROR(HttpStatus.INTERNAL_SERVER_ERROR,"Internal Server Error","ERR_500","api-error", "[email protected]", "Please contact by errors our support."),
    HTTP_404_ERROR(HttpStatus.NOT_FOUND, "Not found", "ERR_404","api-error", "[email protected]", "Please contact by errors our support."),
    HTTP_403_ERROR(HttpStatus.FORBIDDEN,"Forbidden","ERR_403","api-error","[email protected]","Please contact by errors our support."),
    HTTP_401_ERROR(HttpStatus.UNAUTHORIZED,"Unauthorized","ERR_401","api-error","[email protected]","Please contact by errors our support.");
    
    private final HttpStatus httpStatus;
    private final String error;
    private final String code;
    private final String template;
    private final String eMail;
    private final String message;
    
    Error(HttpStatus internalServerError, String error, String errorCode, String templateFile, String email,
            String supportMessage) {
        httpStatus = internalServerError;
        this.error = error;
        code = errorCode;
        template = templateFile;
        eMail = email;
        message = supportMessage;
    }

    public HttpStatus getHttpStatus() {
        return httpStatus;
    }

    public String getError() {
        return error;
    }

    public String getCode() {
        return code;
    }

    public String getTemplate() {
        return template;
    }

    public String geteMail() {
        return eMail;
    }

    public String getMessage() {
        return message;
    }
}

自由标记配置:

@Configuration
public class FreeMarkerConfig {

    @Bean 
    public FreeMarkerViewResolver freeMarkerViewResolver() { 
        FreeMarkerViewResolver resolver = new FreeMarkerViewResolver(); 
        resolver.setCache(true); 
        resolver.setPrefix("/freemarker/"); 
        resolver.setSuffix(".ftl");
        resolver.setOrder(0);
        
        return resolver; 
    }

    @Bean
    public FreeMarkerConfigurer freeMarkerConfigurer() {
        Properties properties = new Properties();
        properties.put("auto_import", "/spring.ftl as spring");

        FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
        configurer.setTemplateLoaderPath("/WEB-INF/templates");
        configurer.setDefaultEncoding(StandardCharsets.UTF_8.displayName());
        configurer.setFreemarkerSettings(properties);

        return configurer;
    }
}

我尝试了有关堆栈溢出的所有类似问题来解决异常。我阅读了 FreeMarker Apache Docs 并尝试了它们,但没有任何变化。经过 3 天的调查后,我希望有人能够支持修复该异常。 我所期望的错误内容是在标记中解析的。

spring-mvc freemarker spring5
1个回答
0
投票

对我来说,我努力将类型 ModelMap 更改为 Model。为什么我不明白。

© www.soinside.com 2019 - 2024. All rights reserved.