SpringMVC知识点整理

1、概述

在了解SpringMVC之前,我们需要知道MVC架构设计模式以及J2EE的三层架构设计,MVC模式如下图所示:

三层结构如下图所示:
在这里插入图片描述

SpringMVC则是基于 MVC 设计理念的 Web 框架,集成于SpringFramework中。SpringMVC也就是我们熟知的’SSM’中的’S’,它通过一套 MVC 注解,让 POJO 成为处理请求的控制器,而无须实现任何接口,并且支持REST风格URL,整体采用松散耦合、可插拔组件结构,比其他 MVC 框架更具扩展性和灵活性。

SpringMVC 基于 Servlet 规范实现,所以基于 SpringMVC 的项目,需要 Servlet 容器服务器支撑,比如常用的tomcat。

2、快速上手

1)环境准备

首先需要导入项目所需要的maven依赖,包括spring-web、spring-webmvc、servlet-api、jsp相关依赖、IoC相关依赖。基本环境为Spring5.1.3.RELEASE,tomcat8。

2)逻辑代码

①控制器类

首先需要创建一个用于分发请求的控制器类,在SpringMVC中只要给类标上@Controller 注解即表示这是个控制器类,之后使用@RequestMapping 注解在方法上标明请求路径,方法的返回值即是对应的视图页面(这里以jsp为例)。

package top.jtszt.controller;
@Controller
public class HelloController {
    @RequestMapping("/hello")
    public String hello(){
        return "/WEB-INF/pages/hello.jsp";
    }
}
②配置相关

有了@Controller注解标注的控制器类之后,我们还需要将组件扫描加入IoC容器中:

<!-- SpringMVC-config.xml -->
<beans ...>
	<context:component-scan base-package="top.jtszt"/>
</beans>

SpringMVC 的核心控制器是一个 Servlet 叫 DispatcherServlet 。所以我们需要在 web.xml中配置它,并且需要配置对应的Spring配置文件指向与启动级别:

<servlet>
	<servlet-name>hello</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:SpringMVC-config.xml</param-value>
	</init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
	<servlet-name>hello</servlet-name>
	<url-pattern>/</url-pattern>
</servlet-mapping>

这里如果不显式配置Spring配置文件路径,默认会按照classpath:{servlet-name}-servlet.xml去查找配置文件,并且需要注意的是这里servlet的拦截路径为/,如果拦截为/*则表示连同jsp页面都拦截,而jsp是我们用来展示的,不可以进行拦截。

之后是配置tomcat以及jsp页面
在这里插入图片描述

③测试

配置完毕,我们可以启动项目,在浏览器查看效果。
在这里插入图片描述

3、视图解析器

1)概述

SpringMVC中的视图解析器(ViewResolver)是前端控制器中九大组件之一,用于将逻辑视图转化为物理视图。SpringMVC会先将控制器类的 String/ModelAndView/View 类型的返回值都转化为ModelAndView 类型,之后视图解析器把它解析为具体的View 类型的视图对象。

ViewResolver接口中有几个主要的实现类,可以实现视图解析功能:

  • BeanNameViewResolver:将视图名解析为一个Bean
  • InternalResourceViewResolver:将视图名解析为一个URL文件
  • jasperReportsViewResolver:将视图名解析为报表文件对应的URL

我们可以选择一种视图解析器或混用多种视图解析器,并且每个视图解析器都实现了 Ordered 接口并开放出一个 order 属性,可以通过 order 属性指定解析器的优先顺序,order 越小优先级越高

2)解析前后缀

在对jsp页面的解析中一般使用InternalResourceViewResolver ,最常见的是配置视图解析的前缀和后缀,对于xml配置来说直接配置bean即可:

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<property name="prefix" value="/WEB-INF/pages/"/>
	<property name="suffix" value=".jsp"/>
</bean>

其中 prefix 代表解析器拼串开头,suffix 代表解析器拼串结尾,这样配置之后,我们在控制器类的返回只需要返回hello即可。

/WEB-INF/pages/hello.jsp  --->  hello

3)拼串与否?

这里还涉及到一个返回值拼串与否的问题,正常来说返回值都是按照设定的前缀和后缀进行拼串之后返回视图,而如果出现以下两种情况,那么视图解析器将不会拼前后缀:

  • 返回值以forward:开头,代表的是直接转发
  • 返回值以redirect:开头,代表的是重定向

除了以上两种情况,其他的都会进行拼串处理。

4、数据传递与参数绑定

现在已经可以实现请求分发与响应功能了,那么如果页面的数据需要在请求时才渲染要怎么做,换句话说,数据传递要怎么办?这时可以将数据放置在request域中,在页面去取出对应的数据。传统的引入HttpServletRequest传递的方式就不介绍了,这里主要介绍SpringMVC中提供了数据传递功能。

1)ModelAndView

ModelAndView 能封装数据和返回跳转的视图信息,在其中存储的数据实际上是存储在request域中。可以在参数位置传入ModelAndView对象,之后调用 setViewName 设置视图名,调用addObject 设置放置的数据信息(键值对) 。

@RequestMapping("/hello")
public ModelAndView hello(ModelAndView mv) {
    mv.addObject("msg", "this is msg from modelAndView");
    mv.setViewName("hello");
    return mv;
}

也可以手动创建一个ModelAndView对象,传入的参数为要跳转的视图名,之后调用该对象的addObject方法设置数据信息。

@RequestMapping("/hello")
public ModelAndView hello(){
	ModelAndView modelAndView = new ModelAndView("hello");
	modelAndView.addObject("msg", "this is msg from modelAndView");
	return modelAndView;
}

之后在jsp页面 ${msg} 取出即可。

当然这里可传入的不只是简单信息,也可以传入Map、List等,在jsp页面通过c:foreach取出即可。

2)Map与Model

Spring MVC 在调用方法前会创建一个隐含的模型对象作为模型数据的存储容器,如果方法的入参为 Map 或 Model 类型,Spring MVC 会将隐含模型的引用传递给这些入参。

在方法体内,我们可以通过入参对象访问到模型中的所有数据,也可以向模型中添加新的属性数据。

实际上传入无论是Model、Map还是ModelMap最终都会被转化为BindingAwareModelMap对象。

@RequestMapping("/hello")
public String hello(ModelMap modelMap, 
                      Map<String,Object> map, 
                      Model model){
	modelMap.addAttribute("msg","hello modelMap");
	map.put("msg2", "hello map");
	model.addAttribute("msg3","hello model");
	return "hello";
}

3)参数收集

① 原生数据类型

这种类型的数据除了可以在入参位置声明 HttpServletRequest ,之后通过request.getParameter() 获取参数之外,还可以直接在入参位置传入需要获取的参数。

@RequestMapping("/hello")
public String hello(HttpServletRequest request) {
    String name = request.getParameter("name");
    System.out.println("name is "+ name);
    return "result";
}
@RequestMapping("/hello")
public String hello(String name) {
    System.out.println("name is "+ name);
    return "result";
}

当我们访问 /hello?name="test" 时就可以在控制器中成功打印结果。

②模型类型

假设有一个Employee对象,包含id和name属性,现在我们需要将参数中的name与id获取之后包装成Employee对象,这时我们不需要手动进行包装。在SpringMVC中,如果请求的参数名称,与模型类中的属性一一对应,那么SpringMVC 会按请求参数名和 POJO 属性名进行自动匹配,自动为该对象填充属性值,支持级联属性。

@RequestMapping("/hello")
public String hello(Employee employee) {
    System.out.println("Employee is "+ employee);
    return "result";
}

当我们访问 /hello?id=1&name="test" 时就可以在控制器中成功打印结果。

③使用注解

在SpringMVC中还可以通过 @RequestParam 注解获取参数,这种获取方式不要求用户传参与规定参数一一对应,可以指定参数名获取。

@RequestMapping("/hello")
public String hello(@RequestParam("username")String name) {
    System.out.println("name is "+ name);
    return "result";
}

这时访问 /hello?username="test" 时也可以在控制器中成功打印结果。

现在我们解决了参数名不一致的获取问题,那么如果不带参数访问呢?实测会抛异常,因为默认是需要传入参数的。为此该注解还有一个属性:required ,它表示请求参数中是否必须携带指定的参数。默认值是 true 。还有一个属性 defaultValue,它可以指定参数不传递时的默认值。

@RequestMapping("/hello")
public String hello(@RequestParam(value = "username",required = false,defaultValue = "default")String name) {
    System.out.println("name is "+ name);
    return "result";
}

这时访问 /hello 也不会抛异常,并且会打印default。

④其他注解

除了获取参数的注解,SpringMVC中还有用于获取header的某个属性值的注解 @RequestHeader,以及获取cookie中的某个属性值的注解 @CookieValue

@RequestMapping("/hello")
public String hello(@RequestHeader("User-Agent")String userAgent,
                    @CookieValue("JSESSIONID")String jid){}

4)乱码问题

在接收POST请求参数时,可能会出现中文乱码的问题,这是编码集不一致导致的。SpringMVC中提供了CharacterEncodingFilter 用于解决乱码问题,它有三个属性,encoding 代表编码格式, forceRequestEncoding 代表对请求设置编码, forceResponseEncoding 代表对响应设置编码。这里我们只需要在web.xml中配置好这个filter,并且设置拦截路径为 /* 即可。

<web-app>
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

如果是GET请求出现乱码问题,那应该考虑的是Tomcat的编码设置问题。

5、请求映射

SpringMVC中使用 @RequestMapping 注解为控制器指定可以处理哪些 URL 请求,这个注解可以标注在类上,也可以标注在方法上。如果@RequestMapping("/admin") 标注在类上,那么该类下的所有方法都须以 /admin 为前缀才可以访问。

//如:
@Controller
@RequestMapping("/admin")
public class MyController {}
@RequestMapping("/hello")
public String hello() {return "hello"}

那么如果要访问到hello.jsp页面必须通过 /admin/hello 访问。

此外 @RequestMapping 中也提供了对于请求头的一些限制属性,其中 value 表示请求URL,method 表示请求方法,params 代表请求参数,heads 表示请求头,他们之间是与的关系,联合使用多个条件可让请求映射更加精确化。

此外SpringMVC也提供了几个限定请求方式的注解:

  • @GetMapping(“xx”) :限定get方式
  • @PostMapping(“xx”) :限定post方式
  • @PutMapping(“xx”):限定put方式
  • @DeleteMapping(“xx”):限定delete方式

6、RESTful风格

REST即Representational State Transfer(表现层状态转化),是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。

每发出一个请求,就代表了客户端和服务器的一次交互过程。HTTP协议,是一个无状态协议,即所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生“状态转化”。而这种转化是建立在表现层之上的,所以就是 “表现层状态转化”。

具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源。而RESTful风格就是将这四种方式与资源的操作联系起来。

举个例子,假设现在要实现员工信息的增删改查,那RESTful风格的url就可以是下面这样的:

  • 员工列表:/emps get方法
  • 员工添加:/emp post方法
  • 员工修改:/emp/{id} put方法
  • 员工删除:/emp/{id} delete方法
  • 员工查询:/emp/{id} get方法

这里的{id}代表的是动态的id传入,在SpringMVC中可以使用@PathVariable("id")标注在Controller的参数位置来获取URL中的指定的值。

@RequestMapping(value = "/emp/{id}")
public String getEmp(@PathVariable("id")Integer id){...}

这样当我们以GET方式访问/emp/1 时,在getEmp方法中就可以拿到具体的id。

7、返回json数据

1)导包、配置

SpringMVC中整合了主流的json转换工具,默认使用 jackson 进行 json 格式转换。使用jackson需要先导入maven依赖,包括:jackson-core、jackson-annotations、jackson-databind。导入依赖之后还需要配置json转换器,一种比较简单的方法是在SpringMVC的配置文件中加入注解驱动配置,也可以手动配置。

<!-- 开启注解驱动 -->
<mvc:annotation-driven/>

2)@ResponseBody

此外还需要在Controller对应方法上标注 @ResponseBody ,表明方法返回信息作为响应体返回(由jackson包封装为json格式)。

//假数据做示例
Employee emp = new Employee(1, "test01", "123@163.com");
@ResponseBody
@RequestMapping("/getinfo")
public Employee getInfo(){
    return emp;
}

还可以将返回值类型设置成ResponseEntity<T> ,之后在方法中new该对象,以此方法可以设置响应体、响应头以及响应状态码。

@RequestMapping("/getinfo2")
public ResponseEntity<Employee> getInfo2(){
    return new ResponseEntity<>(emp,HttpStatus.OK);
}

3)@RestController

当一个Controller类中的所有映射方法都要返回json数据,那么在每个方法上都标注上@ResponseBody 注解就不太现实了。这时可以直接在类上标注@ResponseBody ,也可以使用@RestController注解标注在Controller类上,其相当于@Controller@ResponseBody

4)@RequestBody

@ResponseBody相对的还有 @RequestBody,它可以把请求体中json格式的数据封装成指定的对象。

@PostMapping("/saveinfo")
@ResponseBody
public void saveInfo(@RequestBody Employee employee) {
	System.out.println(employee);
}

当我们POST此路径并带上json格式的数据时,其会将请求体中的数据(如果存在配对)封装成Employee对象。

除此之外还可以将参数声明为 HttpEntity<T> ,这样声明的参数除了可以获得请求体还可以获得请求头。

@PostMapping("/saveinfo2")
@ResponseBody
public void saveInfo(HttpEntity<Employee> httpEntity) {
	Employee emp = httpEntity.getBody();
	System.out.println(emp);
}

8、静态资源配置

1)默认处理器

如果我们尝试引入项目路径下的js或者css,会发现并不能访问到。这个问题源于我们将SpringMVC中的DispatcherServlet 请求映射配置为 /,这时静态资源的请求也会被当成一个普通请求处理,因找不到对应处理器而导致错误。

这时可以在 SpringMVC 的配置文件中配置默认处理器解决静态资源的问题:

<mvc:default-servlet-handler/> 

起作用的是 SpringMVC 上下文中定义的 DefaultServletHttpRequestHandler,它会对进入 DispatcherServlet 的请求进行筛查,如果发现是没有经过映射的请求,就将该请求交由 WEB 应用服务器默认的 Servlet 处理;如果不是静态资源的请求,才由 DispatcherServlet 继续处理
一般 WEB 应用服务器默认的 Servlet 的名称都是 default。若所使用的 WEB 服务器的默认 Servlet 名称不是 default,则需要通过 default-servlet-name 属性显式指定

2)解析配置

除了使用默认处理器解析之外,还可以显式地配置上对于静态资源的解析。通过xml<mvc:resources>标签进行配置,其中cache-period 表示缓存时长。

<mvc:resources mapping="/js/**" location="/scripts/" />
<mvc:resources mapping="/css/**" location="/css/" cache-period="2592000" />

这时js文件的访问会解析到项目的scripts目录,css会解析到css目录。

9、文件上传与下载

1)文件上传

①导依赖

首先需要导入文件上传所需的依赖,包括commons-fileupload,commons-io,以及IoC、MVC相关依赖。

②写配置

在基于xml的配置中,需要先配置一个文件上传处理器(CommonsMultipartResolver),其中的defaultEncoding 可以用于设置文件的编码,maxUploadSize 可以用于设置文件大小的最大值,要注意的是配置的bean的id必须为multipartResolver,否则无法识别到。

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
	<property name="defaultEncoding" value="UTF-8"/>
	<property name="maxUploadSize" value="#{1024*1024*20}"/>
</bean>
③接收文件

现在写一个jsp页面加上包含上传文件的表单,访问路径为项目下的/upload:

<form action="${ctp}/upload" method="post" enctype="multipart/form-data">
    文件上传:<input type="file" name="uploadFile"/>
    <input type="submit" value="提交"/>
</form>

在Controller层我们写一个用于处理/upload请求的方法,这里传入一个 MultipartFile 类型的参数用于接收上传的文件:

@Controller
public class UploadController {
    @PostMapping("/upload")
    public String upload(@RequestParam("uploadFile")MultipartFile file){...}
}
④保存文件

MultipartFile有一个 getOriginalFilename 方法可以用于获取文件原始文件名,如果将文件保存到本地可调用MultipartFile的 transferTo() ,传入new File(xx) 之后即可持久化到本地。如果想保存到数据库,可获取文件的字节码存入。

file.transferTo(new File("I:\\upload\\"+filename));
byte[] bytes = file.getBytes();

2)文件下载

文件的下载需要借助之前写到的ResponseEntity<> 将文件的byte[] 数据放置在其中,之后设置响应头然后返回这个对象,这里演示的是图片的展示。

@Controller
public class DownloadController {
    @RequestMapping("/getImg")
    public ResponseEntity<byte[]> getImg() throws IOException {
        String filename = "I:\\upload\\mvc.png";
        byte[] bytes = Files.readAllBytes(Paths.get(filename));
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        headers.setContentDispositionFormData("attachment", filename);
        return new ResponseEntity<>(bytes, headers, HttpStatus.CREATED);
    }

}

在jsp页面用一个img标签接收即可。

<img src="${pageContext.request.contextPath}/getImg">

10、异常处理器

到目前为止,我们所有的异常都是直接抛出去,当我们用get方法访问一个限制POST方法访问的路径时,得到的是Tomcat的异常页面,这对于用户体验十分不友好并且存在代码安全性问题。因此我们需要让DispatcherServlet(前端控制器),去对异常进行处理。

Spring MVC 通过 HandlerExceptionResolver 处理程序的异常,包括 Handler 映射、数据绑定以及目标方法执行时发生的异常,它有四个主要的实现类可以对异常进行处理:

  • ExceptionHandlerExceptionResolver
  • ResponseStatusExceptionResolver
  • DefaultHandlerExceptionResolver
  • SimpleMappingExceptionResolver

1)ExceptionHandlerExceptionResolver

这个异常解析器主要是处理标明 @ExceptionHandle注解的异常,而@ExceptionHandler 是SpringMVC提供的注解,用于声明式地捕获指定的异常。该注解的标注可分为两种类型:一种是在标明@controller的类中配置,也称为本类配置;一种是在单独的类中配置,也称为全局配置。

①本类配置

在类中创建一个方法标注上@ExceptionHandle(value="xx")注解,value属性指明需要捕获的异常,该方法的返回值会被视图解析器解析,如果想获取异常信息可以在入参位置传入异常类型。

@ExceptionHandler(value = {NullPointerException.class} )
public ModelAndView exceptionHandle(NullPointerException npe){
    npe.printStackTrace();
    ModelAndView mv = new ModelAndView("error");
    mv.addObject("exception",npe.getMessage());
    return mv;
}
②全局配置

创建一个用于处理异常的类,在类上标注 @ControllerAdvice ,表明这个类用来集中处理异常;接下在定义异常处理的方法,步骤与本类配置一致。

@ControllerAdvice
public class ExceptionController {
    @ExceptionHandler(value = {NullPointerException.class})
    public ModelAndView exceptionHandle(NullPointerException npe){
        npe.printStackTrace();
        ModelAndView mv = new ModelAndView("error");
        mv.addObject("exception",npe.getMessage());
        return mv;
    }
}

如果全局和本类都有配置异常处理,那么本类的优先;如果捕获异常的处理器有多个,那么精确的优先。

2)ResponseStatusExceptionResolver

其用于处理标明 @ResponseStatus 注解的异常,可以自定义错误代码和错误原因,其中reason属性为错误原因,value属性与code为HttpStatus类型的错误代码。该注解主要是标注在自定义异常类上,之后在代码逻辑异常处理处抛出该错误类型,SpringMVC捕获之后就会调用到这个处理器。

//自定义一个异常类
@ResponseStatus(reason = "用户访问异常", code = HttpStatus.NOT_ACCEPTABLE)
class UserNotFoundInAdminException extends RuntimeException{
    static final long serialVersionUID = 1L;
}
@RequestMapping("/admin")
public String loginAdmin(@RequestParam("user")String user){
	if(user!="admin"){
        throw new UserNotFoundInAdminException();
    }
    ....
}

3)SimpleMappingExceptionResolver

用于配置简单的异常解析,可以在SpringMVC的配置文件中配置。其中class为class为SimpleMappingExceptionResolver的全类名,exceptionMappings 属性的key为要处理的异常全类名,value为异常后跳转的视图名;exceptionAttribute 属性的value值为异常信息存储在域的key。

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="exceptionMappings">
        <props>
        	<prop key="java.lang.NullPointerException">error</prop>
        </props>
    </property>
    <property name="exceptionAttribute" value="ex"/>
</bean>

4)DefaultHandlerExceptionResolver

用于处理SpringMVC自带的异常,当没有找到其他的异常解析器时,就会来到这个异常解析器解析。

11、拦截器

1)概述

首先需要区分一些拦截器和过滤器的概念。拦截器是SpringMVC中的一个API设计,而过滤器是Servlet中的一个组件。所有经过Servlet容器的请求都可以被过滤器截获到,而拦截器只对经过DispatcherServlet 处理的请求有效。此外,拦截器通过IoC统一管理,所以可以进行任意注入。

2)配置

拦截器的核心接口是HandleInterceptor ,自定义的拦截规则要实现该接口,该接口中有三个主要方法,代表着三个执行时机:

  • preHandle:目标方法运行前,返回true则代表放行;
  • postHandle:目标方法运行后;
  • afterCompletion:视图响应之后;
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, 
                             Object handler) throws Exception {
        System.out.println("preHandle.....");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response,
                           Object handler,
                           ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle.....");
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler,
                                Exception ex) throws Exception {
        System.out.println("afterCompletion.....");
    }
}

接着我们需要将拦截器注册到IoC容器中,使用的是<mvc:interceptors>标签,这也说明可以注册多个拦截器;<mvc:interceptor> 标签体内有一个 <mvc:mapping> 用于配置拦截路径,<bean>用于配置拦截器:

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/"/>
        <bean class="top.jtszt.interceptor.MyInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

3)拦截流程

从上面我们可以看到拦截器一共有三个运行的时机:目标方法运行前、目标方法运行后、视图响应之后。在配置了拦截器的请求流程大致为:

  1. preHandle
  2. 目标方法运行
  3. postHandle
  4. 视图响应
  5. afterCompletion

4)多拦截器

假设配置了两个拦截器,按照配置顺序第一个记为interceptor1,第二个记为interceptor2。
在这里插入图片描述

12、跨域问题

跨域指的是当前发起请求的域与该请求指向的资源所在的域不一样,这里的域概念表示的是协议、域名、端口(同源策略)。只有当这三者都相同才属于同一个域。当这三个任一个不相同则会引发跨域问题。

SpringMVC中可以在允许跨域的方法上标注 @CrossOrigin 注解,该注解上可以指定跨域范围。这个注解实际上是给响应头中添加了一个 Access-Control-Allow-Origin:


参考资料:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值