Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
chenpeng_fei
spring-framework
提交
8ad95b09
S
spring-framework
项目概览
chenpeng_fei
/
spring-framework
与 Fork 源项目一致
从无法访问的项目Fork
通知
2
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
S
spring-framework
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
8ad95b09
编写于
11月 30, 2016
作者:
R
Rossen Stoyanchev
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
ReactiveContext and BindStatus in spring-web-reactive
Issue: SPR-14533
上级
136b33bc
变更
5
隐藏空白更改
内联
并排
Showing
5 changed file
with
900 addition
and
0 deletion
+900
-0
spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/AbstractView.java
...pringframework/web/reactive/result/view/AbstractView.java
+37
-0
spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/BindStatus.java
.../springframework/web/reactive/result/view/BindStatus.java
+342
-0
spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/RequestContext.java
...ingframework/web/reactive/result/view/RequestContext.java
+414
-0
spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/UrlBasedViewResolver.java
...mework/web/reactive/result/view/UrlBasedViewResolver.java
+21
-0
spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/RequestContextTests.java
...amework/web/reactive/result/view/RequestContextTests.java
+86
-0
未找到文件。
spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/AbstractView.java
浏览文件 @
8ad95b09
...
...
@@ -49,6 +49,8 @@ public abstract class AbstractView implements View, ApplicationContextAware {
private
Charset
defaultCharset
=
StandardCharsets
.
UTF_8
;
private
String
requestContextAttribute
;
private
ApplicationContext
applicationContext
;
...
...
@@ -95,6 +97,21 @@ public abstract class AbstractView implements View, ApplicationContextAware {
return
this
.
defaultCharset
;
}
/**
* Set the name of the RequestContext attribute for this view.
* Default is none.
*/
public
void
setRequestContextAttribute
(
String
requestContextAttribute
)
{
this
.
requestContextAttribute
=
requestContextAttribute
;
}
/**
* Return the name of the RequestContext attribute, if any.
*/
public
String
getRequestContextAttribute
()
{
return
this
.
requestContextAttribute
;
}
@Override
public
void
setApplicationContext
(
ApplicationContext
applicationContext
)
{
this
.
applicationContext
=
applicationContext
;
...
...
@@ -126,6 +143,12 @@ public abstract class AbstractView implements View, ApplicationContextAware {
}
Map
<
String
,
Object
>
mergedModel
=
getModelAttributes
(
model
,
exchange
);
// Expose RequestContext?
if
(
this
.
requestContextAttribute
!=
null
)
{
mergedModel
.
put
(
this
.
requestContextAttribute
,
createRequestContext
(
exchange
,
mergedModel
));
}
return
renderInternal
(
mergedModel
,
contentType
,
exchange
);
}
...
...
@@ -145,6 +168,20 @@ public abstract class AbstractView implements View, ApplicationContextAware {
return
attributes
;
}
/**
* Create a RequestContext to expose under the specified attribute name.
* <p>The default implementation creates a standard RequestContext instance for the
* given request and model. Can be overridden in subclasses for custom instances.
* @param exchange current exchange
* @param model combined output Map (never {@code null}),
* with dynamic values taking precedence over static attributes
* @return the RequestContext instance
* @see #setRequestContextAttribute
*/
protected
RequestContext
createRequestContext
(
ServerWebExchange
exchange
,
Map
<
String
,
Object
>
model
)
{
return
new
RequestContext
(
exchange
,
model
,
this
.
applicationContext
);
}
/**
* Subclasses must implement this method to actually render the view.
* @param renderAttributes combined output Map (never {@code null}),
...
...
spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/BindStatus.java
0 → 100644
浏览文件 @
8ad95b09
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org.springframework.web.reactive.result.view
;
import
java.beans.PropertyEditor
;
import
java.util.Arrays
;
import
java.util.List
;
import
org.springframework.beans.BeanWrapper
;
import
org.springframework.beans.PropertyAccessorFactory
;
import
org.springframework.context.NoSuchMessageException
;
import
org.springframework.util.StringUtils
;
import
org.springframework.validation.BindingResult
;
import
org.springframework.validation.Errors
;
import
org.springframework.validation.ObjectError
;
import
org.springframework.web.util.HtmlUtils
;
/**
* Simple adapter to expose the bind status of a field or object.
* Set as a variable by FreeMarker macros and other tag libraries.
*
* <p>Obviously, object status representations (i.e. errors at the object level
* rather than the field level) do not have an expression and a value but only
* error codes and messages. For simplicity's sake and to be able to use the same
* tags and macros, the same status class is used for both scenarios.
*
* @author Rossen Stoyanchev
* @since 5.0
* @see RequestContext#getBindStatus
*/
public
class
BindStatus
{
private
final
RequestContext
requestContext
;
private
final
String
path
;
private
final
boolean
htmlEscape
;
private
final
String
expression
;
private
final
Errors
errors
;
private
BindingResult
bindingResult
;
private
Object
value
;
private
Class
<?>
valueType
;
private
Object
actualValue
;
private
PropertyEditor
editor
;
private
List
<?
extends
ObjectError
>
objectErrors
;
private
String
[]
errorCodes
;
private
String
[]
errorMessages
;
/**
* Create a new BindStatus instance, representing a field or object status.
* @param requestContext the current RequestContext
* @param path the bean and property path for which values and errors
* will be resolved (e.g. "customer.address.street")
* @param htmlEscape whether to HTML-escape error messages and string values
* @throws IllegalStateException if no corresponding Errors object found
*/
public
BindStatus
(
RequestContext
requestContext
,
String
path
,
boolean
htmlEscape
)
throws
IllegalStateException
{
this
.
requestContext
=
requestContext
;
this
.
path
=
path
;
this
.
htmlEscape
=
htmlEscape
;
// determine name of the object and property
String
beanName
;
int
dotPos
=
path
.
indexOf
(
'.'
);
if
(
dotPos
==
-
1
)
{
// property not set, only the object itself
beanName
=
path
;
this
.
expression
=
null
;
}
else
{
beanName
=
path
.
substring
(
0
,
dotPos
);
this
.
expression
=
path
.
substring
(
dotPos
+
1
);
}
this
.
errors
=
requestContext
.
getErrors
(
beanName
,
false
).
orElse
(
null
);
if
(
this
.
errors
!=
null
)
{
// Usual case: A BindingResult is available as request attribute.
// Can determine error codes and messages for the given expression.
// Can use a custom PropertyEditor, as registered by a form controller.
if
(
this
.
expression
!=
null
)
{
if
(
"*"
.
equals
(
this
.
expression
))
{
this
.
objectErrors
=
this
.
errors
.
getAllErrors
();
}
else
if
(
this
.
expression
.
endsWith
(
"*"
))
{
this
.
objectErrors
=
this
.
errors
.
getFieldErrors
(
this
.
expression
);
}
else
{
this
.
objectErrors
=
this
.
errors
.
getFieldErrors
(
this
.
expression
);
this
.
value
=
this
.
errors
.
getFieldValue
(
this
.
expression
);
this
.
valueType
=
this
.
errors
.
getFieldType
(
this
.
expression
);
if
(
this
.
errors
instanceof
BindingResult
)
{
this
.
bindingResult
=
(
BindingResult
)
this
.
errors
;
this
.
actualValue
=
this
.
bindingResult
.
getRawFieldValue
(
this
.
expression
);
this
.
editor
=
this
.
bindingResult
.
findEditor
(
this
.
expression
,
null
);
}
else
{
this
.
actualValue
=
this
.
value
;
}
}
}
else
{
this
.
objectErrors
=
this
.
errors
.
getGlobalErrors
();
}
this
.
errorCodes
=
initErrorCodes
(
this
.
objectErrors
);
}
else
{
// No BindingResult available as request attribute:
// Probably forwarded directly to a form view.
// Let's do the best we can: extract a plain target if appropriate.
Object
target
=
requestContext
.
getModelObject
(
beanName
);
if
(
target
==
null
)
{
throw
new
IllegalStateException
(
"Neither BindingResult nor plain target object for bean name '"
+
beanName
+
"' available as request attribute"
);
}
if
(
this
.
expression
!=
null
&&
!
"*"
.
equals
(
this
.
expression
)
&&
!
this
.
expression
.
endsWith
(
"*"
))
{
BeanWrapper
bw
=
PropertyAccessorFactory
.
forBeanPropertyAccess
(
target
);
this
.
value
=
bw
.
getPropertyValue
(
this
.
expression
);
this
.
valueType
=
bw
.
getPropertyType
(
this
.
expression
);
this
.
actualValue
=
this
.
value
;
}
this
.
errorCodes
=
new
String
[
0
];
this
.
errorMessages
=
new
String
[
0
];
}
if
(
htmlEscape
&&
this
.
value
instanceof
String
)
{
this
.
value
=
HtmlUtils
.
htmlEscape
((
String
)
this
.
value
);
}
}
/**
* Extract the error codes from the ObjectError list.
*/
private
static
String
[]
initErrorCodes
(
List
<?
extends
ObjectError
>
objectErrors
)
{
String
[]
errorCodes
=
new
String
[
objectErrors
.
size
()];
for
(
int
i
=
0
;
i
<
objectErrors
.
size
();
i
++)
{
ObjectError
error
=
objectErrors
.
get
(
i
);
errorCodes
[
i
]
=
error
.
getCode
();
}
return
errorCodes
;
}
/**
* Return the bean and property path for which values and errors
* will be resolved (e.g. "customer.address.street").
*/
public
String
getPath
()
{
return
this
.
path
;
}
/**
* Return a bind expression that can be used in HTML forms as input name
* for the respective field, or {@code null} if not field-specific.
* <p>Returns a bind path appropriate for resubmission, e.g. "address.street".
* Note that the complete bind path as required by the bind tag is
* "customer.address.street", if bound to a "customer" bean.
*/
public
String
getExpression
()
{
return
this
.
expression
;
}
/**
* Return the current value of the field, i.e. either the property value
* or a rejected update, or {@code null} if not field-specific.
* <p>This value will be an HTML-escaped String if the original value
* already was a String.
*/
public
Object
getValue
()
{
return
this
.
value
;
}
/**
* Get the '{@code Class}' type of the field. Favor this instead of
* '{@code getValue().getClass()}' since '{@code getValue()}' may
* return '{@code null}'.
*/
public
Class
<?>
getValueType
()
{
return
this
.
valueType
;
}
/**
* Return the actual value of the field, i.e. the raw property value,
* or {@code null} if not available.
*/
public
Object
getActualValue
()
{
return
this
.
actualValue
;
}
/**
* Return a suitable display value for the field, i.e. the stringified
* value if not null, and an empty string in case of a null value.
* <p>This value will be an HTML-escaped String if the original value
* was non-null: the {@code toString} result of the original value
* will get HTML-escaped.
*/
public
String
getDisplayValue
()
{
if
(
this
.
value
instanceof
String
)
{
return
(
String
)
this
.
value
;
}
if
(
this
.
value
!=
null
)
{
return
(
this
.
htmlEscape
?
HtmlUtils
.
htmlEscape
(
this
.
value
.
toString
())
:
this
.
value
.
toString
());
}
return
""
;
}
/**
* Return if this status represents a field or object error.
*/
public
boolean
isError
()
{
return
(
this
.
errorCodes
!=
null
&&
this
.
errorCodes
.
length
>
0
);
}
/**
* Return the error codes for the field or object, if any.
* Returns an empty array instead of null if none.
*/
public
String
[]
getErrorCodes
()
{
return
this
.
errorCodes
;
}
/**
* Return the first error codes for the field or object, if any.
*/
public
String
getErrorCode
()
{
return
(
this
.
errorCodes
.
length
>
0
?
this
.
errorCodes
[
0
]
:
""
);
}
/**
* Return the resolved error messages for the field or object,
* if any. Returns an empty array instead of null if none.
*/
public
String
[]
getErrorMessages
()
{
initErrorMessages
();
return
this
.
errorMessages
;
}
/**
* Return the first error message for the field or object, if any.
*/
public
String
getErrorMessage
()
{
initErrorMessages
();
return
(
this
.
errorMessages
.
length
>
0
?
this
.
errorMessages
[
0
]
:
""
);
}
/**
* Return an error message string, concatenating all messages
* separated by the given delimiter.
* @param delimiter separator string, e.g. ", " or "<br>"
* @return the error message string
*/
public
String
getErrorMessagesAsString
(
String
delimiter
)
{
initErrorMessages
();
return
StringUtils
.
arrayToDelimitedString
(
this
.
errorMessages
,
delimiter
);
}
/**
* Extract the error messages from the ObjectError list.
*/
private
void
initErrorMessages
()
throws
NoSuchMessageException
{
if
(
this
.
errorMessages
==
null
)
{
this
.
errorMessages
=
new
String
[
this
.
objectErrors
.
size
()];
for
(
int
i
=
0
;
i
<
this
.
objectErrors
.
size
();
i
++)
{
ObjectError
error
=
this
.
objectErrors
.
get
(
i
);
this
.
errorMessages
[
i
]
=
this
.
requestContext
.
getMessage
(
error
,
this
.
htmlEscape
);
}
}
}
/**
* Return the Errors instance (typically a BindingResult) that this
* bind status is currently associated with.
* @return the current Errors instance, or {@code null} if none
* @see org.springframework.validation.BindingResult
*/
public
Errors
getErrors
()
{
return
this
.
errors
;
}
/**
* Return the PropertyEditor for the property that this bind status
* is currently bound to.
* @return the current PropertyEditor, or {@code null} if none
*/
public
PropertyEditor
getEditor
()
{
return
this
.
editor
;
}
/**
* Find a PropertyEditor for the given value class, associated with
* the property that this bound status is currently bound to.
* @param valueClass the value class that an editor is needed for
* @return the associated PropertyEditor, or {@code null} if none
*/
public
PropertyEditor
findEditor
(
Class
<?>
valueClass
)
{
return
(
this
.
bindingResult
!=
null
?
this
.
bindingResult
.
findEditor
(
this
.
expression
,
valueClass
)
:
null
);
}
@Override
public
String
toString
()
{
StringBuilder
sb
=
new
StringBuilder
(
"BindStatus: "
);
sb
.
append
(
"expression=["
).
append
(
this
.
expression
).
append
(
"]; "
);
sb
.
append
(
"value=["
).
append
(
this
.
value
).
append
(
"]"
);
if
(
isError
())
{
sb
.
append
(
"; errorCodes="
).
append
(
Arrays
.
asList
(
this
.
errorCodes
));
}
return
sb
.
toString
();
}
}
spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/RequestContext.java
0 → 100644
浏览文件 @
8ad95b09
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org.springframework.web.reactive.result.view
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Locale
;
import
java.util.Map
;
import
java.util.Optional
;
import
java.util.TimeZone
;
import
org.springframework.context.MessageSource
;
import
org.springframework.context.MessageSourceResolvable
;
import
org.springframework.context.NoSuchMessageException
;
import
org.springframework.http.server.reactive.ServerHttpRequest
;
import
org.springframework.util.Assert
;
import
org.springframework.validation.BindException
;
import
org.springframework.validation.BindingResult
;
import
org.springframework.validation.Errors
;
import
org.springframework.web.bind.EscapedErrors
;
import
org.springframework.web.server.ServerWebExchange
;
import
org.springframework.web.util.HtmlUtils
;
import
org.springframework.web.util.UriTemplate
;
/**
* Context holder for request-specific state, like the {@link MessageSource} to
* use, current locale, binding errors, etc. Provides easy access to localized
* messages and Errors instances.
*
* <p>Suitable for exposition to views, and usage within FreeMarker templates,
* and tag libraries.
*
* <p>Can be instantiated manually, or automatically exposed to views as model
* attribute via AbstractView's "requestContextAttribute" property.
*
* @author Rossen Stoyanchev
* @since 5.0
*/
public
class
RequestContext
{
private
final
ServerWebExchange
exchange
;
private
final
Map
<
String
,
Object
>
model
;
private
final
MessageSource
messageSource
;
private
Boolean
defaultHtmlEscape
;
private
Map
<
String
,
Errors
>
errorsMap
;
private
Locale
locale
;
private
TimeZone
timeZone
;
public
RequestContext
(
ServerWebExchange
exchange
,
Map
<
String
,
Object
>
model
,
MessageSource
messageSource
)
{
Assert
.
notNull
(
exchange
,
"'exchange' is required"
);
Assert
.
notNull
(
model
,
"'model' is required"
);
Assert
.
notNull
(
messageSource
,
"'messageSource' is required"
);
this
.
exchange
=
exchange
;
this
.
model
=
model
;
this
.
messageSource
=
messageSource
;
this
.
defaultHtmlEscape
=
null
;
// TODO
this
.
locale
=
Locale
.
getDefault
();
// TODO
this
.
timeZone
=
TimeZone
.
getDefault
();
// TODO
}
protected
final
ServerWebExchange
getExchange
()
{
return
this
.
exchange
;
}
/**
* Return the MessageSource in use with this request.
*/
public
MessageSource
getMessageSource
()
{
return
this
.
messageSource
;
}
/**
* Return the model Map that this RequestContext encapsulates, if any.
* @return the populated model Map, or {@code null} if none available
*/
public
Map
<
String
,
Object
>
getModel
()
{
return
this
.
model
;
}
/**
* Return the current Locale.
* TODO: currently this is Locale.getDefault()
*/
public
final
Locale
getLocale
()
{
return
this
.
locale
;
}
/**
* Return the current TimeZone.
* TODO: currently this is the Timezone.getDefault()
*/
public
TimeZone
getTimeZone
()
{
return
this
.
timeZone
;
}
/**
* Change the current locale to the specified one.
* TODO: currently simply change the internal field
*/
public
void
changeLocale
(
Locale
locale
)
{
this
.
locale
=
locale
;
}
/**
* Change the current locale to the specified locale and time zone context.
* TODO: currently simply change the internal fields
*/
public
void
changeLocale
(
Locale
locale
,
TimeZone
timeZone
)
{
this
.
locale
=
locale
;
this
.
timeZone
=
timeZone
;
}
/**
* (De)activate default HTML escaping for messages and errors, for the scope
* of this RequestContext.
* <p>TODO: currently no application-wide setting ...
*/
public
void
setDefaultHtmlEscape
(
boolean
defaultHtmlEscape
)
{
this
.
defaultHtmlEscape
=
defaultHtmlEscape
;
}
/**
* Is default HTML escaping active? Falls back to {@code false} in case of
* no explicit default given.
*/
public
boolean
isDefaultHtmlEscape
()
{
return
(
this
.
defaultHtmlEscape
!=
null
&&
this
.
defaultHtmlEscape
.
booleanValue
());
}
/**
* Return the default HTML escape setting, differentiating between no default
* specified and an explicit value.
* @return whether default HTML escaping is enabled (null = no explicit default)
*/
public
Boolean
getDefaultHtmlEscape
()
{
return
this
.
defaultHtmlEscape
;
}
/**
* Return the context path of the the current web application. This is
* useful for building links to other resources within the application.
* <p>Delegates to {@link ServerHttpRequest#getContextPath()}.
*/
public
String
getContextPath
()
{
return
this
.
exchange
.
getRequest
().
getContextPath
();
}
/**
* Return a context-aware URl for the given relative URL.
* @param relativeUrl the relative URL part
* @return a URL that points back to the current web application with an
* absolute path also URL-encoded accordingly
*/
public
String
getContextUrl
(
String
relativeUrl
)
{
String
url
=
getContextPath
()
+
relativeUrl
;
// TODO: this.response.encodeURL(url)
return
url
;
}
/**
* Return a context-aware URl for the given relative URL with placeholders --
* named keys with braces {@code {}}. For example, send in a relative URL
* {@code foo/{bar}?spam={spam}} and a parameter map {@code {bar=baz,spam=nuts}}
* and the result will be {@code [contextpath]/foo/baz?spam=nuts}.
* @param relativeUrl the relative URL part
* @param params a map of parameters to insert as placeholders in the url
* @return a URL that points back to the current web application with an
* absolute path also URL-encoded accordingly
*/
public
String
getContextUrl
(
String
relativeUrl
,
Map
<
String
,
?>
params
)
{
String
url
=
getContextPath
()
+
relativeUrl
;
UriTemplate
template
=
new
UriTemplate
(
url
);
url
=
template
.
expand
(
params
).
toASCIIString
();
// TODO: this.response.encodeURL(url)
return
url
;
}
/**
* Return the request path of the request. This is useful as HTML form
* action target, also in combination with the original query string.
*/
public
String
getRequestPath
()
{
return
this
.
exchange
.
getRequest
().
getURI
().
getPath
();
}
/**
* Return the query string of the current request. This is useful for
* building an HTML form action target in combination with the original
* request path.
*/
public
String
getQueryString
()
{
return
this
.
exchange
.
getRequest
().
getURI
().
getQuery
();
}
/**
* Retrieve the message for the given code, using the "defaultHtmlEscape" setting.
* @param code code of the message
* @param defaultMessage String to return if the lookup fails
* @return the message
*/
public
String
getMessage
(
String
code
,
String
defaultMessage
)
{
return
getMessage
(
code
,
null
,
defaultMessage
,
isDefaultHtmlEscape
());
}
/**
* Retrieve the message for the given code, using the "defaultHtmlEscape" setting.
* @param code code of the message
* @param args arguments for the message, or {@code null} if none
* @param defaultMessage String to return if the lookup fails
* @return the message
*/
public
String
getMessage
(
String
code
,
Object
[]
args
,
String
defaultMessage
)
{
return
getMessage
(
code
,
args
,
defaultMessage
,
isDefaultHtmlEscape
());
}
/**
* Retrieve the message for the given code, using the "defaultHtmlEscape" setting.
* @param code code of the message
* @param args arguments for the message as a List, or {@code null} if none
* @param defaultMessage String to return if the lookup fails
* @return the message
*/
public
String
getMessage
(
String
code
,
List
<?>
args
,
String
defaultMessage
)
{
return
getMessage
(
code
,
(
args
!=
null
?
args
.
toArray
()
:
null
),
defaultMessage
,
isDefaultHtmlEscape
());
}
/**
* Retrieve the message for the given code.
* @param code code of the message
* @param args arguments for the message, or {@code null} if none
* @param defaultMessage String to return if the lookup fails
* @param htmlEscape HTML escape the message?
* @return the message
*/
public
String
getMessage
(
String
code
,
Object
[]
args
,
String
defaultMessage
,
boolean
htmlEscape
)
{
String
msg
=
this
.
messageSource
.
getMessage
(
code
,
args
,
defaultMessage
,
this
.
locale
);
return
(
htmlEscape
?
HtmlUtils
.
htmlEscape
(
msg
)
:
msg
);
}
/**
* Retrieve the message for the given code, using the "defaultHtmlEscape" setting.
* @param code code of the message
* @return the message
* @throws org.springframework.context.NoSuchMessageException if not found
*/
public
String
getMessage
(
String
code
)
throws
NoSuchMessageException
{
return
getMessage
(
code
,
null
,
isDefaultHtmlEscape
());
}
/**
* Retrieve the message for the given code, using the "defaultHtmlEscape" setting.
* @param code code of the message
* @param args arguments for the message, or {@code null} if none
* @return the message
* @throws org.springframework.context.NoSuchMessageException if not found
*/
public
String
getMessage
(
String
code
,
Object
[]
args
)
throws
NoSuchMessageException
{
return
getMessage
(
code
,
args
,
isDefaultHtmlEscape
());
}
/**
* Retrieve the message for the given code, using the "defaultHtmlEscape" setting.
* @param code code of the message
* @param args arguments for the message as a List, or {@code null} if none
* @return the message
* @throws org.springframework.context.NoSuchMessageException if not found
*/
public
String
getMessage
(
String
code
,
List
<?>
args
)
throws
NoSuchMessageException
{
return
getMessage
(
code
,
(
args
!=
null
?
args
.
toArray
()
:
null
),
isDefaultHtmlEscape
());
}
/**
* Retrieve the message for the given code.
* @param code code of the message
* @param args arguments for the message, or {@code null} if none
* @param htmlEscape HTML escape the message?
* @return the message
* @throws org.springframework.context.NoSuchMessageException if not found
*/
public
String
getMessage
(
String
code
,
Object
[]
args
,
boolean
htmlEscape
)
throws
NoSuchMessageException
{
String
msg
=
this
.
messageSource
.
getMessage
(
code
,
args
,
this
.
locale
);
return
(
htmlEscape
?
HtmlUtils
.
htmlEscape
(
msg
)
:
msg
);
}
/**
* Retrieve the given MessageSourceResolvable (e.g. an ObjectError instance), using the "defaultHtmlEscape" setting.
* @param resolvable the MessageSourceResolvable
* @return the message
* @throws org.springframework.context.NoSuchMessageException if not found
*/
public
String
getMessage
(
MessageSourceResolvable
resolvable
)
throws
NoSuchMessageException
{
return
getMessage
(
resolvable
,
isDefaultHtmlEscape
());
}
/**
* Retrieve the given MessageSourceResolvable (e.g. an ObjectError instance).
* @param resolvable the MessageSourceResolvable
* @param htmlEscape HTML escape the message?
* @return the message
* @throws org.springframework.context.NoSuchMessageException if not found
*/
public
String
getMessage
(
MessageSourceResolvable
resolvable
,
boolean
htmlEscape
)
throws
NoSuchMessageException
{
String
msg
=
this
.
messageSource
.
getMessage
(
resolvable
,
this
.
locale
);
return
(
htmlEscape
?
HtmlUtils
.
htmlEscape
(
msg
)
:
msg
);
}
/**
* Retrieve the Errors instance for the given bind object, using the
* "defaultHtmlEscape" setting.
* @param name name of the bind object
* @return the Errors instance, or {@code null} if not found
*/
public
Optional
<
Errors
>
getErrors
(
String
name
)
{
return
getErrors
(
name
,
isDefaultHtmlEscape
());
}
/**
* Retrieve the Errors instance for the given bind object.
* @param name name of the bind object
* @param htmlEscape create an Errors instance with automatic HTML escaping?
* @return the Errors instance, or {@code null} if not found
*/
public
Optional
<
Errors
>
getErrors
(
String
name
,
boolean
htmlEscape
)
{
if
(
this
.
errorsMap
==
null
)
{
this
.
errorsMap
=
new
HashMap
<>();
}
// Since there is no Optional orElse + flatMap...
Optional
<
Errors
>
optional
=
Optional
.
ofNullable
(
this
.
errorsMap
.
get
(
name
));
optional
=
optional
.
isPresent
()
?
optional
:
getModelObject
(
BindingResult
.
MODEL_KEY_PREFIX
+
name
);
return
optional
.
map
(
errors
->
{
if
(
errors
instanceof
BindException
)
{
return
((
BindException
)
errors
).
getBindingResult
();
}
else
{
return
errors
;
}
})
.
map
(
errors
->
{
if
(
htmlEscape
&&
!(
errors
instanceof
EscapedErrors
))
{
errors
=
new
EscapedErrors
(
errors
);
}
else
if
(!
htmlEscape
&&
errors
instanceof
EscapedErrors
)
{
errors
=
((
EscapedErrors
)
errors
).
getSource
();
}
this
.
errorsMap
.
put
(
name
,
errors
);
return
errors
;
});
}
/**
* Retrieve the model object for the given model name, either from the model
* or from the request attributes.
* @param modelName the name of the model object
* @return the model object
*/
@SuppressWarnings
(
"unchecked"
)
protected
<
T
>
Optional
<
T
>
getModelObject
(
String
modelName
)
{
return
Optional
.
ofNullable
(
this
.
model
)
.
map
(
model
->
Optional
.
ofNullable
((
T
)
model
.
get
(
modelName
)))
.
orElse
(
this
.
exchange
.
getAttribute
(
modelName
));
}
/**
* Create a BindStatus for the given bind object using the
* "defaultHtmlEscape" setting.
* @param path the bean and property path for which values and errors will
* be resolved (e.g. "person.age")
* @return the new BindStatus instance
* @throws IllegalStateException if no corresponding Errors object found
*/
public
BindStatus
getBindStatus
(
String
path
)
throws
IllegalStateException
{
return
new
BindStatus
(
this
,
path
,
isDefaultHtmlEscape
());
}
/**
* Create a BindStatus for the given bind object, using the
* "defaultHtmlEscape" setting.
* @param path the bean and property path for which values and errors will
* be resolved (e.g. "person.age")
* @param htmlEscape create a BindStatus with automatic HTML escaping?
* @return the new BindStatus instance
* @throws IllegalStateException if no corresponding Errors object found
*/
public
BindStatus
getBindStatus
(
String
path
,
boolean
htmlEscape
)
throws
IllegalStateException
{
return
new
BindStatus
(
this
,
path
,
htmlEscape
);
}
}
spring-web-reactive/src/main/java/org/springframework/web/reactive/result/view/UrlBasedViewResolver.java
浏览文件 @
8ad95b09
...
...
@@ -77,6 +77,8 @@ public class UrlBasedViewResolver extends ViewResolverSupport implements ViewRes
private
Function
<
String
,
RedirectView
>
redirectViewProvider
=
url
->
new
RedirectView
(
url
);
private
String
requestContextAttribute
;
/**
* Set the view class to instantiate through {@link #createUrlBasedView(String)}.
...
...
@@ -162,6 +164,23 @@ public class UrlBasedViewResolver extends ViewResolverSupport implements ViewRes
this
.
redirectViewProvider
=
redirectViewProvider
;
}
/**
* Set the name of the RequestContext attribute for all views.
* @param requestContextAttribute name of the RequestContext attribute
* @see AbstractView#setRequestContextAttribute
*/
public
void
setRequestContextAttribute
(
String
requestContextAttribute
)
{
this
.
requestContextAttribute
=
requestContextAttribute
;
}
/**
* Return the name of the RequestContext attribute for all views, if any.
*/
protected
String
getRequestContextAttribute
()
{
return
this
.
requestContextAttribute
;
}
@Override
public
void
afterPropertiesSet
()
throws
Exception
{
if
(
getViewClass
()
==
null
)
{
...
...
@@ -169,6 +188,7 @@ public class UrlBasedViewResolver extends ViewResolverSupport implements ViewRes
}
}
@Override
public
Mono
<
View
>
resolveViewName
(
String
viewName
,
Locale
locale
)
{
if
(!
canHandle
(
viewName
,
locale
))
{
...
...
@@ -221,6 +241,7 @@ public class UrlBasedViewResolver extends ViewResolverSupport implements ViewRes
protected
AbstractUrlBasedView
createUrlBasedView
(
String
viewName
)
{
AbstractUrlBasedView
view
=
(
AbstractUrlBasedView
)
BeanUtils
.
instantiateClass
(
getViewClass
());
view
.
setSupportedMediaTypes
(
getSupportedMediaTypes
());
view
.
setRequestContextAttribute
(
getRequestContextAttribute
());
view
.
setDefaultCharset
(
getDefaultCharset
());
view
.
setUrl
(
getPrefix
()
+
viewName
+
getSuffix
());
return
view
;
...
...
spring-web-reactive/src/test/java/org/springframework/web/reactive/result/view/RequestContextTests.java
0 → 100644
浏览文件 @
8ad95b09
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org.springframework.web.reactive.result.view
;
import
java.util.HashMap
;
import
java.util.Map
;
import
org.junit.Before
;
import
org.junit.Test
;
import
org.springframework.context.support.GenericApplicationContext
;
import
org.springframework.mock.http.server.reactive.test.MockServerHttpRequest
;
import
org.springframework.mock.http.server.reactive.test.MockServerHttpResponse
;
import
org.springframework.web.server.ServerWebExchange
;
import
org.springframework.web.server.adapter.DefaultServerWebExchange
;
import
org.springframework.web.server.session.DefaultWebSessionManager
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
/**
* Unit tests for {@link RequestContext}.
* @author Rossen Stoyanchev
*/
public
class
RequestContextTests
{
private
ServerWebExchange
exchange
;
private
MockServerHttpRequest
request
;
private
GenericApplicationContext
applicationContext
;
private
Map
<
String
,
Object
>
model
=
new
HashMap
<>();
@Before
public
void
init
()
{
this
.
request
=
new
MockServerHttpRequest
();
MockServerHttpResponse
response
=
new
MockServerHttpResponse
();
DefaultWebSessionManager
sessionManager
=
new
DefaultWebSessionManager
();
this
.
exchange
=
new
DefaultServerWebExchange
(
this
.
request
,
response
,
sessionManager
);
this
.
applicationContext
=
new
GenericApplicationContext
();
this
.
applicationContext
.
refresh
();
}
@Test
public
void
testGetContextUrl
()
throws
Exception
{
this
.
request
.
setContextPath
(
"foo/"
);
RequestContext
context
=
new
RequestContext
(
this
.
exchange
,
this
.
model
,
this
.
applicationContext
);
assertEquals
(
"foo/bar"
,
context
.
getContextUrl
(
"bar"
));
}
@Test
public
void
testGetContextUrlWithMap
()
throws
Exception
{
this
.
request
.
setContextPath
(
"foo/"
);
RequestContext
context
=
new
RequestContext
(
this
.
exchange
,
this
.
model
,
this
.
applicationContext
);
Map
<
String
,
Object
>
map
=
new
HashMap
<>();
map
.
put
(
"foo"
,
"bar"
);
map
.
put
(
"spam"
,
"bucket"
);
assertEquals
(
"foo/bar?spam=bucket"
,
context
.
getContextUrl
(
"{foo}?spam={spam}"
,
map
));
}
@Test
public
void
testGetContextUrlWithMapEscaping
()
throws
Exception
{
this
.
request
.
setContextPath
(
"foo/"
);
RequestContext
context
=
new
RequestContext
(
this
.
exchange
,
this
.
model
,
this
.
applicationContext
);
Map
<
String
,
Object
>
map
=
new
HashMap
<>();
map
.
put
(
"foo"
,
"bar baz"
);
map
.
put
(
"spam"
,
"&bucket="
);
assertEquals
(
"foo/bar%20baz?spam=%26bucket%3D"
,
context
.
getContextUrl
(
"{foo}?spam={spam}"
,
map
));
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录