发生场景:
导致这个异常只有一种原因,数据模型跟request中的attributes存在同名属性,大致在几种场景下出现: 1. Model和Request对象中有同名的attribute - 比如你在Interceptor中request.setAttribute("key1","niubi"),然后又在Controller中调用model.addAttribute("key1","wocao") 2. PathVariable 和 Model/Request中的attribute同名
解决方法
根据模板引擎添加对应配置即可。
不过本人还是建议能改个属性名就改名吧,不到迫不得已,尽量少一些个性化的配置,日后代码维护或交接的时候会轻松许多。
freemarker
# 指定HttpServletRequest的属性是否可以覆盖controller的model的同名项
spring.freemarker.allow-request-override=true
velocity
# 指定HttpServletRequest的属性是否可以覆盖controller的model的同名项
spring.velocity.allow-request-override=true
groovy
# 指定HttpServletRequest的属性是否可以覆盖controller的model的同名项
spring.groovy.template.allow-request-override=true
追根溯源,揭开神秘面纱
顺着堆栈debug
Java代码:
@GetMapping("/www.tongtian.icu/{test}")
public String tongtian_icu(@PathVariable String test,HttpServletRequest request) {
request.setAttribute("test","123456");
return "TestTemplate";
}
堆栈信息:
javax.servlet.ServletException: Cannot expose request attribute 'test' because of an existing model object of the same name
at org.springframework.web.servlet.view.AbstractTemplateView.renderMergedOutputModel(AbstractTemplateView.java:124) ~[spring-webmvc-5.3.4.jar:5.3.4]
at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:316) ~[spring-webmvc-5.3.4.jar:5.3.4]
at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1393) ~[spring-webmvc-5.3.4.jar:5.3.4]
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1138) ~[spring-webmvc-5.3.4.jar:5.3.4]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1077) ~[spring-webmvc-5.3.4.jar:5.3.4]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:962) ~[spring-webmvc-5.3.4.jar:5.3.4]
# 到DispatcherServlet就可以了,后续略...
renderMergedOutputModel 渲染合并后的输出模型
这个方法作用是判断是否公开request中的attributes,如果exposeRequestAttributes为true,会将request中的attributes复制到model中。
第十行可以看到,在exposeRequestAttributes为true,且allowRequestOverride不为true的情况下,会抛出异常,这也就是场景1中出现这种情况的原因,所以我们解决方法中的配置就允许覆盖。
@Override
protected final void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
if (this.exposeRequestAttributes) {
Map<String, Object> exposed = null;
for (Enumeration<String> en = request.getAttributeNames(); en.hasMoreElements();) {
String attribute = en.nextElement();
if (model.containsKey(attribute) && !this.allowRequestOverride) {
throw new ServletException("Cannot expose request attribute '" + attribute +
"' because of an existing model object of the same name");
}
Object attributeValue = request.getAttribute(attribute);
if (logger.isDebugEnabled()) {
exposed = exposed != null ? exposed : new LinkedHashMap<>();
exposed.put(attribute, attributeValue);
}
model.put(attribute, attributeValue);
}
if (logger.isTraceEnabled() && exposed != null) {
logger.trace("Exposed request attributes to model: " + exposed);
}
}
render
注释的意思是,将静态属性与requestContext属性合并(17行),再委托renderMergedOutputModel进行实际渲染。
/**
* Prepares the view given the specified model, merging it with static
* attributes and a RequestContext attribute, if necessary.
* Delegates to renderMergedOutputModel for the actual rendering.
* @see #renderMergedOutputModel
*/
@Override
public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
logger.debug("View " + formatViewName() +
", model " + (model != null ? model : Collections.emptyMap()) +
(this.staticAttributes.isEmpty() ? "" : ", static attributes " + this.staticAttributes));
}
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
prepareResponse(request, response);
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}
createMergedOutputModel
19行可以看到,我们的pathVariable全部都被加入到了mergedModel中,所以再通过request.setAttribude方法设置同名属性,就会在renderMergedOutputModel实际渲染的时候抛出异常。这也就是场景2中抛出异常的原因,针对场景2,我们可以单独设置exposePathVariables为false解决报错。
/**
* Creates a combined output Map (never {@code null}) that includes dynamic values and static attributes.
* Dynamic values take precedence over static attributes.
*/
protected Map<String, Object> createMergedOutputModel(@Nullable Map<String, ?> model,
HttpServletRequest request, HttpServletResponse response) {
@SuppressWarnings("unchecked")
Map<String, Object> pathVars = (this.exposePathVariables ?
(Map<String, Object>) request.getAttribute(View.PATH_VARIABLES) : null);
// Consolidate static and dynamic model attributes.
int size = this.staticAttributes.size();
size += (model != null ? model.size() : 0);
size += (pathVars != null ? pathVars.size() : 0);
Map<String, Object> mergedModel = CollectionUtils.newLinkedHashMap(size);
mergedModel.putAll(this.staticAttributes);
if (pathVars != null) {
mergedModel.putAll(pathVars);
}
if (model != null) {
mergedModel.putAll(model);
}
// Expose RequestContext?
if (this.requestContextAttribute != null) {
mergedModel.put(this.requestContextAttribute, createRequestContext(request, response, mergedModel));
}
return mergedModel;
}
标题:SpringWeb Cannot expose request attribute 'XXX' because of an existing model object of the same name
分类:Java
链接:https://www.aqwdzy.com/content/93
版权:通天技术网(www.aqwdzy.com)所分享发布内容,部分为网络转载,如有侵权请立即联系方式,我们第一时间删除并致歉!
热烈庆祝通天技术网开业大吉
@通天技术网 热烈庆祝通天技术网开业大吉
热烈庆祝通天技术网开业大吉
@通天技术网 热烈庆祝通天技术网开业大吉