今天我们要完成上一篇博客中接口的设计,链接为 从零开发一款APP 一、Java Web后端注册接口的设计 。上一篇的大致设计为,客户端传入用户名、密码、昵称,然后服务端进行验证,根据不同的情况返回不同的结果。
首先,根据上一节写的请求接口的请求示例,我们服务器端需要从请求中读取到请求的结果,请求的示例为 {s:{"uname"=xxx,"upwd"=xxx,"nkname"=xxx}} ,但是如果我们每次接受到请求时,都需要先得到s对应的JsonObject,然后在去一个一个的解析,这样实在是太痛苦了,而且这样做重复代码太多。所以,我们可以把json解析放到父类中。
我们知道,java web中每次请求都会映射到对应的HttpServlet 类对象中的service()方法中,在HttpServlet 的service()方法源码中,会根据请求的类型,调用自身不同的方法来处理,比如如果得到请求类型为get,则调用自己的doGet()方法,如果请求类型为post,则调用自己的doPost()方法。所以,我们可以覆盖HttpServlet 的service()方法,在里面做出json解析。我们json解析使用的是阿里巴巴开源的fastjson。
下面为BaseServlet的源码:
/**
* 所有HttpServlet的基类,会把请求中的加密字符串取出来并进行解密 (当前还尚未解密)
* @author liuheng
*
*/
public class BaseServlet extends HttpServlet {
//请求的Json
protected JSONObject requestJson;
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String securityString = req.getParameter("s");
System.out.println("获取的加密String为" + securityString);
requestJson = JSON.parseObject(URLDecoder.decode(securityString, "UTF8"));
super.service(req, resp);
}
}
可以看出,我们直接将s对应的字符串解析成JSONObject,这样子类就可以使用了。需要注意的是,我们所有的请求都使用post方式,所以,子类只需要复写doPost()方法即可。
获取到请求参数后,就应该去做相应的逻辑处理,比如判断是否有重复用户名,是否有重复昵称,当判断都没有时,进行写入,这时候如果写入失败,还需要返回失败的标志位。我们统称这种逻辑处理类为Service类。
仅仅一个Service类是不够的,我们还需要进行数据库访问的DAO类,DAO模式是标准的J2EE设计模式之一.开发人员使用这个模式把底层的数据访问操作和上层的商务逻辑分开,即,我们使用DAO类来将访问数据库的代码进行封装,可以直接调用DAO类来获取到我们需要的对象,而无需考虑数据库内部的实现。
实体类为:
public class User extends IdEntity {
private String name;
private String password;
private String nickname;
private String token;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
UserDAO接口为:
public interface UserDao {
/**
* 将User保存至数据库中
*
* @param connection
* @param user
*/
public void save(Connection connection, User user) throws SQLException;
/**
* 查询数据库中是否有对应的UserName,如果有,返回对应id,没有,返回0
*
* @param connection
* @param userName
* @return
*/
public int queryUserName(Connection connection, String userName) throws SQLException;
/**
* 根据User查询数据库中相应的id的password是否正确。如果正确,返回对应的id,否则返回0
*
* @param connection
* @param user
* @return
* @throws SQLException
*/
public int queryPassWord(Connection connection, int id, String password) throws SQLException;
/**
* 向指定Id的User中更新token
*
* @param connection
* @param userId
* @param token
* @throws SQLException
*/
public void updateToken(Connection connection, int userId, String token) throws SQLException;
/**
* 根据token获取到用户的id,如果没有,返回0
* @param connection
* @param token
* @throws SQLException
*/
public int getUserId(Connection connection, String token) throws SQLException;
}
其相应的实现类为:
public class UserDaoImplement extends BaseDaoImplement implements UserDao {
@Override
public void save(Connection connection, User user) throws SQLException {
PreparedStatement statement = connection
.prepareCall("insert into tal_user (name,password,nickname) values(?,?,?)");
statement.setString(1, user.getName());
statement.setString(2, user.getPassword());
statement.setString(3, user.getNickname());
statement.execute();
}
public int queryUserName(Connection connection, String userName) throws SQLException {
PreparedStatement statement = connection.prepareCall("Select * from tal_user where name = ?");
statement.setString(1, userName);
ResultSet set = statement.executeQuery();
// 下一个不为空,说明数据库中包含有此字段,则返回此条数据的id
if (set.next()) {
return set.getInt("id");
} else {
return 0;
}
}
@Override
public int queryPassWord(Connection connection, int id, String password) throws SQLException {
PreparedStatement statement = connection.prepareCall("Select * from tal_user where id = ? and password = ?");
statement.setInt(1, id);
statement.setString(2, password);
ResultSet set = statement.executeQuery();
// 下一个不为空,说明数据库中包含有此字段,则返回此条数据的id
if (set.next()) {
return set.getInt("id");
} else {
return 0;
}
}
@Override
public void updateToken(Connection connection, int userId, String token) throws SQLException {
PreparedStatement statement = connection.prepareCall("update tal_user set token = ? where id = ?");
statement.setString(1, token);
statement.setInt(2, userId);
statement.execute();
}
@Override
public int getUserId(Connection connection, String token) throws SQLException {
PreparedStatement statement = connection.prepareCall("select * from tal_user where token = ?");
statement.setString(1, token);
statement.executeQuery();
ResultSet set = statement.executeQuery();
if (set.next()) {
return set.getInt("id");
} else {
return 0;
}
}
}
DAO类写好后,我们就可以书写逻辑Service类,比如如果我们需要判断是否有相应的用户名,那么就应该调用DAO类的queryUserName()方法,如果返回为0,则有相同用户名。
下面为LoginService类源码:
public class LoginService {
private UserDao mUserDao = new UserDaoImplement();
private static final int RESULT_NULL_USERNAME = 1,RESULT_WRONG_PASSWORD = 2;
public LoginResult login(String userName, String passWord) {
Connection connection = null;
LoginResult result = new LoginResult();
connection = ConnectionFactory.getInstance().makeConnection();
try {
//1、先判断是否有相应的用户名
int id = mUserDao.queryUserName(connection, userName);
if (id == 0) {
result.setCode(RESULT_NULL_USERNAME);
return result;
}
//2、在判断密码是否正确
int userId = mUserDao.queryPassWord(connection, id, passWord);
if (userId == 0) {
result.setCode(RESULT_WRONG_PASSWORD);
return result;
}
//3、设置相应的token
long currentTime = System.currentTimeMillis();
String token = userId+"_"+currentTime;
mUserDao.updateToken(connection, userId, token);
result.setCode(0);
result.setToken(token);
return result;
} catch (SQLException e) {
e.printStackTrace();
result.setCode(100);
return result;
}
}
}
写好了
LoginService类之后,我们的服务端代码就接近完成了,我们还需要做的就是写一个BaseServlet的子类,然后复写其onPost()方法,得到请求的参数,然后调用LoginService类获取返回的参数,进行包装后使用fastjson解析成json返回即可。
下面为LoginServlet类的源码:
@WebServlet("/Login")
public class LoginServlet extends BaseServlet {
private static final long serialVersionUID = 7245361420327420429L;
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String userName = requestJson.getString("uname");
String passWord = requestJson.getString("upwd");
System.out.println("请求的userName为" + userName + "\n请求的passWord为" + passWord);
LoginService service = new LoginService();
LoginResult loginResult = service.login(userName, passWord);
Map map = new HashMap<>();
map.put("result", loginResult.getCode());
//如果成功,还需要加上token
if (loginResult.getCode() == 0) {
Map dataMap = new HashMap<>();
dataMap.put("token", loginResult.getToken());
map.put("data", dataMap);
}
String result = JSON.toJSONString(map);
System.out.println("结果为"+result);
PrintWriter printWriter = resp.getWriter();
printWriter.write(result);
printWriter.close();
}
}
上面写出了登陆接口实现的大部分分析,但是还时有一些源码没有贴出来,大家可以访问此链接: 服务器端代码github地址
,来得到和运行服务器端的源码。另外 ,Android端的代码也写好了登陆、注册的界面,登陆的功能也已经实现。其地址为 Android端代码github地址
。欢迎大家star 。