SpringBoot框架
SpringBoot接口架构风格 ----- RESTful
再用一点点时间分享一下SpringBoot就准备开启计算机基础的回顾了【计算机基础 + 项目 + 算法 — 以后每篇计算机基础下面都会简单分享算法题】; 项目则会同时进行
就我自己的感觉来说: SpringBoot和SSM相比没有太大的变化,但是确实是简化了许多操作【这种简化的致使programmer做的越来越少; 更加专注于业务逻辑和其他的高并发的思考;SSM的弊端就是限制了很多,首先就是繁琐的配置,其次就是固定的框架,只能使用SpringMVC和Mybatis;如果想使用hibernate,那么就要使用SSH框架; 而SpringBoot就不同了,SpringBoot就是一个Spirng的不重复造轮子思想的体现 — 它可以使用Starter来完美的使用其他的框架,不像SSM已经固定好技术框架;
SpringBoot的两个重要的变化就是配置类JavaConfig;还有就是起步依赖starter;其他的变化其实没有太大;核心都没有变: IOC,DI,AOP等
接口 ---- 也就是API,应用程序接口,刚接触java的时候就经常听到这个概念,但是当时理解不深刻,只是觉得就是java的核心类库的API,但是后面上手了Vue之后,才感觉明显;因为Vue的概念就是前端工程化的思想;前后端分离,并且大概率是部署在不同的服务器【使用ngix解决跨域,或者后端开启访问】,那么这个时候后端发挥的功能就是返回一些数据,前端通过axios接收即可;后端只需要给前端提供一份api文档,就是一份约定,来完美衔接前端和后端的程序
所谓的无需访问源码,也就是只用调用即可,因为具体功能api文档会说明,前端程序员也大概率看不到后端的源码【访问的url或者其他程序的接口】
架构风格: api的组织方式
; 比如一个url地址,地址提供了访问资源名称addStudent后面?get传递参数
Representational State Transfer【表现层状态转移】 一种互联网软件架构设计的风格,不是标准,只是提出了一组客户端和服务器交互时的架构理念和设计的原则;基于这种理念和原则设计的接口可以更简洁,有层次
表现层状态转移:
任何一个技术都可以实现这种理念,如果符合REST原则,那么称为RESTful架构;用REST表示资源和对资源的操作
比如原来访问http接口:
http://localhost:8080/boot/order?id=1021&status=1
采用RESTful架构的风格的接口:
http://localhost:8080/boot/order/1021/1
:happy:,博主之前vue中就已经在大量使用了;比如但是的动态路由: http:…/:id 就是RESTful架构;
之前的普通架构就是: 每一个方法对应的就是对资源的操作比如CRUD;
那么REST中,资源是使用url表示,通过名词表示资源;使用http的动作,表示对资源的操作,但是操作的资源都是相同的
【这部分知识对应的就是前端的axios访问的方式: axios.get; post, delete,put】
这里可以演示一下对于学生的CRUD
GET: 查询资源 sql query
http://localhost:8080/bootOrm/student/1 //这里的1就是学生的id,代表查询这个学生的资源
http://localhost:8080/bootOrm/student/1/HC2001 //可以再加上一个属性
处理多个资源: 使用复数形式
http://localhost:8080/bootOrm/students/1/2 查询id为1和2的学生
POST : 创建资源 sql insert
http://localhost:8080/bootOrm/student //这里使用post提交数据,那么就是创建操作,就直接是student
PUT: 更新资源 sql update
在使用原生js的时候,因为是不能直接使用put和delete,所以需要在表单中hidden隐藏域来表示
<form>
姓名: <input type='text' name='name'/>
年龄: <input tupe='text' name='age'/>
<input type='hidden' name="_method" value='PUT'/>
form>
DELETE: 删除资源 sql delete
http://localhost:8080/bootOrm/student/1 直接访问该链接代表删除学号为1的学员
如果需要进行分页、排序的参数,依然放在url的后面,还是?的方式
http://localhost:8080/bootOrm/student/1?page=1&pageSize=20
之前使用的是?的方式来进行参数的传递,所以后端就可以使用request.getParameter()来获取数据,但是现在数据直接就在url中,不能通过这种方式进行获取了,现在获取数据需要使用注解@PathVariable ---- 路径变量,就是路径中的数据
获取url中的数据,该注解是实现RESTful最主要的一个注解
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PathVariable {
@AliasFor("name")
String value() default ""; //指定变量
@AliasFor("value")
String name() default "";
boolean required() default true;
} //这里就是指定value和name;键值对
这个注解放在类的上面,创建控制层对象,同时类中的所有控制器方法都会默认有@ResponseBody,所有的返回值都直接输出到响应体,对象类型就会变为JSON格式,不需要配置driven,SpringBoot帮忙做了很多事情,包括jarkson依赖
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller //这两个注解的复合注解
@ResponseBody
public @interface RestController {
支持get请求的方式,等同于@RequestMapping(method=RequestMethod.GET),直接将前端get的路径放入即可
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping( //说明这个注解和这个是相同的作用
method = {RequestMethod.GET}
)
public @interface GetMapping {
支持post请求得方式,等同于@RequestMapping(method=RequestMethod.POST)
支持put请求方式,等同于@RequestMapping(method=RequestMethod.PUT)
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(
method = {RequestMethod.PUT}
)
public @interface PutMapping {
支持delete请求方式 等同于@RequestMapping(method=RequestMethod.DELETE)
使用CTRL + N查看这些注解的原型
前端vue的路径就是RESTful风格的,对于动态路由,前端使用的也是动态匹配
比如: http://localhost:8080/bootOrm/student/:id
这里每次就是访问不同的学生的时候,前端就会给id传入不同的值,如果开启props传参,就可以直接接收,使用axios发送http请求,将props参数拼接在后面
类似的,后端处理器也会使用路径占位符来接收动态的参数;就一个{}即可,和vue插值表达式类似但少一层
比如这里: /student/{stuno};
@PathVariable放在控制器方法的形参前面,通过value指定上面占位符的路径变量
,赋值给后面的形参;类似之前的controller中的@RequestParam和之前的mybatis中的@Param【校正赋值】
@RestController
public class StudentController {
@Resource
private StudentService studentService;
//查询学生;接收的get请求
@GetMapping("/student/{stuName}")
public Student queryStudent(@PathVariable("stuName") String stuName){
return studentService.queryStudent(stuName);
}
使用@RestController就不需要@ResponseBody再注解了
前端访问地址,发送get请求,就可以查询数据
http://localhost:8081/bootOrm/student/王6
{"stuno":2,"stuname":"王6","stuclass":"HC2004"}
可以对比之前的操做的接口风格
@RequestMapping("/student/query")
@ResponseBody
public Student searchStudent(String stuName) {
return studentService.queryStudent(stuName);
}
@RequestMapping("/student/add")
CRUD分别对应的是不同的路径,这显得非常的繁琐,REST风格的接口确实简单了许多
再来康康创建资源的post请求
对于前端直接post的JSON格式的数据,后台显然不能直接使用@PathVariable逐个接收,这个时候使用之前的@RequestBody来接收;⚠: 这个注解会接收所有响应体中的JSON数据然后赋值给对象,所以就不能再用RequestParam来接收了
但是需要注意的是,这个注解接收的是前端转换的JSON格式的数据,而不是将get类似的N=V&N=V的方式
POST /bootOrm/student HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://localhost:8081/bootOrm/
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 37
stuno=12&stuname=Clie&stuclass=HC2001
比如这里就会请求失败,不是JSON格式
methods:{
async postBlog() {
//这里使用网址测试https://jsonplaceholder.typicode.com/posts
// const {data:res} = await this.$ajax.post('https://jsonplaceholder.typicode.com/posts',{
// title: this.blog.title,
// body: this.blog.content,
// userId:1,
// })
const res = await this.$ajax.post("http://localhost:8081/bootOrm/student",{
stuno: 12,
stuname: 'Clie',
stuclass: 'HC2001'
})
这里使用vue的anxios发送请求,后台;注意,这里的端口号不一致,存在跨域问题:happy:
Access to XMLHttpRequest at 'http://localhost:8081/bootOrm/student' from origin 'http://localhost:3000' has been blocked by CORS policy
所以vue中需要在vue.config中配置
proxy: 'http://localhost:8081' // proxy代理解决跨域传递 之前这里忘记给注释加//导致这行代码不生效
axios.defaults.baseURL = '/bootOrm'
const res = await this.$ajax.post("/student",{
stuno: 12,
stuname: 'Clie',
stuclass: 'HC2001'
})
配置后正常访问
@PostMapping("/student")
public int addStudent(@RequestBody Student student) {
return studentService.addStudent(student);
}
后台数据库操作日志
JDBC Connection [HikariProxyConnection@987910987 wrapping com.mysql.cj.jdbc.ConnectionImpl@181b1187] will be managed by Spring
==> Preparing: INSERT INTO student (stuno,stuname,stuclass) VALUES (?,?,?)
==> Parameters: 12(Integer), Clie(String), HC2001(String)
<== Updates: 1
这里很简单的问题:invalid character,Http method name,说名可能是路径中有字符的问题,是因为前台和后台请求的协议不一致;前台是https,后台是http
再康康删除
删除就是前台发起delete请求,后台用相应的处理器方法执行即可
//删除学生,接收的是delete请求
@GetMapping("/student/{stuName}")
public Student removeStudent(@PathVariable("stuName") String stuName){
return studentService.removeStudent(stuName);
}
所以,REST风格更加简便,但是后台的处理器方法并不会减少,还是之前的,只是请求的接口更规范,可能多个处理器方法接收的是同一个url,但是因为http动作不同,所以执行的处理器方法不同
; 这样url地址就不会出现多级,并且利于和前台vue配合
默认情况下,浏览器只能发送get和post请求,put、delete请求不支持
在SpringMVC中有一个过滤器,支持post请求转为put等,这个过滤器是内置写好的,HiddenHttpMethodFilter
如果是SSM项目,那么就需要在xml中进行配置,在SpringBoot中就很easy了,在application.yml中配置即可;
<form action="/bootOrm/student/query" method="post">
请输入修改姓名:<input type="text" name="stuName"><br>
<input type="submit" style="color: black;background-color: aquamarine" value="查询">
<input type="hidden" name="_method" value="put"/>
form>
spring:
mvc:
hiddenmethod:
filter:
enabled: true #默认是关闭的
这样就可以支持put请求操作
REST风格虽然简化了很多,但是也有一定的问题,需要programmer避免,那就是http动作 + url的唯一性;比如现在举个例子
//查询学生 byName
@GetMapping("/student/{stuName}")
public Student queryStudent(@PathVariable("stuName") String stuName){
return studentService.queryStudent(stuName);
}
//查询学生;byAge
@GetMapping("/student/{stuAge}")
public Student queryStudentByAge(@PathVariable("stuAge") String stuAge){
return studentService.queryStudent(stuAge);
}
可以看到这两个方法不能保证唯一性,如果访问http://localhost:8080/bootOrm/student/20; 那么后台就会报错,因为不知道执行哪个处理器方法,所以设计的时候要注意唯一性