目录
项目描述
用springboot创建一个人才招聘网站,我使用的是IDEA 2020版本。
问题描述
在实现用户注册的时候,在页面中输入了账号密码,但出现500的错误提示,如下图所示:
查看控制台报错信息,显示用户账号(J_account)不能为空。
问题分析
我在做完业务层之后就对后端的代码进行过测试,所以可以排除持久层(mapper)和业务层(service)代码的问题,如果没有做测试可以先根据下面的方法测试一下。
排除持久层和业务层之后,我们首先要确认控制层的接口是否与业务层匹配,然后再确认前端数据是否发送成功。
可以直接从控制层(controller)开始测试,如果报错了往前面看,如果没报错往后面看。
解决流程
1. 持久层测试(mapper)
1.1 在test文件夹中如下位置创建一个mapper包,在包中新建一个测试类,命名为UserMapperTests
1.2 对我们mapper层进行测试
查看xml文件的接口,这里我的接口是insert.
<!-- 这是我的xml文件中的插入操作相关代码 -->
<insert id="insert">
INSERT INTO jobseeker (j_account, j_pwd, j_name, j_sex, j_state, j_tel) VALUES (
#{jAccount}, #{jPwd}, #{jName}, #{jSex}, #{jState}, #{jTel}
)
</insert>
测试类中进行测试,对应你的xml接口哦
package com.gp.recruit.mapper;
import com.gp.recruit.demos.web.entity.User;
import com.gp.recruit.demos.web.mapper.UserMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
// @SpringBootTest: 表示标注当前的类是一个测试类,不会随同项目一块打包发送
@SpringBootTest
// @RunWith: 表示启动这个单元测试类(使这个类可以运行),需要传递一个参数,必须是SpringRunner的实例类型
@RunWith(SpringRunner.class)
public class UserMapperTests {
@Autowired
private UserMapper userMapper;
/**
* 单元测试方法:(可以单独独立运行,不用启动整个项目)
* 1. 必须被@Test注解修饰
* 2. 返回值类型必须是void
* 3. 方法的参数列表不能指定任何类型
* 4. 方法的访问修饰符必须是public
*/
@Test
public void insert() {
User user = new User();
user.setjAccount("13579");
user.setjPwd("123");
Integer rows = userMapper.insert(user);
System.out.println(rows);
}
}
这里如果在自动装配声明mapper层接口的时候报错的话,也就是
@Autowired
private UserMapper userMapper;
这里的userMapper如果有一段红色的波浪线,
因为IDEA有检测的功能,接口是不能够直接创建Bean的,具体啥意思我也不懂,反正,解决方法,把权限等级降低:
1.3 点击如下图所示位置,运行测试类
1.4 看控制台是否成功
1.5 如果报错了,则(去找找别的博客吧)
(1)检查数据库是否成功连接;
(2)检查xml文件是否书写有误;
(3)确认你的测试类中传递的参数是否正确,以及传递的内容是否符合要求。
可能还有别的错误原因,根据控制台报错提示慢慢找吧emmmm
如果你的注册功能还有其他操作,也可以一并测试,比如你想在注册之前先查询该账号是否已经存在,在测试类中继续写查询的测试方法就行。
2. 业务层测试(service)
如果我们的持久层没问题的话,那么再进行业务层测试吧
2.1 同样的,我们先在test文件夹中如下位置创建一个service包,在包中新建一个测试类,命名为UserServiceTests
2.2 对service层进行测试,这里我有对处理异常进行测试,如果service层中没有异常处理的相关代码的话,直接进行注册的插入操作就行了,即将try catch的方法省略,直接写try中的代码即可
package com.gp.recruit.service;
import com.gp.recruit.demos.web.entity.User;
import com.gp.recruit.demos.web.service.IUserService;
import com.gp.recruit.demos.web.service.ex.ServiceException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@SpringBootTest
@RunWith(SpringRunner.class)
public class UserServiceTests {
@Autowired
private IUserService userService;
@Test
public void reg() {
try {
User user = new User();
user.setjAccount("721");
user.setjPwd("1235678910");
userService.reg(user);
System.out.println("OK");
} catch (ServiceException e) {
// 获取类的对象,再获取类的名称
System.out.println(e.getClass().getSimpleName());
// 获取异常的具体描述信息
System.out.println(e.getMessage());
}
}
}
2.3 运行这个测试方法
2.4 控制台出现OK即为成功,说明service层没有问题
2.5 如果报错了,则说明service层有问题,可以检查接口参数类型是否匹配啥的,具体的还是根据控制台信息查看错误原因。
3. 检查控制层代码(controller)
应该大概都没问题吧,就,检查一些接口吧emmmm,启动项目后直接访问登录注册的页面。
我们先看一下控制层代码,可以看到我的代码中,如果要访问注册界面,需要先访问users界面
package com.gp.recruit.demos.web.controller;
import com.gp.recruit.demos.web.entity.User;
import com.gp.recruit.demos.web.service.IUserService;
import com.gp.recruit.demos.web.util.JsonResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController // @Controller + @ResponseBody(响应结果以json格式进行数据的响应给到前端)
@RequestMapping("users")
public class UserController extends BaseController {
@Autowired
private IUserService userService;
@RequestMapping("reg")
public JsonResult<Void> reg(User user) {
userService.reg(user);
return new JsonResult<>(OK);
}
}
启动项目后,我们直接在浏览起中打开localhost:8080/users/reg?jAccount=654321&jPwd=123456,也就是说,我在注册页面中输入jAccount的值为654321以及jPwd的值为123456
如果你是先做了前面的测试,那么需要注意修改启动项目
完成浏览器中的插入之后,在数据库中查看数据是否被插入,我这个密码长得比较奇怪是因为我对密码进行加密处理了,没有加密处理的话显示的就是你输入的密码值,也就是123456
如果前面都没报错,这里报错了的话,可以检查一下接口,我这里是需要先访问users,再访问reg的,根据自己的控制层代码确认一下接口。
4. 确认是前端代码的问题
这里只有我出现过的问题,可能还会有别的问题,不过如果前面的测试都没问题,则可以确认是前端代码的问题了。
4.1 确认与控制层的接口
这里我用的是ajax函数,url就是我们需要访问的地址,也就是注册页面,data中获取表单数据,注意这个接口名称需要与你前面的表单id一致。
4.2 表单method属性
首先是需要将input标签放入一个表单内,并且给这个表单加上method属性
<form action="/users/reg" id="form-reg" method="post">
<!-- 手机号输入框 -->
<img src="../img/user.svg" class="icon" alt=""/>
<input id="j_account" type="text" name="jAccount" class="input" placeholder="手机号码" /><br>
<!-- 密码输入框 -->
<img src="../img/square-lock.svg" class="icon" alt=""/>
<input id="j_pwd" type="Password" name="jPwd" class="input" placeholder="密码" /><br>
<!-- 重复密码输入框 -->
<img src="../img/square-lock.svg" class="icon" alt=""/>
<input id="j_pwd_s" type="Password" class="input" placeholder="再次输入密码" /><br>
<span id="msg" class="emptyWarn"></span><br>
</form>
我一开始忘记加这个属性了,注册方法一般使用POST,这是我在看上去似乎比较官方的网站上看到的,总之,注册使用POST就没错了。
4.3 input标签的name属性
这个name属性就是对应的接口,需要与你的参数名保持一致
4.4 参数命名不规范导致无法接收数据
在数据库中我们经常会出现如 j_account 这样的字段,但似乎在java中并不支持这种命名方式,需要使用大头命名法(应该是这个名字),即 jAccount,但在数据库中并没有区分大小写,即,如果你在数据库中设置字段名为 jAccount,那么它在java中就变成了 jaccount,并且还会给你画一条波浪线提示你拼写错误。
那么如果我的数据库中字段是如 j_account 这种形式的怎么办呢?
需要在mapper层中,注册方式对应的xml文件中加入自定义映射规则
<!-- 自定义映射规则:resultMap标签来完成映射规则的定义 -->
<!--
id属性:标签给这个映射规则分配一个唯一的id值,对应的就是resultMap=“id的属性值”
type属性:取值是一个类,表示的是数据库中的查询结果与java中哪个实体类进行结果集的映射
-->
<resultMap id="UserEntityMap" type="com.gp.recruit.demos.web.entity.User">
<!-- 将表的字段和类的属性不一致的字段进行匹配指定,名称一致的字段可以省略不写 -->
<!--
配合完成名称不一致的映射:
column属性:表示表中的资源名称
property属性:表示类中的属性名称
-->
<!-- 在定义映射规则时,主键不可省略 -->
<result column="j_account" property="jAccount"></result>
<result column="j_pwd" property="jPwd"></result>
<result column="j_name" property="jName"></result>
<result column="j_sex" property="jSex"></result>
<result column="j_state" property="jState"></result>
<result column="j_tel" property="jTel"></result>
</resultMap>
然后修改实体类,顺便再检查一下其他地方的参数名
/** 用户的实体类 */
public class User implements Serializable {
private String jAccount;
private String jPwd;
private String salt;
private String jName;
private Integer jSex;
private Integer jState;
private String jTel;
// get和set等方法已省略
}
4.5 其他原因
如果以上都没能解决你的问题,可以考虑一下:
(1) 确保前端发送的请求中参数名称与实体类中字段名称一致,且数据类型匹配;
(2)如果使用了JavaScript框架(jQuery、axios等),确保构建对象正确并发送了数据。
(3)这是我在做的时候知道的可能出现的问题,如果至此你的问题还没有解决,那就只能祝你好运了。