• spring security入门


    1. Restful API和传统API的区别

    •  用URL描述资源
    •  用http描述方法行为,用http状态码描述结果
    •  使用json交互数据
    • RESTful是一种风格,不是强制的标准

     2. 使用spring mvc开发Restful API

    2.1 请求参数校验

       1)实体类

    import com.fasterxml.jackson.annotation.JsonView;
    import org.hibernate.validator.constraints.NotBlank;
    import org.hibernate.validator.constraints.Range;
    
    import javax.validation.constraints.NotNull;
    import java.util.Date;
    
    public class User {
        public interface UserSimpleView{}
        public interface UserDetailView extends UserSimpleView{}
    
        @JsonView(UserSimpleView.class)
        private Integer id;
    
        @JsonView(UserSimpleView.class)
        @NotBlank
        private String username;
    
        @JsonView(UserDetailView.class)
        private String password;
    
        @JsonView(UserSimpleView.class)
        @Range(max = 200, min = 0)
        @NotNull
        private Integer age;
    
        @JsonView(UserSimpleView.class)
        private Date birthday;
    }
    User.java

      2)控制器,如果不加BindingResult参数,并且出异常则不进入方法,直接将错误信息返回

        @PostMapping
        public User create(@Valid @RequestBody User user, BindingResult errors) {
            if (errors.hasErrors()) {
                errors.getAllErrors().forEach(error -> {
                    System.out.println("error ==> "+error.getDefaultMessage());
                });
            }
    
            System.out.println(user);
            user.setBirthday(new Date());
            return user;
        }

    2.2 选择性的返回实体类的属性,JsonView

    1)实体类

    public class User {
        public interface UserSimpleView{}
        public interface UserDetailView extends UserSimpleView{}
    
        @JsonView(UserSimpleView.class)
        private Integer id;
    
        @JsonView(UserSimpleView.class)
        @NotBlank
        private String username;
    
        @JsonView(UserDetailView.class)
        private String password;
    
        @JsonView(UserSimpleView.class)
        @Range(max = 200, min = 0)
        @NotNull
        private Integer age;
    
        @JsonView(UserSimpleView.class)
        private Date birthday;
    }
    User.java

    2)控制器

        @GetMapping
        @JsonView(User.UserSimpleView.class)
        public List<User> query(UserQueryCondition condition,
                                @PageableDefault(page = 1, size = 10, sort = "age") Pageable pageable) {
            System.out.println(condition);
            List<User> users = new ArrayList<>();
            users.add(new User() {{
                setId(1);
                setUsername("admin1");
                setPassword("123");
                setAge(20);
            }});
            users.add(new User() {{
                setId(2);
                setUsername("admin2");
                setPassword("123");
                setAge(22);
            }});
            return users;
        }
    UserController.java

    2.3 请求URL参数变量

      使用正则表达式对请求URL参数做限制

    @GetMapping("/{id:\d+}")

     

    2.2  服务异常处理

      在controller中发生异常,怎样处理?

      1)自定义异常类

    public class UserNotExistException extends RuntimeException {
        private static final long serialVersionId = Long.MIN_VALUE;
    
        private int id;
    
        public UserNotExistException(int id){
            super("user not exists");
            this.id = id;
        }
    }

      2)处理控制器抛出的异常

    @ControllerAdvice
    public class ControllerExceptionHandler {
        @ExceptionHandler(UserNotExistException.class)
        @ResponseBody
        @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
        public Map<String, Object> handlerUserNotExistException(UserNotExistException ex){
            Map<String, Object> result = new HashMap<>();
            result.put("id", ex.getId());
            result.put("message", ex.getMessage());
            return result;
        }
    }

     3 RESTful API拦截

    3.1 过滤器(Filter)

      定义过滤器,注册为spring组件

    import javax.servlet.*;
    import java.io.IOException;
    import java.util.Date;
    
    @Component  // 让过滤器生效, 默认拦截所有请求(/*)
    public class TimeFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            System.out.println("time filter init...");
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
                throws IOException, ServletException {
            System.out.println("time filter start");
            long start = new Date().getTime();
            filterChain.doFilter(servletRequest, servletResponse);
            long finish = new Date().getTime();
            System.out.println("time filter 耗时:"+(finish-start));
            System.out.println("time filter finish");
        }
    
        @Override
        public void destroy() {
            System.out.println("time filter destroy...");
        }
    }
    TimeFilter.java

      将第三方过滤器(没有@Component)注册到项目中,将TimeFilter的@Component去掉:

    @Configuration
    public class WebConfig {
    
        @Bean
        public FilterRegistrationBean timeFilter(){
            FilterRegistrationBean registrationBean = new FilterRegistrationBean();
            TimeFilter timeFilter = new TimeFilter();
            registrationBean.setFilter(timeFilter);
    
            // 配置拦截的URL
            List<String> urls = new ArrayList<>();
            urls.add("/*");
            registrationBean.setUrlPatterns(urls);
            return registrationBean;
        }
    }

      在过滤器中无法获取控制器中的信息,可以使用拦截器获取spring中的控制器信息

    3.2 拦截器(Interceptor)

       定义拦截器:

    @Component
    public class TimeInterceptor implements HandlerInterceptor {
    
        // 控制器方法被调用之前
        @Override
        public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
            System.out.println("prehandler...");
            System.out.println(((HandlerMethod)o).getBean().getClass().getName());//o为控制器方法
            System.out.println(((HandlerMethod)o).getMethod().getName());//o为控制器方法
            return true;
        }
    
        // 控制器方法被调用之后,如果控制器中抛出异常,则不会调用该方法
        @Override
        public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
            System.out.println("postHandler...");
    
        }
    
        // 处理请求后调用该方法,无论控制器方法是否抛出异常
        @Override
        public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
            System.out.println("after completion...");
        }
    }
    TimeInterceptor.java

    如果配置了控制器异常处理,那么拦截器的异常对象为空。

      配置拦截器:

    @Configuration
    public class WebConfig extends WebMvcConfigurerAdapter {
        @Autowired
        private TimeInterceptor timeInterceptor;
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(timeInterceptor);
        }
    }

    3.3 切片(Aspect)

      只需定义切面,不需要其他配置即可生效

    //比较细粒度的切片,一定能获取到切入点方法的参数信息
    @Aspect
    @Component
    public class TimeAspect {
        //切入点定义
        @Pointcut("execution(* com.getword.web.controller.UserController.*(..))")
        public void myPointcut(){}
    
        @Around("myPointcut()")
        public Object handlerControllerMethod(ProceedingJoinPoint pjp) throws Throwable { // 参数pjp包含控制器方法信息
            System.out.println("time aspect start...");
            //获取控制器方法的参数
            Object[] args = pjp.getArgs();
            for(Object arg : args){
                System.out.println("arg is :"+arg);
            }
            Object object = pjp.proceed();
            System.out.println("time aspect finish...");
            return object;
        }
    }

     4 文件上传下载

    @RestController
    @RequestMapping("/file")
    public class FileController {
        @PostMapping
        public FileInfo upload(MultipartFile file){
            System.out.println(file.getName());
            System.out.println(file.getOriginalFilename());
            System.out.println(file.getSize());
            try {
                InputStream inputStream = file.getInputStream();
           file.transferTo(new File("")); }
    catch (IOException e) { e.printStackTrace(); } FileInfo fileInfo = new FileInfo(""); return fileInfo; } @GetMapping("/{id}") public void download(@PathVariable String id, HttpServletRequest request, HttpServletResponse response) throws Exception { //获取file文件 try(InputStream inputStream = new FileInputStream(new File("")); OutputStream out = response.getOutputStream(); ){ response.setContentType("application/x-download"); response.setHeader("Content-Disposition","attachment;filename=test.txt"); IOUtils.copy(inputStream, out); out.flush(); } } }

    5 使用多线程提高RESTful性能

     5.1 使用Callable,Runnable

    @RequestMapping("/order")
    public Callable<String> orderAsync(){
        logger.info("主线程开始...");
        Callable<String> result = new Callable<String>() {
            @Override
            public String call() throws Exception {
                logger.info("副线程开始。。。");
                Thread.sleep(1000);
                logger.info("副线程返回。。。");
                return "success";
            }
        };
        logger.info("主线程返回...");
        return result;
    }

    5.2 异步处理RESTful服务,DeferredResult

    1)控制器

    @RequestMapping("/order3")
    public DeferredResult<String> deferredResult(){
        logger.info("主线程开始");
        // 生成订单号,相当于线程号
        String orderNumber = RandomStringUtils.randomNumeric(8);
        // 处理订单,开启新的线程,同时监听是否处理完毕,如果处理完毕就对客户端响应
        mockQueue.setPlaceOrder(orderNumber);
    
        // 该对象可以完成向 对应客户端 做出响应
        DeferredResult<String> deferredResult = new DeferredResult<>();
        deferredResultHolder.getMap().put(orderNumber, deferredResult);
    
        System.out.println("主线程返回");
        return deferredResult;
    }

    2)订单队列

    @Component
    public class MockQueue {
        private Logger logger = LoggerFactory.getLogger(getClass());
        private String placeOrder;
        private String completeOrder;
    
        public String getPlaceOrder() {
            return placeOrder;
        }
    
        public void setPlaceOrder(String placeOrder) {
            // 处理订单业务
            new Thread(()->{
                logger.info("接到下单请求..."+placeOrder);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.placeOrder = placeOrder;
                this.completeOrder = placeOrder;
                logger.info("下单请求处理完毕..."+placeOrder);
            }).start();
        }
    
        public String getCompleteOrder() {
            return completeOrder;
        }
    
        public void setCompleteOrder(String completeOrder) {
            this.completeOrder = completeOrder;
        }
    }
    MockQueue.java

    3)DeferredResultHolder存放<订单序号,DeferredResult>键值对

    @Component
    public class DeferredResultHolder {
        // 订单号,订单结果
        private Map<String, DeferredResult<String>> map = new HashMap<>();
    
        public Map<String, DeferredResult<String>> getMap() {
            return map;
        }
        public void setMap(Map<String, DeferredResult<String>> map) {
            this.map = map;
        }
    }

    4)订单完成监听器

    @Component
    public class QueueListener implements ApplicationListener<ContextRefreshedEvent> {
        private Logger logger = LoggerFactory.getLogger(getClass());
        @Autowired
        private MockQueue mockQueue;
        @Autowired
        private DeferredResultHolder deferredResultHolder;
        @Override
        public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
            // 开启新的线程监听队列中是否有完成的订单
            new Thread(()->{
                while (true) {
                    if (StringUtils.isNotBlank(mockQueue.getCompleteOrder())) {
                        // 监听到有订单完成了,结束异步请求,返回结果
                        String orderNumber = mockQueue.getCompleteOrder();
                        logger.info("返回订单处理结果:" + orderNumber);
                        deferredResultHolder.getMap().get(orderNumber).setResult("order completed success...");
                        mockQueue.setCompleteOrder(null);
                    } else {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
        }
    }
    QueueListener.java

     6 与前端并行开发

    6.1 使用swagger自动生成HTML文档

    1)引入maven依赖

    <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>2.9.2</version>
                <exclusions>
                    <exclusion>
                        <groupId>io.swagger</groupId>
                        <artifactId>swagger-annotations</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>io.swagger</groupId>
                        <artifactId>swagger-models</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>io.swagger</groupId>
                <artifactId>swagger-annotations</artifactId>
                <version>1.5.21</version>
            </dependency>
            <dependency>
                <groupId>io.swagger</groupId>
                <artifactId>swagger-models</artifactId>
                <version>1.5.21</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>2.9.2</version>
            </dependency>
    View Code

    2)在spring boot启动类添加模块

    @SpringBootApplication
    @EnableSwagger2
    public class DemoApplication {
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    }

     3)api描述:

    方法描述

    模型参数描述:

    参数描述:

    6.2 使用WireMock快速伪造RESTful服务

     

     1)下载WireMock的jar包

    2)运行WireMock服务器端

    java -jar wiremock-standalone-2.20.0.jar --port 8000

    3)使用Java代码,描述规则

    public class MockServer {
        public static void main(String[] args) throws IOException {
            WireMock.configureFor(8000);
            WireMock.removeAllMappings(); //清空urlMapping
    
            mock("/order", "01.json");
        }
        public static void mock(String url, String file) throws IOException {
            ClassPathResource classPathResource = new ClassPathResource("mock/response/"+file);
            String content = FileUtils.readFileToString(classPathResource.getFile(), "UTF-8");
            System.out.println(content);
            WireMock.stubFor(WireMock.get(WireMock.urlEqualTo(url))
                    .willReturn(WireMock.aResponse().withBody(content)
                            .withStatus(200)));
         WireMock.saveMappings(); } }

    乱码?

    withHeader("content-type", "application/json;charset=utf-8")

     将mapping保存起来,下次启动WireMock时,这些mapping还存在

    end

  • 相关阅读:
    沉痛悼念乔布斯 (Steven Paul Jobs)
    Linux下文件属性
    window phone开发之动画效果
    简单的UDP收发讯息
    Red Hat Enterprise Linux 5 (红帽子企业版5)下Samba, VSFTP配置与安装
    XNA那些事(六)--WINDOWS PHONE 游戏开发中的3D摄像机
    今天发布iPhone 4s的可能性大一点吧(结果:iPhone4s+CDMA/GSM)
    iPhone开发:如何在iPhone应用中使用自定义字体
    C语言数组与指针详解
    linux 常用命令
  • 原文地址:https://www.cnblogs.com/zhuxiang1633/p/10199055.html
Copyright © 2020-2023  润新知