Spring Boot前后端分离开发之后端开发
📃个人主页: 不断前进的皮卡丘
🌞博客描述: 梦想也许遥不可及,但重要的是追梦的过程,用博客记录自己的成长,记录自己一步一步向上攀登的印记
🔥系列专栏: Spring Boot专栏
前端和后端:
前端和后端是针对于技术来说的。
前端: 负责页面展示相关的技术,比如html,css,js,jquery,vue,bookstrap等
后端:与数据,还有业务逻辑相关,比如Java,MySQL等
移动端:移动设置相关的技术,比如Android(java,kotilin),iOS(swift)
全栈工程师:前端,后端,移动端
全过程工程师:产品设计,代码开发,测试,运维
前台和后台
前台和后台是相对于使用者来说的
前台:它是相对于普通用户来说,比如用户可以看到的网站
后台:它是对应程序的管理人员和运维人员而言的,后台也包括前端(页面)和后端(数据和业务)
早期的时候,web应用中的数据,页面,渲染都在服务端完成,但是随着时代的发展,前端设备越来越多(手机,平板,桌面电脑等),后来就慢慢兴起前后端分离的思想:后端负责数据处理,前端负责数据渲染,前端静态页面调用指定api获取固定格式的数据
为了实现前后端分离,必须要有一种统一的机制,方便不同的前端设备和后端进行通信

1.URL传参更加简洁
2.完成不同终端之间的资源共享,RESTful提供了一套规范,不同终端之间只要遵守这个规范,就可以实现数据交互。
RESTful具体来说是四种表现形式,HTTP请求中四种请求类型(GET、POST、PUT、DELETE)分别表示四种常规操作,CRUD。通过不同的请求方式来完成不同的操作
两个终端要完成数据交互,基于RESTful的方式,增删改查操作分别需要使用不同的HTTP请求类型来访问。
传统的web开发中form表单只支持GET和POST请求,如何解决呢?我们可以通过添加HiddenHttpMethodFilter过滤器,可以把POST请求转为PUT或者DELETE
RESTful风格的话,响应的数据格式要求是json
http://api.example.com/school/students
http://api.example.com/school/students/{100}
GET http://api.example.com/school/students 查询所有学生信息
GET http://api.example.com/school/students/{id} 查询指定id的学生信息
POST http://api.example.com/school/students 添加学生信息
PUT http://api.example.com/school/students/{id} 修改指定id的学生信息
DELETE http://api.example.com/school/students/{id} 删除指定id的学生信息
如果记录数量很多,服务器不可能返回所有信息给用户,API需要提供参数,来过滤返回的结果
?limit=10 指定返回记录的数量
?offset=10 指定返回记录的开始位置
?page=2&pageCount=100 指定第几页,以及每页的记录数
?orderby=name&order=asc 指定返回结果按照哪一个属性排序,以及排序顺序
?age>=20指定筛选条件
有的公司的请求方式还是使用GET和POST请求,然后通过路径来区分目的
/stus/query 查询操作
/stus/delete/{sid} 根据id删除
返回数据的格式必须要使用json格式,返回结果建议包含三个部分的信息,状态码,提示信息和真实数据
JSON(JavaScript Object Notation, JS对象标记)是一种轻量级的数据交换格式,目前使用特别广泛。
json的数据格式分成json Object(json对象)和json array(json数组)
json Object数据格式:
学生信息:
{
"id":100,
"name":"tom",
"cls":{
"id":1,
"name":"计算机一班"
}
}
json array数据格式
所有班级信息的数组:
["计算机一班","计算机二班","软件一班", "软件二班"]


我们知道浏览器它只能发送GET请求,这样的话,其他的请求方式,我们是没法测试的。
接下来就介绍前后端分离开发,经常用到的工具postman
官网地址:https://www.postman.com/downloads/

我们可以选择把exe文件放在指定文件家,然后双击


然后可以去登录,或者跳过登录




点击send(代码实现在后面介绍)

restful风格要求返回的字符串是json格式
@ResponseBody可以把Java对象转化为json对象,并返回给客户端
我们写一个小demo测试一下(数据是从数据库中查询出来的)
实体类(班级表对应的实体类)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Cls {
private Integer id;
private String name;
}
Controller(可以直接用@RestController)

