如何快速提高网站关键词排名,施工企业质量管理应遵循的原则,百度推广工具有哪些,地推拉新接单网文章目录 Spring MVC#xff08;中#xff09;RESTFul风格设计RESTFul风格概述RESTFul风格特点RESTFul风格设计规范RESTFul风格好处RESTFul风格实战需求分析RESTFul风格接口设计后台接口实现 基于RESTFul风格练习#xff08;前后端分离模式#xff09;案例功能和接口分析功… 文章目录 Spring MVC中RESTFul风格设计RESTFul风格概述RESTFul风格特点RESTFul风格设计规范RESTFul风格好处RESTFul风格实战需求分析RESTFul风格接口设计后台接口实现 基于RESTFul风格练习前后端分离模式案例功能和接口分析功能预览接口分析 工程项目准备前端项目搭建后端项目搭建 后台增删改查实现项目根路径设计SpringMVC解决跨域问题业务实现 Spring MVC中
RESTFul风格设计
RESTFul风格概述 RESTfulRepresentational State Transfer是一种软件架构风格用于设计网络应用程序和服务之间的通信。它是一种基于标准 HTTP 方法的简单和轻量级的通信协议广泛应用于现代的Web服务开发。
通过遵循 RESTful 架构的设计原则可以构建出易于理解、可扩展、松耦合和可重用的 Web 服务。RESTful API 的特点是简单、清晰并且易于使用和理解它们使用标准的 HTTP 方法和状态码进行通信不需要额外的协议和中间件。
RESTful 架构通常用于构建 Web API提供数据的传输和操作。它可以用于各种应用场景包括客户端-服务器应用、单页应用SPA、移动应用程序和微服务架构等。
总而言之RESTful 是一种基于 HTTP 和标准化的设计原则的软件架构风格用于设计和实现可靠、可扩展和易于集成的 Web 服务和应用程序 学习RESTful设计原则可以帮助我们更好去设计HTTP协议的API接口
RESTFul风格特点
每一个URI代表1种资源URI 是名词客户端使用GET、POST、PUT、DELETE 4个表示操作方式的动词对服务端资源进行操作GET用来获取资源POST用来新建资源也可以用于更新资源PUT用来更新资源DELETE用来删除资源资源的表现形式是XML或者JSON客户端与服务端之间的交互在请求之间是无状态的从客户端到服务端的每个请求都必须包含理解请求所必需的信息。
RESTFul风格设计规范
HTTP协议请求方式要求
REST 风格主张在项目设计、开发过程中具体的操作符合HTTP协议定义的请求方式的语义。
操作请求方式查询操作GET保存操作POST删除操作DELETE更新操作PUT
URL路径风格要求
REST风格下每个资源都应该有一个唯一的标识符例如一个 URI统一资源标识符或者一个 URL统一资源定位符。资源的标识符应该能明确地说明该资源的信息同时也应该是可被理解和解释的
使用URL请求方式确定具体的动作他也是一种标准的HTTP协议请求
操作传统风格REST 风格保存/CRUD/saveEmpURL 地址/CRUD/emp 请求方式POST删除/CRUD/removeEmp?empId2URL 地址/CRUD/emp/2 请求方式DELETE更新/CRUD/updateEmpURL 地址/CRUD/emp 请求方式PUT查询/CRUD/editEmp?empId2URL 地址/CRUD/emp/2 请求方式GET
总结
根据接口的具体动作选择具体的HTTP协议请求方式
路径设计从原来携带动标识改成名词对应资源的唯一标识即可
RESTFul风格好处 含蓄安全 使用问号键值对的方式给服务器传递数据太明显容易被人利用来对系统进行破坏。使用 REST 风格携带数据不再需要明显的暴露数据的名称。 风格统一 URL 地址整体格式统一从前到后始终都使用斜杠划分各个单词用简单一致的格式表达语义。 无状态 在调用一个接口访问、操作资源的时候可以不用考虑上下文不用考虑当前状态极大的降低了系统设计的复杂度。 严谨规范 严格按照 HTTP1.1 协议中定义的请求方式本身的语义进行操作。 简洁优雅 过去做增删改查操作需要设计4个不同的URL现在一个就够了。 丰富的语义 通过 URL 地址就可以知道资源之间的关系。它能够把一句话中的很多单词用斜杠连起来反过来说就是可以在 URL 地址中用一句话来充分表达语义。
操作传统风格REST 风格保存/CRUD/saveEmpURL 地址/CRUD/emp 请求方式POST删除/CRUD/removeEmp?empId2URL 地址/CRUD/emp/2 请求方式DELETE更新/CRUD/updateEmpURL 地址/CRUD/emp 请求方式PUT查询/CRUD/editEmp?empId2URL 地址/CRUD/emp/2 请求方式GET
RESTFul风格实战
需求分析
数据结构 User {id 唯一标识,name 用户名age 用户年龄}功能分析 用户数据分页展示功能条件page 页数 默认1size 每页数量 默认 10保存用户功能根据用户id查询用户详情功能根据用户id更新用户数据功能根据用户id删除用户数据功能多条件模糊查询用户功能条件keyword 模糊关键字page 页数 默认1size 每页数量 默认 10
RESTFul风格接口设计
接口设计
功能接口和请求方式请求参数返回值分页查询GET /userpage1size10 param{ 响应数据 }用户添加POST /user{ user 数据 }{响应数据}用户详情GET /user/1路径参数{响应数据}用户更新PUT /user{ user 更新数据}{响应数据}用户删除DELETE /user/1路径参数{响应数据}条件模糊GET /user/searchpage1size10keywork关键字{响应数据}
问题讨论
为什么查询用户详情就使用路径传递参数多条件模糊查询就使用请求参数传递
误区restful风格下不是所有请求参数都是路径传递可以使用其他方式传递
在 RESTful API 的设计中路径和请求参数和请求体都是用来向服务器传递信息的方式。
对于查询用户详情使用路径传递参数是因为这是一个单一资源的查询即查询一条用户记录。使用路径参数可以明确指定所请求的资源便于服务器定位并返回对应的资源也符合 RESTful 风格的要求。而对于多条件模糊查询使用请求参数传递参数是因为这是一个资源集合的查询即查询多条用户记录。使用请求参数可以通过组合不同参数来限制查询结果路径参数的组合和排列可能会很多不如使用请求参数更加灵活和简洁。 此外还有一些通用的原则可以遵循路径参数应该用于指定资源的唯一标识或者 ID而请求参数应该用于指定查询条件或者操作参数。请求参数应该限制在 10 个以内过多的请求参数可能导致接口难以维护和使用。对于敏感信息最好使用 POST 和请求体来传递参数。
后台接口实现
准备用户实体类
package com.gj.pojo;/*** projectName: com.gj.pojo* 用户实体类*/
public class User {private Integer id;private String name;private Integer age;public Integer getId() {return id;}public void setId(Integer id) {this.id id;}public String getName() {return name;}public void setName(String name) {this.name name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age age;}Overridepublic String toString() {return User{ id id , name name \ , age age };}
}准备用户Controller:
/*** projectName: com.gj.controller** description: 用户模块的控制器*/
RequestMapping(user)
RestController
public class UserController {/*** 模拟分页查询业务接口*/GetMappingpublic Object queryPage(RequestParam(name page,required false,defaultValue 1)int page,RequestParam(name size,required false,defaultValue 10)int size){System.out.println(page page , size size);System.out.println(分页查询业务!);return {status:ok};}/*** 模拟用户保存业务接口*/PostMappingpublic Object saveUser(RequestBody User user){System.out.println(user user);System.out.println(用户保存业务!);return {status:ok};}/*** 模拟用户详情业务接口*/PostMapping(/{id})public Object detailUser(PathVariable Integer id){System.out.println(id id);System.out.println(用户详情业务!);return {status:ok};}/*** 模拟用户更新业务接口*/PutMappingpublic Object updateUser(RequestBody User user){System.out.println(user user);System.out.println(用户更新业务!);return {status:ok};}/*** 模拟条件分页查询业务接口*/GetMapping(search)public Object queryPage(RequestParam(name page,required false,defaultValue 1)int page,RequestParam(name size,required false,defaultValue 10)int size,RequestParam(name keyword,required false)String keyword){System.out.println(page page , size size , keyword keyword);System.out.println(条件分页查询业务!);return {status:ok};}
}基于RESTFul风格练习前后端分离模式
案例功能和接口分析
功能预览 接口分析
学习计划查询
/*
需求说明查询全部数据页数据
请求urischedule
请求方式 get
响应的json{code:200,flag:true,data:[{id:1,title:学习java,completed:true},{id:2,title:学习html,completed:true},{id:3,title:学习css,completed:true},{id:4,title:学习js,completed:true},{id:5,title:学习vue,completed:true}]}
*/学习计划删除
/*
需求说明根据id删除日程
请求urischedule/{id}
请求方式 delete
响应的json{code:200,flag:true,data:null}
*/学习计划保存
/*
需求说明增加日程
请求urischedule
请求方式 post
请求体中的JSON{title: ,completed: false}
响应的json{code:200,flag:true,data:null}
*/学习计划修改
/*
需求说明根据id修改数据
请求urischedule
请求方式 put
请求体中的JSON{id: 1,title: ,completed: false}
响应的json{code:200,flag:true,data:null}
*/工程项目准备
前端项目搭建
安装node和npm node和npm简介 Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环境可以使 JavaScript 运行在服务器端。 NPM全称Node Package Manager是Node.js包管理工具是全球最大的模块生态系统里面所有的模块都是开源免费的也是Node.js的包管理工具! node和npm安装 安装Nodejs自动安装npm包管理工具 打开官网https://nodejs.org/en下载对应操作系统的 LTS 版本。资料中已有node安装包 双击安装包进行安装安装过程中遵循默认选项即可(或者参照https://www.runoob.com/nodejs/nodejs-install-setup.html )。安装完成后可以在命令行终端输入 node -v 和 npm -v 查看 Node.js 和 npm 的版本号。 npm镜像和版本升级 配置阿里镜像 npm config set registry https://registry.npmmirror.com查看镜像配置结果 npm config get registry升级npm版本 npm install -g npm9.6.6使用npm安装项目依赖 使用cmd黑窗口进入前端工程项目文件夹下(切记进入到package.json同层文件夹下运行一下命令 npm i启动前端程序 npm run dev后端项目搭建
数据库怎么办使用HashMap模拟所以不涉及和MyBatis、Spring的整合!
搭建后台项目
pom.xml
propertiesspring.version6.0.6/spring.versionmaven.compiler.source17/maven.compiler.sourcemaven.compiler.target17/maven.compiler.targetproject.build.sourceEncodingUTF-8/project.build.sourceEncoding
/propertiesdependencies!-- springioc相关依赖 --dependencygroupIdorg.springframework/groupIdartifactIdspring-context/artifactIdversion${spring.version}/version/dependency!-- web相关依赖 --!-- 在 pom.xml 中引入 Jakarta EE Web API 的依赖 --!--在 Spring Web MVC 6 中Servlet API 迁移到了 Jakarta EE API因此在配置 DispatcherServlet 时需要使用Jakarta EE 提供的相应类库和命名空间。错误信息 “‘org.springframework.web.servlet.DispatcherServlet’is not assignable to ‘javax.servlet.Servlet,jakarta.servlet.Servlet’” 表明你使用了旧版本的Servlet API没有更新到 Jakarta EE 规范。--dependencygroupIdjakarta.platform/groupIdartifactIdjakarta.jakartaee-web-api/artifactIdversion9.1.0/versionscopeprovided/scope/dependencydependencygroupIdcom.fasterxml.jackson.core/groupIdartifactIdjackson-databind/artifactIdversion2.15.0/version/dependency!-- springwebmvc相关依赖 --dependencygroupIdorg.springframework/groupIdartifactIdspring-webmvc/artifactIdversion${spring.version}/version/dependency/dependencies准备实体类
/*** projectName: com.gj.pojo** description: 任务实体类*/
public class Schedule {private Integer id;private String title;private Boolean completed;public Schedule() {}public Schedule(Integer id, String title, Boolean completed) {this.id id;this.title title;this.completed completed;}public Integer getId() {return id;}public void setId(Integer id) {this.id id;}public String getTitle() {return title;}public void setTitle(String title) {this.title title;}public Boolean getCompleted() {return completed;}public void setCompleted(Boolean completed) {this.completed completed;}Overridepublic String toString() {return Schedule{ id id , title title \ , completed completed };}
}准备R结果包装类
/*** projectName: com.gj.utils** description: 返回结果类*/
public class R {private int code 200; //200成功状态码private boolean flag true; //返回状态private Object data; //返回具体数据public static R ok(Object data){R r new R();r.data data;return r;}public static R fail(Object data){R r new R();r.code 500; //错误码r.flag false; //错误状态r.data data;return r;}public int getCode() {return code;}public void setCode(int code) {this.code code;}public boolean isFlag() {return flag;}public void setFlag(boolean flag) {this.flag flag;}public Object getData() {return data;}public void setData(Object data) {this.data data;}
}准备业务类
业务接口
/*** projectName: com.gj.service** description: schedule业务接口*/public interface ScheduleService {/*** 返回全部学习计划* return*/ListSchedule getAll();/*** 保存学习计划* param schedule*/void saveSchedule(Schedule schedule);/*** 更新学习计划* param schedule*/void updateSchedule(Schedule schedule);/*** 移除学习计划* param*/void removeById(Integer id);}业务实现
/*** projectName: com.gj.service.impl** description:*/
Service
public class ScheduleServiceImpl implements ScheduleService {//准备假数据private static MapInteger,Schedule scheduleMap;private static int maxId 5;static {scheduleMap new HashMap();Schedule schedule null;schedule new Schedule(1, 学习Java, false);scheduleMap.put(1, schedule);schedule new Schedule(2, 学习H5, true);scheduleMap.put(2, schedule);schedule new Schedule(3, 学习Css, false);scheduleMap.put(3, schedule);schedule new Schedule(4, 学习JavaScript, false);scheduleMap.put(4, schedule);schedule new Schedule(5, 学习Spring, true);scheduleMap.put(5, schedule);} /*** 返回全部学习计划** return*/Overridepublic ListSchedule getAll() {return new ArrayList(scheduleMap.values());}/*** 保存学习计划** param schedule*/Overridepublic void saveSchedule(Schedule schedule) {maxId;schedule.setId(maxId);scheduleMap.put(maxId,schedule);}/*** 更新学习计划** param schedule*/Overridepublic void updateSchedule(Schedule schedule) {scheduleMap.put(schedule.getId(),schedule);}/*** 移除学习计划** param id*/Overridepublic void removeById(Integer id) {scheduleMap.remove(id);}}准备spring-mvc.配置文件
位置resources/spring-mvc.xml
?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:contexthttp://www.springframework.org/schema/contextxmlns:mvchttp://www.springframework.org/schema/mvcxsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd!-- 扫描controller对应的包,将handler加入到ioc--context:component-scan base-packagecom.gj.controller,com.gj.service /!--注意: 导入mvc命名空间!mvc:annotation-driven 是一个整合标签他会导入handlerMapping和handlerAdapter他会导入json数据格式转化器等等!--mvc:annotation-driven /!-- viewResolver 不需要配置,因为我们不需要查找逻辑视图!!! --!-- 加入这个配置SpringMVC 就会在遇到没有 RequestMapping 的请求时放它过去 --!-- 所谓放它过去就是让这个请求去找它原本要访问的资源 --mvc:default-servlet-handler/
/beans准备 web.xml配置文件
?xml version1.0 encodingUTF-8?
web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaeexmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsdversion4.0!-- 配置SpringMVC中负责处理请求的核心Servlet也被称为SpringMVC的前端控制器 --servletservlet-nameDispatcherServlet/servlet-name!-- DispatcherServlet的全类名 --servlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class!-- 通过初始化参数指定SpringMVC配置文件位置 --init-param!-- 如果不记得contextConfigLocation配置项的名称可以到DispatcherServlet的父类FrameworkServlet中查找 --param-namecontextConfigLocation/param-name!-- 使用classpath:说明这个路径从类路径的根目录开始才查找 --param-valueclasspath:spring-mvc.xml/param-value/init-param!-- 作为框架的核心组件在启动过程中有大量的初始化操作要做这些操作放在第一次请求时才执行非常不恰当 --!-- 我们应该将DispatcherServlet设置为随Web应用一起启动 --load-on-startup1/load-on-startup/servletservlet-mappingservlet-nameDispatcherServlet/servlet-name!-- 对DispatcherServlet来说url-pattern有两种方式配置 --!-- 方式一配置“/”表示匹配整个Web应用范围内所有请求。这里有一个硬性规定不能写成“/*”。只有这一个地方有这个特殊要求以后我们再配置Filter还是可以正常写“/*”。 --!-- 方式二配置“*.扩展名”表示匹配整个Web应用范围内部分请求 --url-pattern//url-pattern/servlet-mapping/web-app后台增删改查实现
项目根路径设计
因为前端项目设置了后台访问的项目根路径为 /rest
我们后台项目也对应的设置
SpringMVC解决跨域问题
假设我们有一个网站 http://example.com现在需要跨域请求另外一个网站 http://api.example.com 中的数据。浏览器就会因为安全问题拒绝客户端访问请求
跨域问题是指在浏览器中发起跨域请求被浏览器拦截的问题。在同一个源域同一协议、主机、端口浏览器允许 JavaScript 发起跨域请求在不同的源域下浏览器对发起的异域请求会做出不同的限制。
常见的跨域问题的场景有
访问不同的子域名访问不同的端口号访问不同的协议http、https访问不同的域名
基于CORS方式解决跨域思路
CORSCross-Origin Resource Sharing是 W3C 制定的一种跨域解决方案它给出了跨域请求和响应的标准。服务器端代码需要在响应头中设置 Access-Control-Allow-Origin并指定访问来源域名名或 * 通配符表示允许的跨域请求。浏览器可以根据响应头信息判断是否允许该请求。
SpringMVC基于CORS思路解决跨域方案 CrossOrigin注解 CrossOrigin 注释在带注释的【控制器方法】 / 【控制器类】上启用跨源请求 RestController
RequestMapping(/account)
public class AccountController {CrossOriginGetMapping(/{id})public Account retrieve(PathVariable Long id) {// ...}DeleteMapping(/{id})public void remove(PathVariable Long id) {// ...}
}默认情况下 CrossOrigin 允许 All origins.All headers.All HTTP methods to which the controller method is mapped. 注解核心设置属性讲解 Target({ElementType.TYPE, ElementType.METHOD})
Retention(RetentionPolicy.RUNTIME)
Documented
public interface CrossOrigin {/*** 设置哪些客户端地址可以跨域访问! 格式为: 协议://主机地址:端口号* return*/AliasFor(origins)String[] value() default {};AliasFor(value)String[] origins() default {};/*** 设置哪些客户端的[自定义请求头]可以跨域访问!*/String[] allowedHeaders() default {};/*** 设置哪些服务端的自定义响应头,可以被客户端读取!*/String[] exposedHeaders() default {};/***设置哪些请求方法,可以跨域方式! */RequestMethod[] methods() default {};/*** 值为 true 或者 false* 客户端是否可以携带cookie!*/String allowCredentials() default ;
}xml全局跨域配置 mvc:corsmvc:mapping path/**allowed-origins*allowed-methodsGET, PUTallowed-headersheader1, header2, header3exposed-headersheader1, header2 allow-credentialstrue/mvc:mapping path/**allowed-originshttps://domain1.com //mvc:cors业务实现
查询业务
/*** projectName: com.gj.controller** description: 学习计划controller*/
CrossOrigin
/*CrossOrigin 注释在带注释的控制器方法上启用跨源请求默认情况下 CrossOrigin 允许All origins 任何请求主机地址All headers 任何请求头All HTTP methods to which the controller method is mapped. 任何请求方式!可以设置:CrossOrigin(origins https://domain2.com) 指定允许跨域请求的主机地址*/
RequestMapping(schedule)
RestController
public class ScheduleController
{Autowiredprivate ScheduleService scheduleService;GetMappingpublic R showList(){ListSchedule list scheduleService.getAll();return R.ok(list);}
}修改业务
PutMapping
public R changeSchedule(RequestBody Schedule schedule){scheduleService.updateSchedule(schedule);return R.ok(null);
}删除业务
DeleteMapping(/{id})
public R removeSchedule(PathVariable Integer id){scheduleService.removeById(id);return R.ok(null);
}保存业务
PostMapping
public R saveSchedule(RequestBody Schedule schedule){scheduleService.saveSchedule(schedule);return R.ok(null);
}