DAO layer design implementation
Here we use Spring DATA JPA
to implement database operations, of course, we can use Mybatis
all the same, we are still in a user table operation as an example:
/**
* AdUserRepository for 用户数据库操作接口
* 继承自JpaRepository<AdUser, Long>,第一个参数AdUser代表当前要操作的实体类的class定义,第二个参数Long表示该类的主键类型
*
* @author <a href="mailto:[email protected]">Isaac.Zhang</a>
*/
public interface AdUserRepository extends JpaRepository<AdUser, Long> {
/**
* 根据用户名称获取用户
*
* @param username 名称
* @return 用户对象
*/
AdUser findByUserName(String username);
List<AdUser> findAllByUserName(String userName);
}
-
The default JPARepository implementation method, if we just inherited
JpaRepository
without the achievement of specific methods of operation, we can also do it by using the default methodCRUD
of operation, as follows:
Service implementation functions
Create a service package, the user is still operating, for example, create com.sxzhongf.ad.service.IUserService
and com.sxzhongf.ad.service.impl.UserServiceImpl
, UserServiceImpl
we realized IUserService
.
- Creating
IUserService
interfaces
/**
* IUserService for 用户service
*
* @author <a href="mailto:[email protected]">Isaac.Zhang | 若初</a>
*/
public interface IUserService {
/**
* 创建用户接口
*
* @param userRequestVO {@link UserRequestVO}
* @return {@link UserResponseVO}
* @throws AdException 错误
*/
UserResponseVO createUser(UserRequestVO userRequestVO) throws AdException;
List<AdUser> findAllByUserName(String userName);
}
- Use
IUserService
Interface
/**
* UserServiceImpl for 用户service
*
* @author <a href="mailto:[email protected]">Isaac.Zhang | 若初</a>
*/
@Slf4j
@Service
public class UserServiceImpl implements IUserService {
private final AdUserRepository userRepository;
@Autowired
public UserServiceImpl(AdUserRepository userRepository) {
this.userRepository = userRepository;
}
/**
* 创建用户
*
* @param userRequestVO {@link UserRequestVO}
* @return result {@link UserResponseVO}
*/
@Override
@Transactional
public UserResponseVO createUser(UserRequestVO userRequestVO) throws AdException {
if (!userRequestVO.validate()) {
log.error("Request params error: {}", userRequestVO);
throw new AdException(Constants.ErrorMessage.REQUEST_PARAM_ERROR);
}
//查重
AdUser existUser = userRepository.findByUserName(userRequestVO.getUserName());
if (existUser != null) {
log.error("{} user is not exist.", userRequestVO.getUserName());
throw new AdException(Constants.ErrorMessage.USER_EXIST);
}
AdUser user = userRepository.save(new AdUser(userRequestVO.getUserName(), CommonUtils.md5(userRequestVO.getUserName())));
log.info("current user is : {}", user);
return new UserResponseVO(user.getUserId(), user.getUserName(), user.getToken(),
user.getCreateTime(), user.getUpdateTime());
}
@Override
public List<AdUser> findAllByUserName(String userName) {
return userRepository.findAllByUserName(userName);
}
}
-
Creating a Data Transfer Object (dto / vo)
In fact, a lot of people here will be particularly depressed, do not know what difference these names, personal recommendation is that we do not tangle, dto (data transfer object), that represents the object of our passing in each layer, presentation layer objects vo in operation. But this is just a name, it's essentially a object, you pass to the DAO layer can it? Sure, you pass a separate field are possible. So, no need too tangled this information quibble sometimes it will be counterproductive.
/**
* UserRequestVO for 创建用户请求对象VO
*
* @author <a href="mailto:[email protected]">Isaac.Zhang | 若初</a>
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserRequestVO {
private String userName;
public boolean validate() {
return !StringUtils.isEmpty(userName);
}
}
---
/**
* UserResponseVO for 用户响应VO
*
* @author <a href="mailto:[email protected]">Isaac.Zhang | 若初</a>
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserResponseVO {
private Long userId;
private String userName;
private String token;
private Date createTime;
private Date updateTime;
}
- Because the error message may be the same, then we extract a constant class to encapsulate.
/**
* Constants for TODO
*
* @author <a href="mailto:[email protected]">Isaac.Zhang | 若初</a>
*/
public class Constants {
/**
* 通用错误信息异常类
*/
public static class ErrorMessage {
public static final String REQUEST_PARAM_ERROR = "请求参数异常";
public static final String USER_EXIST = "用户已存在";
public static final String USER_NOT_EXIST = "用户不存在";
}
}
- Create a utility class in Common Project below
com.sxzhongf.ad.common.utils.CommonUtils
, used to be md5 encrypted user username to obtain the token information.
/**
* CommonUtils for 工具类
*
* @author <a href="mailto:[email protected]">Isaac.Zhang | 若初</a>
*/
@Slf4j
public class CommonUtils {
/**
* md5 加密
*/
public static String md5(String value) {
return DigestUtils.md5Hex(value).toUpperCase();
}
}
Reference to create an implementation of the user, in order to achieve other table operations.
Controller implementation
Still to implement user functions, for example:
/**
* UserController for 用户controller
*
* @author <a href="mailto:[email protected]">Isaac.Zhang | 若初</a>
*/
@RestController
@Slf4j
@RequestMapping("/user")
public class UserController {
@Autowired
private IUserService userService;
@PostMapping(path = "/create")
public UserResponseVO createUser(@RequestBody UserRequestVO requestVO) throws AdException {
log.info("ad-sponsor: createUser -> {}", JSON.toJSONString(requestVO));
return userService.createUser(requestVO);
}
@GetMapping(path = "/get")
public CommonResponse getUserList(@Param(value = "username") String username) throws AdException {
log.info("ad-sponsor: getUserList -> {}", JSON.toJSONString(username));
return new CommonResponse(userService.findAllByUserName(username));
}
}
Configuration advertising system in the gateway
We put the system in the configuration, the configuration of server.servlet.context-path:/ad-sponsor
such a path, the current system means that all requests need the path with the ad-sponsor, for example: http://xxx/ad-sponsor/user/get?username=yyy
It is necessary to request the gateway. According to the above, we configure our current delivery system in the Gateway service:
spring:
application:
name: ad-gateway-zuul
server:
port: 1111
eureka:
client:
service-url:
defaultZone: http://server1:7777/eureka/,http://server2:8888/eureka/,http://server3:9999/eureka/
instance:
hostname: ad-gateway-zuul
##############################################
# 以下为重要信息
zuul:
ignored-services: '*' # 过滤所有请求,除了下面routes中声明过的服务
# 配置网关路由规则
routes:
sponsor: #在路由中自定义服务路由名称
path: /ad-sponsor/**
serviceId: mscx-ad-sponsor #微服务name
strip-prefix: false
search: #在路由中自定义服务路由名称
path: /ad-search/**
serviceId: mscx-ad-search #微服务name
strip-prefix: false
prefix: /gateway/api
strip-prefix: false #不对 prefix: /gateway/api 设置的路径进行截取,默认转发会截取掉配置的前缀
Test
-
Direct access to the delivery system
Call
curl -G http://localhost:7000/ad-sponsor/user/get?username=Isaac%20Zhang
, return the result:
{
code: 0, // 统一成功标示
message: "success", // 统一处理结果message
data: [ // 具体的对象信息
{
userId: 10,
userName: "Isaac Zhang",
token: "2D3ABB6F2434109A105170FB21D00453",
userStatus: 1,
createTime: 1561118873000,
updateTime: 1561118873000
}
]
}
-
By calling gateway
Because I added the prefix in the gateway configuration
prefix: /gateway/api
, therefore, we need to add the time of the visit on the prefix information, otherwise it will report a 404 error.curl -G http://localhost:1111/gateway/api/ad-sponsor/user/get?username=Isaac%20Zhang
We found that the results do not show it as we imagine.bogon:~ zhangpan$ http://localhost:1111/gateway/api/ad-sponsor/user/get?username=Isaac%20Zhang -bash: http://localhost:1111/gateway/api/ad-sponsor/user/get?username=Isaac%20Zhang: No such file or directory
why? We take a look at the log:
2019-07-27 20:44:19.093 INFO 4766 --- [nio-1111-exec-4] c.s.a.g.filter.ValidateTokenFilter : GET request to http://localhost:1111/gateway/api/ad-sponsor/user/get 2019-07-27 20:44:19.093 WARN 4766 --- [nio-1111-exec-4] c.s.a.g.filter.ValidateTokenFilter : access token is empty 2019-07-27 20:44:19.098 INFO 4766 --- [nio-1111-exec-4] c.s.ad.gateway.filter.AccessLogFilter : Request "/gateway/api/ad-sponsor/user/get" spent : 0 seconds. 2019-07-27 20:48:37.801 INFO 4766 --- [trap-executor-0] c.n.d.s.r.aws.ConfigClusterResolver : Resolving eureka endpoints via configuration
We can clearly see
ValidateTokenFilter : access token is empty
why there is such an error it? That is because when I configure the gateway, add an interception:/** * ValidateTokenFilter for 服务token校验 * * @author <a href="mailto:[email protected]">Isaac.Zhang</a> */ @Slf4j @Component public class ValidateTokenFilter extends ZuulFilter { ... @Override public Object run() throws ZuulException { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); log.info(String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString())); Object accessToken = request.getHeader("accessToken"); //.getParameter("accessToken"); if (accessToken == null) { log.warn("access token is empty"); ctx.setSendZuulResponse(false); ctx.setResponseStatusCode(401); // ctx.setResponseBody(body)对返回body内容进行编辑 return null; } log.info("access token ok"); return null; } }
Observe the code we found from'll
RequestHeader
get theaccessToken
parameters, we have not provided, of course, it would be incorrect report. Next, we try to provide the parameters:bogon:~ zhangpan$ curl -H "accessToken:true" http://localhost:1111/gateway/api/ad-sponsor/user/get?username=Isaac%20Zhang ---返回 {"code":0,"message":"success","data":[{"userId":10,"userName":"Isaac Zhang","token":"2D3ABB6F2434109A105170FB21D00453","userStatus":1,"createTime":1561118873000,"updateTime":1561118873000}]}
At this point, our ad delivery system has a simple function to achieve full completion, and can be forwarded through the gateway.