[Spring cloud realizes advertising system step by step] 6. Service implementation & Zuul configuration & Test

Keywords: Java curl Spring JSON Database

Design and Implementation of DAO Layer

Here we use Spring DATA JPA to implement database operations, of course, you can also use Mybatis, which is the same, we still use user table operations as an example:

/**
 * AdUserRepository for User Data Base Operating Interface
 * Inherited from JpaRepository < AdUser, Long>, the first parameter AdUser represents the class definition of the entity class to be operated on, and the second parameter Long represents the primary key type of the class.
 *
 * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang</a>
 */

public interface AdUserRepository extends JpaRepository<AdUser, Long> { 
    /**
     * Getting users by user name
     *
     * @param username Name
     * @return User Object
     */
    AdUser findByUserName(String username);

    List<AdUser> findAllByUserName(String userName);
}
  • If we only inherit Jpa Repository and do not implement a specific method of operation, we can also use its default method to do CRUD operations, as follows:

Functional Service Implementation

Create service package, still take user operation as an example, create com.sxzhongf.ad.service.IUserService and com. sxzhongf. ad. service. impl. UserService Impl, UserService Impl implements IUserService.

  1. Create the IUserService interface
/**
 * IUserService for User service
 *
 * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | Ruochuan</a>
 */
public interface IUserService {
    /**
     * Creating User Interface
     *
     * @param userRequestVO {@link UserRequestVO}
     * @return {@link UserResponseVO}
     * @throws AdException error
     */
    UserResponseVO createUser(UserRequestVO userRequestVO) throws AdException;

    List<AdUser> findAllByUserName(String userName);
}
  1. Using the IUserService interface
/**
 * UserServiceImpl for User service
 *
 * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | Ruochuan</a>
 */
@Slf4j
@Service
public class UserServiceImpl implements IUserService {

    private final AdUserRepository userRepository;

    @Autowired
    public UserServiceImpl(AdUserRepository userRepository) {
        this.userRepository = userRepository;
    }

    /**
     * Creating Users
     *
     * @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);
        }
        //Duplicate checking
        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);
    }
}
  1. Create data transfer objects (dto/vo)

    In fact, many people here will be particularly depressed, do not know what the difference between these names, personal advice is that you do not have to entanglement, dto(data transfer object), is that we pass on the object at all levels, vo in the display layer operation object. But this is just a name. Its essence is an object. Can you pass it to the DAO layer? Of course, you can pass individual fields. Therefore, there is no need to over-entanglement with this information. Sometimes it is counterproductive to chew words.

/**
 * UserRequestVO for Creating User Request Object VO
 *
 * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | Ruochuan</a>
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserRequestVO {
    private String userName;
    public boolean validate() {
        return !StringUtils.isEmpty(userName);
    }
}

---
  
/**
 * UserResponseVO for User Response VO
 *
 * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | Ruochuan</a>
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserResponseVO {
    private Long userId;
    private String userName;
    private String token;
    private Date createTime;
    private Date updateTime;
}
  1. Because the error message may be the same, we extract a constant class to encapsulate it.
/**
 * Constants for TODO
 *
 * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | Ruochuan</a>
 */
public class Constants {
    /**
     * General error information exception class
     */
    public static class ErrorMessage {
        public static final String REQUEST_PARAM_ERROR = "Request parameter exception";
        public static final String USER_EXIST = "User already exists";
        public static final String USER_NOT_EXIST = "user does not exist";
    }
}
  1. Create a tool class com.sxzhongf.ad.common.utils.CommonUtils under Common Project to encrypt user username md5 for token information.
/**
 * CommonUtils for Tool class
 *
 * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | Ruochuan</a>
 */
@Slf4j
public class CommonUtils {
    /**
     * md5 encryption
     */
    public static String md5(String value) {
        return DigestUtils.md5Hex(value).toUpperCase();
    }
}

Refer to the implementation of creating users and implement other table operations in turn.

Controller Implementation

Still take the implementation of user functions as an example:

/**
 * UserController for User controller
 *
 * @author <a href="mailto:magicianisaac@gmail.com">Isaac.Zhang | Ruochuan</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));
    }
}
Configuring the Advertising Playing System in the Gateway

In the configuration of the delivery system, we configure a path such as server.servlet.context-path:/ad-sponsor, which means that all paths requesting the current system need to have ad-sponsor, such as http://xxx/ad-sponsor/user/get?username=yyy, which is necessary for gateway requests. Based on 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
##############################################
# The following are important information
zuul:
  ignored-services: '*' # Filter all requests except the services declared in routes below
  # Configuring Gateway Routing Rules
  routes:
    sponsor: #Customize service routing name in routing
      path: /ad-sponsor/**
      serviceId: mscx-ad-sponsor #Microservice name
      strip-prefix: false
    search: #Customize service routing name in routing
      path: /ad-search/**
      serviceId: mscx-ad-search #Microservice name
      strip-prefix: false
  prefix: /gateway/api
  strip-prefix: false #Instead of intercepting the path set by prefix: /gateway/api, default forwarding intercepts the prefix of the configuration
Test
  • Direct Access Delivery System

    Call curl-G http://localhost:7000/ad-sponsor/user/get?Username=Isaac%20Zhang and return the result:

{
  code: 0,  // Unified Success Marking
  message: "success", // Unified Processing Result message
  data: [  // Specific object information
    {
      userId: 10,
      userName: "Isaac Zhang",
      token: "2D3ABB6F2434109A105170FB21D00453",
      userStatus: 1,
      createTime: 1561118873000,
      updateTime: 1561118873000
    }
  ]
}
  • Called through Gateway

    Because I added the prefix prefix: /gateway/api in the gateway configuration, we need to add this prefix information when accessing, otherwise we will report 404 errors.

    Curl-G http://localhost:1111/gateway/api/ad-sponsor/user/get?Username=Isaac%20Zhang, we found that the results did not show up as we expected.

    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? Let's check 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 see clearly that ValidateTokenFilter: access token is empty, why is there such an error? That's because I added an intercept when configuring the gateway:

    /**

*

*/
@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) edits the returned body content

          return null;
      }
      log.info("access token ok");
      return null;
  }

}

Looking at the code, we found that the `accessToken'parameter would be obtained from `RequestHeader'. We did not provide it, of course, it would be wrong. Next, we provide the parameters to try again:

bogon:~ zhangpan$ curl -H "accessToken:true" http://localhost:1111/gateway/api/ad-sponsor/user/get?username=Isaac%20Zhang
- Return
{"code":0,"message":"success","data":[{"userId":10,"userName":"Isaac Zhang","token":"2D3ABB6F2434109A105170FB21D00453","userStatus":1,"createTime":1561118873000,"updateTime":1561118873000}]}

Posted by chipmunken on Mon, 29 Jul 2019 05:04:28 -0700