@Controller
@ResponseBody
public class ClsController {
@Autowired
private ClsService clsService;
/**
* 查询所有班级信息
*/
@GetMapping("/school/cls")
public List<Cls> queryAllCls() {
try {
List<Cls> clsList = clsService.queryAllCls();
return clsList;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
Service
public interface ClsService {
/**
* 查询所有班级信息
* @return
*/
List<Cls> queryAllCls() throws Exception;
}
Service实现类
@Service
public class ClsServiceImpl implements ClsService {
@Autowired
private ClsMapper clsMapper;
@Override
public List<Cls> queryAllCls() throws Exception {
return clsMapper.queryAllCls();
}
}
Mapper
@Mapper
public interface ClsMapper {
/**
* 查询所有班级信息
*/
@Select("select * from cls")
List<Cls> queryAllCls() throws Exception;
}

下面这张图片显示的数据和上面图片的不一样,是因为后来我把格式进行修改了,也就是对代码进行了修改,后面会进行说明

单元测试
@SpringBootTest
class ClsMapperTest {
@Autowired
private ClsMapper clsMapper;
@Test
public void testQueryAllCls() throws Exception {
List<Cls> cls = clsMapper.queryAllCls();
for (Cls c:cls){
System.out.println(c);
}
}
}
输出内容:
Cls(id=1, name=计科1班)
Cls(id=2, name=计科2班)
Cls(id=3, name=软工1班)
Cls(id=4, name=软工2班)
@PathVariable:把请求路径的变量和形参名进行绑定
Controller
@RestController
public class ClsController {
@Autowired
private ClsService clsService;
/**
* 查询所有班级信息
*/
@GetMapping("/school/cls")
public List<Cls> queryAllCls() {
try {
List<Cls> clsList = clsService.queryAllCls();
return clsList;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 查询指定编号的班级信息
*/
@GetMapping("/school/cls/{id}")
public Cls queryClsById(@PathVariable("id") Integer id){
Cls cls= null;
try {
cls = clsService.queryClsById(id);
} catch (Exception e) {
e.printStackTrace();
}
return cls;
}
}
Service
public interface ClsService {
/**
* 查询所有班级信息
* @return
*/
List<Cls> queryAllCls() throws Exception;
/**
* 查询指定编号的班级信息
* @param id
* @return
* @throws Exception
*/
Cls queryClsById(Integer id) throws Exception;
}
Service实现类
@Service
public class ClsServiceImpl implements ClsService {
@Autowired
private ClsMapper clsMapper;
@Override
public List<Cls> queryAllCls() throws Exception {
return clsMapper.queryAllCls();
}
@Override
public Cls queryClsById(Integer id) throws Exception {
return clsMapper.queryClsById(id);
}
}
Mapper
@Mapper
public interface ClsMapper {
/**
* 查询所有班级信息
*/
@Select("select * from cls")
List<Cls> queryAllCls() throws Exception;
/**
* 查询指定编号的班级信息
* @param id
* @return
*/
@Select("select * from cls where id=#{id}")
Cls queryClsById(@Param("id") Integer id);
}


:::info
接口返回类型:
json格式
------code:状态码
------message:状态信息
------data:真实数据
:::
我们如果想要达到这样的效果的话,有两种方法,第一种就是我们写一个实体类,第二种方法就是通过map来存数据
通过定义实体类

通过map




/**
* 根据id来删除班级信息
* @param id
* @return
*/
@RequestMapping(value = "/school/cls/{id}" ,method = RequestMethod.DELETE)
public ResultInfo deleteById(@PathVariable("id") Integer id){
ResultInfo info = new ResultInfo();
try {
clsService.deleteById(id);
info.setOk();
} catch (Exception e) {
e.printStackTrace();
info.setError();
}
return info;
}
默认是接收key-value格式的数据,如果前端传入的是json格式的数据,那么数据就会接收失败,如果我们希望前端传送的数据是json格式的数据,那么,我们就需要在Controller中,在参数前面加上@RequestBody注解(把请求体中的json格式转换为对应Java实体类格式)
Controller
/**
* 添加班级 前端需要传参数进来
* @return
*/
@PostMapping("/school/cls")
public ResultInfo addCls(Cls cls){
ResultInfo info = new ResultInfo();
try {
clsService.insert(cls);
info.setOk();
info.setData(cls);
} catch (Exception e) {
e.printStackTrace();
info.setError();
}
return info;
}
Service
public interface ClsService {
/**
* 查询所有班级信息
* @return
*/
List<Cls> queryAllCls() throws Exception;
/**
* 查询指定编号的班级信息
* @param id
* @return
* @throws Exception
*/
Cls queryClsById(Integer id) throws Exception;
void deleteById(Integer id) throws Exception;
void insert(Cls cls) throws Exception;
}
Service实现类
@Service
public class ClsServiceImpl implements ClsService {
@Autowired
private ClsMapper clsMapper;
@Override
public List<Cls> queryAllCls() throws Exception {
return clsMapper.queryAllCls();
}
@Override
public Cls queryClsById(Integer id) throws Exception {
return clsMapper.queryClsById(id);
}
@Override
public void deleteById(Integer id) throws Exception {
clsMapper.deleteById(id);
}
//添加数据
@Override
public void insert(Cls cls) throws Exception {
clsMapper.insert(cls);
}
}
Mapper
@Mapper
public interface ClsMapper {
/**
* 查询所有班级信息
*/
@Select("select * from cls")
List<Cls> queryAllCls() throws Exception;
/**
* 查询指定编号的班级信息
* @param id
* @return
*/
@Select("select * from cls where id=#{id}")
Cls queryClsById(@Param("id") Integer id);
/**
* 根据id删除数据
* @param id
*/
@Delete("delete from cls where id=#{id}")
void deleteById(@Param("id") Integer id);
/**
* 插入班级
* @param cls
*/
@Insert("insert into cls values(default,#{name})")
@Options(useGeneratedKeys = true,keyColumn = "id",keyProperty = "id")
void insert(Cls cls);
}

通过数据库查询的前后两次对比可以很清楚的发现,插入数据成功了

Controller
@PutMapping("/school/cls/{id}")
public ResultInfo updateById(@PathVariable("id") Integer id,String name){
ResultInfo info = new ResultInfo();
try {
clsService.updateById(id,name);
info.setOk();
info.setData(new Cls(id,name));
} catch (Exception e) {
e.printStackTrace();
info.setError();
}
return info;
}
Service
public interface ClsService {
/**
* 查询所有班级信息
* @return
*/
List<Cls> queryAllCls() throws Exception;
/**
* 查询指定编号的班级信息
* @param id
* @return
* @throws Exception
*/
Cls queryClsById(Integer id) throws Exception;
void deleteById(Integer id) throws Exception;
void insert(Cls cls) throws Exception;
void updateById(Integer id, String name) throws Exception;
}
Service实现类
@Service
public class ClsServiceImpl implements ClsService {
@Autowired
private ClsMapper clsMapper;
@Override
public List<Cls> queryAllCls() throws Exception {
return clsMapper.queryAllCls();
}
@Override
public Cls queryClsById(Integer id) throws Exception {
return clsMapper.queryClsById(id);
}
@Override
public void deleteById(Integer id) throws Exception {
clsMapper.deleteById(id);
}
@Override
public void insert(Cls cls) throws Exception {
clsMapper.insert(cls);
}
@Override
public void updateById(Integer id, String name) throws Exception {
clsMapper.updateById(id,name);
}
}
Mapper
@Mapper
public interface ClsMapper {
/**
* 查询所有班级信息
*/
@Select("select * from cls")
List<Cls> queryAllCls() throws Exception;
/**
* 查询指定编号的班级信息
* @param id
* @return
*/
@Select("select * from cls where id=#{id}")
Cls queryClsById(@Param("id") Integer id);
/**
* 根据id删除数据
* @param id
*/
@Delete("delete from cls where id=#{id}")
void deleteById(@Param("id") Integer id);
/**
* 插入班级
* @param cls
*/
@Insert("insert into cls values(default,#{name})")
@Options(useGeneratedKeys = true,keyColumn = "id",keyProperty = "id")
void insert(Cls cls);
/**
* 根据id修改数据
* @param id
* @param name
*/
@Update("update cls set name=#{name} where id=#{id} ")
void updateById(@Param("id") Integer id, @Param("name") String name);
}

通过查询数据库中的数据,也可以发现数据改变了

在下一篇文章中会讲到跨域处理

我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm
我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI
这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub
我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD
在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList()Obt
@作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors 1、什么是behaviors 2、behaviors的工作方式 3、创建behavior 4、导入并使用behavior 5、behavior中所有可用的节点 6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors 1、什么是behaviorsbehaviors是小程序中,用于实现
了解Rails缓存如何工作的人可以真正帮助我。这是嵌套在Rails::Initializer.runblock中的代码:config.after_initializedoSomeClass.const_set'SOME_CONST','SOME_VAL'end现在,如果我运行script/server并发出请求,一切都很好。然而,在我的Rails应用程序的第二个请求中,一切都因单元化常量错误而变得糟糕。在生产模式下,我可以成功发出第二个请求,这意味着常量仍然存在。我已通过将以上内容更改为以下内容来解决问题:config.after_initializedorequire'some_cl
我有一个使用PDFKit呈现网页的pdf版本的Rails应用程序。我使用Thin作为开发服务器。问题是当我处于开发模式时。当我使用“bundleexecrailss”启动我的服务器并尝试呈现任何PDF时,整个过程会陷入僵局,因为当您呈现PDF时,会向服务器请求一些额外的资源,如图像和css,看起来只有一个线程.如何配置Rails开发服务器以运行多个工作线程?非常感谢。 最佳答案 我找到的最简单的解决方案是unicorn.geminstallunicorn创建一个unicorn.conf:worker_processes3然后使用它: