书城项目(持续更新中)

解决跨域的5种方法

什么时跨域

什么是跨域 浏览器从⼀个域名的⽹⻚去请求另⼀个域名的资源时,协议、域名(主机)、端⼝任⼀不同,都是跨域。在前后端分离的模式下,前后端的域名是不⼀致的,此时就会发⽣跨域访问问题。跨域出于浏览器的同源策略限制。

对于 CORS的跨域请求,主要有以下⼏种⽅式可供选择

返回新的CorsFilter

重写 WebMvcConfigurer

使⽤注解 @CrossOrigin

⼿动设置响应头 (HttpServletResponse)

⾃定web filter 实现跨域

方法1  返回新的 CorsFilter

在任意配置类,返回⼀个新的 CorsFilter 的对象 ,并添加映射路径和具体的 CORS 配置路径

@Configuration
public class GlobalCorsConfig {
@Bean
public CorsFilter corsFilter() {
//1. 添加 CORS配置信息
CorsConfiguration config = new CorsConfiguration();
//放⾏哪些原始域
config.addAllowedOrigin("*");
//放⾏哪些请求⽅式
config.addAllowedMethod("*");
//放⾏哪些原始请求头部信息
config.addAllowedHeader("*");
 // 添加最⼤存活时间 单位:秒
config.setMaxAge(1800L);
//2. 添加映射路径
UrlBasedCorsConfigurationSource corsConfigurationSource = new
UrlBasedCorsConfigurationSource();
corsConfigurationSource.registerCorsConfiguration("/**",config);
//3. 返回新的CorsFilter
return new CorsFilter(corsConfigurationSource);
 }
}

方法2 重写 WebMvcConfigurer

@Configuration
public class CorsConfig implements WebMvcConfigurer {
public void addCorsMappings(CorsRegistry registry){
registry.addMapping("/*/**")
 .allowedHeaders("*")
 .allowedMethods("*")
 .maxAge(1800L)
 .allowedOrigins("*");
 }
}

方法3  使⽤注解 @CrossOrigin

这个注解可以⽤于两个地⽅:

类:表示该类的所有⽅法允许跨域。

⽅法:表示当前⽅法允许跨域 

@RestController
@CrossOrigin(origins = "*")
public class DailyblueController {
@RequestMapping("/a")
public String a() {
return "This is DailyblueController`a method!";
 }
@RequestMapping("/b")
@CrossOrigin(origins = "http://localhost:8081") //指定具体ip允许跨域
public String b() {
return "This is DailyblueController`b method!";
 }
@RequestMapping("/c")
public String c() {
return "This is DailyblueController`c method!";
 }
}

其中 @CrossOrigin 中的2个参数:

origins:允许可访问的域列表。 maxAge:准备响应前的缓存持续的最⼤时间(以秒为单位) 

方法4 ⼿动设置响应头 

使⽤ HttpServletResponse 对象添加响应头(Access-Control-Allow-Origin)来授权原始域,这⾥ Origin 的值也可以 设置为 “*”,表示全部放⾏。 

@RequestMapping("/index")
public String index(HttpServletResponse response) {
response.addHeader("Access-Control-Allow-Origin","*");
return "index";
}

方法5 使⽤⾃定义 filter 实现跨域 

可以⾃定义⼀个多滤器,对请求的数据头进⾏过来,允许其他域请求访问: 

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebFilter("/*")  //  过滤器解决跨域
public class HttpFilter1 extends javax.servlet.http.HttpFilter {
    @Override
    protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "x-requested-with,content-type");
        chain.doFilter(request, response);
    }
}

 此时必须让启动项扫描到过滤器

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
//@ServletComponentScan("com.jiazhong.bookstore.filter")
@EnableCaching
public class BookStoreApplication {
    public static void main(String[] args) {
        SpringApplication.run(BookStoreApplication.class, args);
    }
}

 在项目中应用到 Swagger

第一步:导入依赖包

 <!--测试用的-->
        <!--添加Knife4j依赖-->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
            <version>4.1.0</version>
        </dependency>

第二步 设置配置类,进行配置 

Knife4jConfig .java

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;

@Configuration
@EnableSwagger2WebMvc
// http://localhost:8080/doc.html
public class Knife4jConfig {//对于配置类要求可以看懂即可,不用反复去写,将来可以CV
    //配置Swagger2的Docket的Bean实例
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                // apiInfo():配置 API 的一些基本信息,比如:文档标题title,文档描述description,文档版本号version
                .apiInfo(apiInfo())
                // select():生成 API 文档的选择器,用于指定要生成哪些 API 文档
                .select()
                // apis():指定要生成哪个包下的 API 文档
                .apis(RequestHandlerSelectors.basePackage("com.jiazhong.controller"))
                // paths():指定要生成哪个 URL 匹配模式下的 API 文档。这里使用 PathSelectors.any(),表示生成所有的 API 文档。
                .paths(PathSelectors.any())
                .build();
    }
    //文档信息配置
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                // 文档标题
                .title("书城开发接口文档")
                // 文档描述信息
                .description("书城开发接口文档")
                // 文档版本号
                .version("1.0")
                .build();
    }
}

第三步 常用注解

通过注解可以控制生成的接口文档,使接口文档拥有更好的可读性,常用注解如下:

注解说明
@Api用在类上,例如Controller,表示对类的说明
@ApiModel用在类上,例如entity、DTO、VO
@ApiModelProperty用在属性上,描述属性信息
@ApiOperation用在方法上,例如Controller的方法,说明方法的用途、作用
@RestController
@RequestMapping("/book")
//@CrossOrigin
@Api("图书接口")
public class BookController {
    @Autowired
    private BookService bookService;
    @Autowired
    private BookServiceImpl bookServiceImpl;
    @Autowired
    private CategoryServiceImpl categoryService;

    @GetMapping("/list")
    @ApiOperation("全部图书")
    public JsonResult list() {
        List<Book> list = bookService.list();
        return ResultTool.success(list);
    }
}

全局异常处理 

在项⽬开发中出现异常时很平常不过的事情,我们处理异常也有很多种⽅式。可能如下

public int div(int a ,int b){
int c = 0;
try{
c = a / b;
 }catch (Exception ex){
ex.printStackTrace();
 }
return c;
}

这就引出了我们要介绍的全局异常处理⽅法,主要有两种⽅式:

1. 使⽤ @RestControllerAdvice 和 @ExceptionHandler 注解

2. 使⽤ ErrorController 类来实现 

@RestControllerAdvice ⽅式 

全局异常处理类

书写⼀个全局异常处理类,代码如下 

// 注解@RestControllerAdvice表示这是⼀个控制器增强类,当控制器发⽣异常且符合类中定义的拦截异常类,将会
被拦截。
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
// 对那些异常进⾏拦截
@ExceptionHandler(Exception.class)
public JsonResult globalException(HttpServletResponse response, Exception e) {
log.info("进⼊了全局异常处理⽅法");
log.info("异常信息是:{}", e.getMessage());
return ResultTool.fail(e.getMessage());
 }
}

 控制器

@RequestMapping("/second")
@RestController
public class SecondController {
@RequestMapping("/login")
public String login() {
System.out.println("aaa");
return "login";
 }
// 这个⽅法会出现错误
@RequestMapping("/b")
public String b() {
System.out.println("bbb");
System.out.println(9/0);
return "b";
 }
}

启动 

启动并调⽤错误⽅法,⻚⾯效果: 

控制台效果 

使⽤ ErrorController 类 

系统默认的错误处理类为 BasicErrorController,当出现错误时显示默认的错误⻚⾯。这⾥编写⼀个⾃⼰的错误处 理类,上⾯默认的处理类将不会起作⽤。 getErrorPath()返回的路径服务器将会重定向到该路径对应的处理类,本例中为error⽅法。 

@RestController
@Slf4j
public class HttpErrorController implements ErrorController {
private final static String ERROR_PATH = "/error";
@RequestMapping(path = ERROR_PATH)
public JsonResult error(HttpServletRequest request, HttpServletResponse response) {
log.info("访问/error" + " 错误代码:" + response.getStatus());
return ResultTool.fail();
 }
public String getErrorPath() {
return ERROR_PATH;
 }
}

还是⽤刚才的错误控制器⽅法,继续启动并访问,浏览器效果 

控制台效果 

 两者区别

1. 注解 @RestControllerAdvice ⽅式只能处理控制器抛出的异常。此时请求已经进⼊控制器中。 2. 类 ErrorController ⽅式可以处理所有的异常,包括未进⼊控制器的错误,⽐如404,401等错误 3. 如果应⽤中两者共同存在,则 @RestControllerAdvice ⽅式处理控制器抛出的异常,类 ErrorController ⽅式 未进⼊控制器的异常。 4. @RestControllerAdvice ⽅式可以定义多个拦截⽅法,拦截不同的异常类,并且可以获取抛出的异常信息,⾃ 由度更⼤ 

例如:

GlobalExceptionHandler.java

import com.jiazhong.util.JsonResult;
import com.jiazhong.util.ResultTool;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;


@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(RemoveBookException.class)
    public JsonResult removeBook(Throwable e) {
        return ResultTool.fail(5001, e.getMessage());
    }

    @ExceptionHandler(ChangeBookStatusException.class)
    public JsonResult chanChangeBookStatusExceptionge(Throwable e) {
        return ResultTool.fail(5002, e.getMessage());
    }
}

 ChangeBookStatusException.java

public class ChangeBookStatusException extends RuntimeException {
    public ChangeBookStatusException(String message) {
        super(message);
    }
}

 RemoveBookException.java

public class RemoveBookException extends RuntimeException {
    public RemoveBookException(String message) {
        super(message);
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值