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>

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

spring security入门的更多相关文章

  1. SpringBoot集成Spring Security入门体验

    一.前言 Spring Security 和 Apache Shiro 都是安全框架,为Java应用程序提供身份认证和授权. 二者区别 Spring Security:重量级安全框架 Apache S ...

  2. Spring Security 入门(基本使用)

    Spring Security 入门(基本使用) 这几天看了下b站关于 spring security 的学习视频,不得不说 spring security 有点复杂,脑袋有点懵懵的,在此整理下学习内 ...

  3. Spring Security 入门(1-1)Spring Security是什么?

    1.Spring Security是什么? Spring Security 是一个安全框架,前身是 Acegi Security , 能够为 Spring企业应用系统提供声明式的安全访问控制. Spr ...

  4. Spring Security 入门

    一.Spring Security简介 Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中配 ...

  5. Spring Security 入门 (二)

    我们在篇(一)中已经谈到了默认的登录页面以及默认的登录账号和密码. 在这一篇中我们将自己定义登录页面及账号密码. 我们先从简单的开始吧:设置自定义的账号和密码(并非从数据库读取),虽然意义不大. 上一 ...

  6. Spring Security 入门(一)

    当你看到这篇文章时,我猜你肯定是碰到令人苦恼的问题了,我希望本文能让你有所收获. 本人几个月前还是 Spring 小白,几个月走来,看了 Spring,Spring boot,到这次的 Spring ...

  7. Spring Security 入门—内存用户验证

    简介 作为 Spring 全家桶组件之一,Spring Security 是一个提供安全机制的组件,它主要解决两个问题: 认证:验证用户名和密码: 授权:对于不同的 URL 权限不一样,只有当认证的用 ...

  8. 030 SSM综合练习06--数据后台管理系统--SSM权限操作及Spring Security入门

    1.权限操作涉及的三张表 (1)用户表信息描述users sql语句: CREATE TABLE users ( id ) DEFAULT SYS_GUID () PRIMARY KEY, email ...

  9. Spring Security 入门详解(转)

    1.Spring Security介绍 Spring Security是基于spring的应用程序提供声明式安全保护的安全性框架,它提供了完整的安全性解决方案,能够在web请求级别和方法调用级别 处理 ...

  10. Spring Security入门(1-13)Spring Security的投票机制和投票器

    1.三种表决方式,默认是 一票制AffirmativeBased public interface AccessDecisionManager { /** * 通过传递的参数来决定用户是否有访问对应受 ...

随机推荐

  1. 云课堂Android模块化实战--如何设计一个通用性的模块

    本文来自 网易云社区 . 如何设计一个通用性的模块 前言 每个开发者都会知道,随着项目的开发,会发现业务在不断壮大,产品线越来越丰富,而留给开发的时间却一直有限,在有限的时间,尽快完成某个功能的迭代. ...

  2. Logstash 收集 IIS 日志

    日志样例 查看 IIS 日志配置,选择格式为 W3C(默认字段设置)保存生效. 2016-02-25 01:27:04 112.74.74.124 GET /goods/list/0/1.html - ...

  3. redis 3.0 集群__安装

    参考文档 http://redis.io/topics/cluster-tutorial http://redis.io/topics/cluster-spec http://redis.readth ...

  4. Java-代理模式的理解

    引言 设计模式是语言的表达方式,它能让语言轻便而富有内涵.易读却功能强大.代理模式在Java中十分常见,有为扩展某些类的功能而使用静态代理,也有如Spring实现AOP而使用动态代理,更有RPC实现中 ...

  5. 移动端一个奇怪的触摸bug

    这两天遇到一个很奇怪的bug,在移动端,一个页面里所有的input框都不能点击,我查了一下,里面的没有设置readonly属性,只要页面滚动一下就可以用了,而且,只要我在真机测试的时候,f12开发者模 ...

  6. 2016级算法第三次上机-A.Bamboo的小吃街

    A Bamboo的小吃街 分析 经典的两条流水线问题,题目描述基本类似于课件中的流水线调度,符合动态规划最优子结构性质 关键的动态规划式子为: dp[0][j] = min(dp[0][j - 1], ...

  7. linux 常用命令手册

    命令 功能说明 线上查询及帮助命令(2个) man 查看命令帮助,命令的词典,更复杂的还有info,但不常用. help 查看Linux内置命令的帮助,比如cd命令. 文件和目录操作命令(18个) l ...

  8. API Monitor程序分析工具简介

    API Monitor是一个免费软件,可以让你监视和控制应用程序和服务,取得该应用程序的API调用情况. 它是一个强大的工具,看到的应用程序和服务是如何工作的,或跟踪,你在自己的应用程序的问题. AP ...

  9. [转] ScalaTest测试框架

    [From] https://blog.csdn.net/hany3000/article/details/51033610 ScalaTest测试框架 2016年04月01日 02:49:35 阅读 ...

  10. js中自执行函数写法

    //自执行写法1 (function T(){ alert(1) })() //自执行写法2 var T1=function(){ alert(1) }(); //传值 var para1={a:1; ...