本文共 11696 字,大约阅读时间需要 38 分钟。
您可以应用Spring Web MVC框架主题来设置应用程序的整体外观,从而增强用户体验。主题是影响应用程序视觉风格的静态资源(通常是样式表和图像)的集合。
要在Web应用程序中使用主题,您必须设置接口的 org.springframework.ui.context.ThemeSource
实现。该WebApplicationContext
接口扩展ThemeSource
,但其代表职责的专用实现。默认情况下,委托将是 org.springframework.ui.context.support.ResourceBundleThemeSource
从类路径根目录加载属性文件的实现。要使用自定义ThemeSource
实现或配置基本名称前缀ResourceBundleThemeSource
,可以在应用程序上下文中使用保留名称注册一个bean themeSource
。Web应用程序上下文将自动检测具有该名称的bean并使用它。
使用时ResourceBundleThemeSource
,在一个简单的属性文件中定义一个主题。属性文件列出构成主题的资源。这是一个例子:
=的styleSheet /主题/冷却/ style.css中背景= /主题/冷却/ IMG / coolBg.jpg
属性的键是从视图代码引用主题元素的名称。对于JSP,您通常使用与spring:theme
标记非常相似的自定义标签来执行此操作spring:message
。以下JSP片段使用上一个示例中定义的主题来自定义外观:
<%@ taglib prefix = “spring” uri = “http://www.springframework.org/tags” %> “ type = ”text / css“ /> “ > ...
默认情况下,ResourceBundleThemeSource
使用空的基本名称前缀。因此,属性文件从类路径的根目录加载。因此,您可以将 cool.properties
主题定义放在类路径的根目录下,例如/WEB-INF/classes
。它ResourceBundleThemeSource
使用标准的Java资源包加载机制,允许主题的全面国际化。例如,我们可以有一个/WEB-INF/classes/cool_nl.properties
引用一个特殊的背景图像与荷兰文本。
定义主题后,如上一节所述,您决定使用哪个主题。该 DispatcherServlet
会寻找一个叫豆themeResolver
,以找出 ThemeResolver
使用实施。主题解析器的工作方式与a的方式大致相同 LocaleResolver
。它检测到用于特定请求的主题,还可以更改请求的主题。以下主题解析器由Spring提供:
表18.5。ThemeResolver实现
类 | 描述 |
---|---|
FixedThemeResolver | 选择一个固定的主题,使用defaultThemeName 属性设置。 |
SessionThemeResolver | 主题维护在用户的HTTP会话中。它只需要为每个会话设置一次,但不会在会话之间持久化。 |
CookieThemeResolver | 所选主题存储在客户端的cookie中。 |
ThemeChangeInterceptor
允许使用简单请求参数对每个请求进行主题更改的功能。 Spring的内置多部分支持处理Web应用程序中的文件上传。您可以MultipartResolver
使用org.springframework.web.multipart
包中定义的可插入对象来 启用此multipart支持。Spring提供了一个MultipartResolver
用于,另一个用于Servlet 3.0 multipart请求解析。
默认情况下,Spring没有多部门处理,因为一些开发人员想要自己处理多个部件。通过将多部分解析器添加到Web应用程序的上下文来启用Spring multipart处理。检查每个请求以查看它是否包含多部分。如果没有找到multipart,请求按预期方式继续。如果在请求中找到一个multipart,MultipartResolver
则使用在上下文中声明的multipart 。之后,您的请求中的multipart属性被视为任何其他属性。
以下示例显示如何使用CommonsMultipartResolver
:
<! - 可用属性之一; 最大文件大小(以字节为单位) - >
当然,您还需要将适当的jar放在您的类路径中,以使多部分解析器工作。在这种情况下CommonsMultipartResolver
,您需要使用 commons-fileupload.jar
。
当Spring DispatcherServlet
检测到多部分请求时,它会激活已经在上下文中声明的解析器,并交给请求。解析器然后将当前包装HttpServletRequest
到MultipartHttpServletRequest
支持多部分文件上传的内容中。使用它MultipartHttpServletRequest
,您可以获取有关此请求所包含的多部分的信息,实际上可以在控制器中自己访问多部分文件。
为了使用的Servlet 3.0基于多解析,您需要标记 DispatcherServlet
用"multipart-config"
的部分web.xml
,或用 javax.servlet.MultipartConfigElement
在编程的Servlet注册,或在自定义Servlet类的情况下,可能与javax.servlet.annotation.MultipartConfig
你的Servlet类注解。需要在Servlet注册级别应用最大大小或存储位置等配置设置,因为Servlet 3.0不允许从MultipartResolver完成这些设置。
一旦使用上述方法之一启用了Servlet 3.0 multipart解析,您可以添加StandardServletMultipartResolver
到Spring配置:
完成MultipartResolver
工作后,请求像其他任何一样处理。首先,创建一个带有文件输入的表单,允许用户上传表单。编码属性(enctype="multipart/form-data"
)允许浏览器知道如何将表单编码为多部分请求:
上传文件请</ title> </ head> 请上传文件
下一步是创建一个处理文件上传的控制器。该控制器非常类似于,除了我们使用MultipartHttpServletRequest
或MultipartFile
在方法参数中:
@Controller public class FileUploadController { @PostMapping(“/ form”) public String handleFormUpload( @RequestParam(“name”) String name, @RequestParam(“file”) MultipartFile文件){ if(!file.isEmpty()){ byte [] bytes = file.getBytes(); //存储字节某处 返回 “redirect:uploadSuccess” ; } 返回 “redirect:uploadFailure” ; }}
注意@RequestParam
方法参数如何映射到表单中声明的输入元素。在这个例子中,没有什么是完成的byte[]
,但实际上你可以将它保存在数据库中,将它存储在文件系统上,等等。
当使用Servlet 3.0多部分解析时,您也可以使用javax.servlet.http.Part
方法参数:
@Controller public class FileUploadController { @PostMapping(“/ form”) public String handleFormUpload( @RequestParam(“name”) String name, @RequestParam(“file”)零件文件){ InputStream inputStream = file.getInputStream(); //将上传文件的字节存储在某处 返回 “redirect:uploadSuccess” ; }}
也可以在RESTful服务方案中从非浏览器客户端提交多部分请求。所有上述示例和配置也适用于此。然而,与通常提交文件和简单表单字段的浏览器不同,编程客户端还可以发送特定内容类型的更复杂数据,例如具有文件的多部分请求,第二部分使用JSON格式的数据:
POST / someUrlContent-Type:multipart / mixed--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp内容处理:表单数据; NAME =“元数据”Content-Type:application / json; 字符集= UTF-8内容传输编码:8bit{ “名称”:“值”}--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp内容处理:表单数据; NAME =“文件的数据”; 文件名= “file.properties”Content-Type:text / xml内容传输编码:8bit...文件数据...
您可以使用@RequestParam("meta-data") String metadata
controller method参数访问名为“meta-data”的部分。但是,您可能更喜欢接受从请求部分正文中的JSON格式数据初始化的强类型对象,非常类似于@RequestBody
在非帮助下将非多部分请求的正文转换为目标对象的方式HttpMessageConverter
。
您可以使用@RequestPart
注释而不是@RequestParam
注释用于此目的。它允许您通过HttpMessageConverter
考虑多部分的'Content-Type'
标题来传递特定多部分的内容:
@PostMapping(“/ someUrl”) public String onSubmit( @RequestPart(“meta-data”)MetaData元数据, @RequestPart(“file-data”)MultipartFile文件){ // ...}
注意如何MultipartFile
使用@RequestParam
或 @RequestPart
互换访问方法参数。但是,@RequestPart("meta-data") MetaData
在这种情况下,该方法参数将被读取为基于其'Content-Type'
头部的JSON内容,并在此帮助下进行转换MappingJackson2HttpMessageConverter
。
Spring HandlerExceptionResolver
实现处理控制器执行期间发生的意外异常。一个HandlerExceptionResolver
有点象异常映射的,你可以在Web应用程序描述符定义web.xml
。但是,它们提供了一种更灵活的方法。例如,它们提供有关在抛出异常时正在执行哪个处理程序的信息。此外,处理异常的编程方式可以在请求转发到另一个URL之前提供更多的响应选项(与使用Servlet特定的异常映射相同的最终结果)。
除了实现HandlerExceptionResolver
接口,这只是一个实现resolveException(Exception, Handler)
方法和返回的问题 ModelAndView
,您还可以使用提供的SimpleMappingExceptionResolver
或创建 @ExceptionHandler
方法。将SimpleMappingExceptionResolver
让您采取可能被抛出的异常的类名,并将它映射到视图名。这在功能上等同于Servlet API的异常映射功能,但也可以从不同的处理程序实现更精细的异常映射。@ExceptionHandler
另一方面,注释可以用于应该调用来处理异常的方法。这样的方法可以定义在本地内部,@Controller
或者可以在@Controller
类中定义时应用于许多类 @ControllerAdvice
。以下部分将对此进行更详细的解释。
HandlerExceptionResolver
接口和SimpleMappingExceptionResolver
实现允许您在转发到这些视图之前将异常与声明性地映射到特定视图以及一些可选的Java逻辑。然而,在某些情况下,特别是在依赖于@ResponseBody
方法而不是视图分辨率的情况下,直接设置响应的状态并可选地将错误内容写入响应主体可能会更为方便。
你可以用@ExceptionHandler
方法来做到这一点。当在控制器内声明时,这种方法适用于由@RequestMapping
该控制器(或其任何子类)的方法引发的异常。您也可以@ExceptionHandler
在@ControllerAdvice
类中声明一个方法, 在这种情况下,它可以处理@RequestMapping
来自多个控制器的方法的异常。下面是一个控制器局部@ExceptionHandler
方法的例子 :
@Controller public class SimpleController { // @RequestMapping方法省略... @ExceptionHandler(IOException.class) public ResponseEntityhandleIOException(IOException ex){ // prepare responseEntity return responseEntity; }}
该@ExceptionHandler
值可以设置为一个异常类型的数组。如果抛出与列表中的一个类型匹配的异常,@ExceptionHandler
则将调用注释与匹配的方法。如果未设置注释值,则使用列为方法参数的异常类型。
与使用@RequestMapping
注释注释的标准控制器方法非常相似,方法的方法参数和返回值@ExceptionHandler
可以是灵活的。例如,HttpServletRequest
可以在Servlet环境中访问。返回类型可以是一个String
,它被解释为一个视图名称,一个ModelAndView
对象,一个ResponseEntity
或者你也可以添加@ResponseBody
一个方法返回值转换为消息转换器并写入响应流。
Spring MVC可能会在处理请求时引发许多异常。根据需要, SimpleMappingExceptionResolver
可以轻松地将任何异常映射到默认错误视图。但是,在使用以自动方式解释响应的客户端时,您将需要在响应中设置特定的状态代码。根据引发的异常,状态代码可能会指示客户端错误(4xx)或服务器错误(5xx)。
将DefaultHandlerExceptionResolver
Spring MVC异常转换为特定的错误状态代码。它默认注册了MVC命名空间,MVC Java配置,还有DispatcherServlet
(即不使用MVC命名空间或Java配置时)。下面列出了这个解析器处理的一些异常和相应的状态代码:
例外 | HTTP状态码 |
---|---|
BindException | 400(不良要求) |
ConversionNotSupportedException | 500内部服务器错误) |
HttpMediaTypeNotAcceptableException | 406(不可接受) |
HttpMediaTypeNotSupportedException | 415(不支持的媒体类型) |
HttpMessageNotReadableException | 400(不良要求) |
HttpMessageNotWritableException | 500内部服务器错误) |
HttpRequestMethodNotSupportedException | 405(不允许方法) |
MethodArgumentNotValidException | 400(不良要求) |
MissingPathVariableException | 500内部服务器错误) |
MissingServletRequestParameterException | 400(不良要求) |
MissingServletRequestPartException | 400(不良要求) |
NoHandlerFoundException | 错误(404) |
NoSuchRequestHandlingMethodException | 错误(404) |
TypeMismatchException | 400(不良要求) |
将DefaultHandlerExceptionResolver
通过设置响应的状态透明地工作。但是,您的应用程序可能需要为每个错误响应添加开发人员友好的内容,例如提供REST API时,不会将任何错误内容写入响应正文。你可以准备一个ModelAndView
和渲染通过视图解析错误内容-通过配置,即ContentNegotiatingViewResolver
,MappingJackson2JsonView
,等等。但是,您可能更喜欢使用@ExceptionHandler
方法。
如果您喜欢通过@ExceptionHandler
方法编写错误内容,可以ResponseEntityExceptionHandler
改为扩展 。这是提供处理标准Spring MVC异常和返回@ControllerAdvice
的@ExceptionHandler
方法的类的方便基础 ResponseEntity
。这允许您自定义响应并使用消息转换器写入错误内容。有关ResponseEntityExceptionHandler
详细信息,请参阅 javadocs。
一个@RestController
可以使用@ExceptionHandler
返回一个方法, ResponseEntity
在响应的主体既提供响应状态和错误的详细信息。这些方法也可以被添加到@ControllerAdvice
用于子集或所有控制器的异常处理的类中。
一个常见的要求是在响应的正文中包含错误详细信息。Spring不会自动执行此操作(尽管Spring Boot),因为响应主体中的错误详细信息的表示是特定于应用程序的。
希望在响应体中实现具有错误详细信息的全局异常处理策略的应用程序应考虑扩展抽象基类ResponseEntityExceptionHandler
,为Spring MVC引发的异常提供处理,并提供钩子来自定义响应体以及处理其他异常。只需将扩展类声明为一个Spring bean并用它进行注释@ControllerAdvice
。有关详细信息,请参阅ResponseEntityExceptionHandler
。
可以注释业务异常@ResponseStatus
。当异常提出时,ResponseStatusExceptionResolver
通过相应地设置响应的状态来处理它。默认情况下,DispatcherServlet
寄存器 ResponseStatusExceptionResolver
,它是可用。
当响应的状态设置为错误状态代码并且响应的正文为空时,Servlet容器通常会呈现HTML格式的错误页面。要自定义容器的默认错误页面,可以在其中声明一个<error-page>
元素web.xml
。直到Servlet 3,该元素必须映射到特定的状态代码或异常类型。从Servlet 3开始,不需要映射错误页面,这意味着指定的位置定制了默认的Servlet容器错误页面。
/ error
请注意,错误页面的实际位置可以是容器中的JSP页面或其他一些URL,包括通过一种@Controller
方法处理的页面:
编写错误信息时,HttpServletResponse
可通过控制器中的请求属性访问状态码和设置的错误信息 :
@Controller public class ErrorController { @RequestMapping(path =“/ error”,produce = MediaType.APPLICATION_JSON_UTF8_VALUE) @ResponseBody public Maphandle(HttpServletRequest request){ Map map = new HashMap (); map.put(“status”,request.getAttribute(“javax.servlet.error.status_code”)); map.put(“reason”,request.getAttribute(“javax.servlet.error.message”)); 返回地图; }}
或在JSP中:
<%@ page contentType = “application / json” pageEncoding = “UTF-8” %> { status:<% = request.getAttribute(“javax.servlet.error.status_code”) %>, reason:<% = request。 getAttribute(“javax.servlet.error.message”) %> }
在项目提供的功能,以防止恶意攻击Web应用程序。请参阅 , 以及 。请注意,使用Spring Security来保护应用程序并不一定需要所有功能。例如,CSRF保护可以通过添加CsrfFilter
和配置 CsrfRequestDataValueProcessor
来添加。参见 示例。
另一个选择是使用专门用于Web Security的框架。 是一个这样的框架,并与Spring MVC集成。
对于很多项目,坚持既定的约定和合理的默认值就是它们(项目)所需要的,而Spring Web MVC现在已经明确地支持约定的配置。这意味着如果您建立了一组命名约定等等,您可以大幅度减少设置处理程序映射,查看解析器,ModelAndView
实例等所需的配置量 。这对于快速原型,并且如果您选择将其推向生产,还可以在代码库中提供一定程度的(始终如一的)一致性。
公约超配置支持解决了MVC的三个核心领域:模型,视图和控制器。
该ControllerClassNameHandlerMapping
班是一个HandlerMapping
使用惯例来确定请求的URL和之间的映射实现Controller
是要处理这些请求的情况。
考虑以下简单的Controller
实现。特别注意 课程名称。
public class ViewShoppingCartController implements Controller { public ModelAndView handleRequest(HttpServletRequest request,HttpServletResponse response){ //实现对于这个例子来说不是很重要... }}
以下是相应的Spring Web MVC配置文件的代码段:
<! - 根据需要注入依赖关系... - >
在ControllerClassNameHandlerMapping
找出所有的处理程序(或 Controller
在其应用上下文定义的)豆和剥离Controller
掉,以限定其处理程序映射的名称。因此,ViewShoppingCartController
映射到 /viewshoppingcart*
请求URL。
我们再来看一些更多的例子,使中心思想变得熟悉。(注意URL中的全部小写,与骆驼Controller
类的类名相反)。
WelcomeController
映射到/welcome*
请求URLHomeController
映射到/home*
请求URLIndexController
映射到/index*
请求URLRegisterController
映射到/register*
请求URL在MultiActionController
处理程序类的情况下,生成的映射稍微复杂一点。以下Controller
示例中的名称被假定为实现MultiActionController
:
AdminController
映射到/admin/*
请求URLCatalogController
映射到/catalog/*
请求URL如果你按照你的命名的惯例Controller
实现的 xxxController
,将ControllerClassNameHandlerMapping
节省您定义和维护一个潜在的沉闷一长串SimpleUrlHandlerMapping
(或类似的东西)。
本ControllerClassNameHandlerMapping
类扩展AbstractHandlerMapping
基类,所以你可以定义HandlerInterceptor
实例和一切,就像你与许多其他HandlerMapping
的实现。