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 增量更新研究

    Android 增量更新实例(Smart App Updates) http://blog.csdn.net/duguang77/article/details/17676797 Android AP ...

  2. [ActionScript 3.0] 常用的正则表达式

    as 3.0常用的正则表达式: /* * 去除字符串前面的空格和跳格符 */ var src:String=" Hello! "; trace(src); //原文本 trace( ...

  3. 在iOS7中修改键盘Return键的类型

    今天将之前运行在iOS7之前的一段代码拿出来,在iOS7的机器上运行,发现键盘上的ReturnKeyType不能被修改了. 经过几番查找资料,了解到iOS7中UISearchBar的结构发生了变化,将 ...

  4. MongoDB mongo.exe启动及闪退解决 转载

    转载自:http://blog.csdn.net/wyx_wx/article/details/76108662 启动: 进入MongoDB安装目录下的bin目录,启动mongod.exe 出现如图所 ...

  5. 高阶篇:4.2.5)DFMEA建议措施及后续完备

    本章目的:填写建议措施及DFMEA后续完备. 1.建议措施(k) 定义 总的来说,预防措施(降低发生率)比探测措施更好.举例来说,比起设计定稿后的产品验证/确认,使用已证实的设计标准或最佳实践更加可取 ...

  6. 【算法笔记】B1051 复数乘法

    题目链接:https://pintia.cn/problem-sets/994805260223102976/problems/994805274496319488 思路: 难点在于对复数其他形式的认 ...

  7. python 模块和包以及他们的导入关系

    一 模块 1 什么是模块? 常见的场景:一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀. 但其实import加载的模块分为四个通用类别: 1 使用python编 ...

  8. form表单提交到Controller之后空值变成逗号

    首先这个错误不是我遇到的,是别人遇到来找我给帮忙调试的(我不会犯这种错误!!!) 错误非常神奇,前端页面的form表单是空的啥都没填,提交到后台之后(后台用@ModelAttribute实体类接受的) ...

  9. (转)python collections模块详解

    python collections模块详解 原文:http://www.cnblogs.com/dahu-daqing/p/7040490.html 1.模块简介 collections包含了一些特 ...

  10. Oracle PL/SQL编程之过程

    1.简介 过程用于执行特定的操作,当建立过程时,既可以指定输入参数(in),也可以指定输出参数(out),通过在过程中使用输入参数,可以将数据传递到执行部分,通过使用输出参数,可以将执行部分的数据传递 ...