[JavaEE] E-commerce spike project · Chapter 3 · User module development

foreword

Don't forget that the original intention of rewatching the video this time is to complete the function of Android login and registration with the help of local MySQL
Chapter 3. Basic process of user module development:
overall architecture layering -> user information layering processing -> encapsulation return information —> otp mobile phone verification code development —> front-end page development —> login registration function development

In the last chapter, we learned:

  1. Build the Maven project
  2. Introduce SpringBoot
  3. Introduce Mybatis
  4. Simple use of SpringMVC

Next, we will learn to use SpringMVC in detail. The specific knowledge points are:

  1. MVC layered architecture

  2. Generic return type

  3. otp verification code generation

  4. Login and registration functions

  5. Validation logic in the project

text

First, the overall structure layering

At the end of the last chapter, we simply used SpringMVC with RESTful style in App.java, but not all logic can be written in App.java, so what needs to be done urgently is to layer first.
Create two new packages, one controller and one service

Create a new UserController in the controller, create a new interface UserService in the service and the corresponding implementation class UserServiceImpl

The Service layer is to return the user model, that is, user information, to the Controller layer, and needs to access the database.

This requires a DAO layer, which Mybatis has already implemented for us.

Their relationship is:

The URL request is sent to the Controller layer,

The Controller layer calls the Service layer,

The Service layer calls the DAO layer,

The DAO layer performs the CURD operation of the database through the mapping mapping of Mybatis (CURD means addition, deletion, modification and search).

Layered summary of the overall architecture:

The Controller layer needs to add @Controller(...), @RequestMapping(...), @CrossOrigin(...) at the class level

The Service layer only needs to add @Service at the class level

For Controller, the annotation is loaded on the subclass, BaseController does not need

For Service, on the annotation loading implementation class, the UserService interface does not need to be

These layers are the years of industrial experience of the predecessors. As the younger generation, they should learn with a humble mind and understand the ideas in them, such as decoupling, scalability, ease of maintenance, single responsibility principle (SRP) and so on.

2. Different processing methods of user model in three layers

The above process is from the View layer to the Model layer. Let's understand the process from the Model layer to the View layer.

  1. DAO layer: The bottom layer is the dataobject that DAO maps from the database one by one. What the DAO layer needs to do is the CURD work of user information.

  2. Service layer: When designing the database, we divided the user information and password into tables because of business requirements, which will lead to the appearance of two dataobjects, UserDO and UserPasswordDO . What the Service layer needs to do is to integrate these two DOs and encapsulate them as models .

  3. Controller layer: The model is obtained from the Service layer , but the specific method needs to be used specifically. For example, in the getUser method of the Controller layer, the front end sends a localhost:8090/user/get request to request user information. The front end does not need to know the user's password, login method and other sensitive information. Therefore, a viewobject layer is needed to mask sensitive information when returning to the front end.

    Question : The model is integrated with the dataobject , and now it needs to be shielded. What is the picture?

    Answer : The specific methods of the Controller layer are used specifically. The getUser method is used to return to the front end, and some information in the model is not used. Therefore, the viewobject is used to shield sensitive information such as user passwords, but the Controller layer is not only a function of getUser, such as There are also functions such as login verification login. At this time, more information of the model is needed for comparison.

A three-level summary of user information:

1. DAO层 --- dataobject,与数据库一一映射
2. Service层 --- model,整合因业务需求而分离的dataobject
3. Controller层 --- viewobject,屏蔽如密码等敏感信息返回给前端

If you can understand the specific role of these three layers, the understanding of the entire project will be of great help.

3. The return value and type of the method in the Controller

We know that the return value of the Controller layer is returned to the front-end page.

However, the return value types of the methods in the Controller will be different, such as returning viewobject, returning null, returning Exception information, and so on. If each method has its own return value type, that design is not very good, and it is not easy to present it to the front end in a unified form (such as a unified JSON format). A good solution is to create a separate layer: response , create a common return value type CommonReturnType, which has two attributes status and data.

status: success means success, fail means failure;

data: It will return correct information viewobject or exception information (exception information includes errorCode and errorMsg) or empty according to the specific business.

The code for CommonReturnType to construct the object is as follows, as the teacher Sanshao said, the duet method:

public static CommonReturnType create(Object result){
    
    
    return CommonReturnType.create(result,"success");
}
public static CommonReturnType create(Object result, String status){
    
    
    CommonReturnType type = new CommonReturnType();
    type.setData(result);
    type.setStatus(status);
    return type;
}

4. Return correct information

The specific implementation of returning the correct information has been written in the CommonReturnType class under the response package. Since it is the correct information, the status is the specified "success", and the data will either return the viewobject or return null according to business needs.

For the design of null, you can take a look at " The Art of Java Null "

With such a structured return value, coupled with the @ResponseBody annotation, it will be serialized into a JSON string that is easily understood by the front end.

For example, after the login verification is successful, it will be returned to the front end

{
    
    
    "status":"success",
    "data":null
}

For another example, after successfully obtaining user information, it will be returned to the front end

{
    
    
    "status":"success",
    "data":{
    
    
        "name":"张三",
        "age":"18",
        ......
    }
}

5. Return the error message

There are many kinds of error information, such as business exception: user does not exist, input data exception, etc., and non-business exception: null pointer exception, etc.; therefore, special design is required to achieve a unified format. We need to separate a layer: error , and then abstract a CommonError interface:

public interface CommonError {
    
    
    public int getErrorCode();
    public String getErrorMsg();
    public CommonError setErrorMsg(String errorMsg);
}

Then create a new business exception class BusinessException extends Exception implements CommonError,

The implementation of this BusinessException class belongs to a design pattern: wrapper business exception class implementation;

Wherever design patterns appear, there are profound connotations. Some of them have connotations at first glance, and some have no connotations at first glance. But one day, looking back, you will find that every design pattern has its subtleties.

Because the exception has two attributes, ErrorCode and ErrorMsg, we encapsulate the exception and abstract it into an enumeration class EmBusinessError

For an introduction to the Enum class, you can refer to: [CSDN] zejian_ "In-depth understanding of Java enumeration types (enum)"

In addition, the blogger who wrote this blog has written a series of articles with in-depth understanding of Java knowledge points, you can visit it.

In this way, when a business exception occurs in the Controller layer, you can use:

throw new BusinessException(EmBusinessError.USER_NOT_EXIST);

Throwing, of course, if you encounter a runtime exception such as java.lang.NullPointException instead of a business exception, the JVM virtual machine will automatically throw it.

But whether it is a business exception that is directly thrown or a runtime exception thrown by the JVM virtual machine, it is only thrown to the container layer of Tomcat, and we cannot handle it at the Controller layer.

For the specific layering of Tomcat, you can read the book "Seeing Through SpringMVC Source Code Analysis and Practice". I remember that it talked about the source code of Tomcat.

Fortunately, SpringBoot has already implemented an annotation for us

@ExceptionHandler(Exception.class)

Add annotations to the method of custom handling exceptions in the Controller layer, and specify the type of exception to be intercepted, so that the exception can be intercepted for custom processing.

The processing method is to first determine whether the thrown Exception is a BusinessException. If so, convert the Exception to a BusinessException, and use CommonReturnType to encapsulate the corresponding status and data.

If it is not a business exception such as BusinessException, it will be classified as an unknown error, and the processing method is also to use CommonReturnType to encapsulate the same errorCode and errorMsg.

For work like handling exceptions, not only UserController needs to be used, but other modules may also have exceptions in the future. Therefore, extract the code into BaseController and let UserController inherit BaseController, so that the code can be reused when extending other modules in the future.

The returned exception JSON format is as follows

{
    
    
    "status":"fail",
    "data":{
    
    
        "errorCode":10001,
        "errorMsg":"未知错误"
    }
}

Summary of return values ​​and types:

  1. CommonReturnType has its own independent layer: response
  2. CommonError, BusinessException, and EmBusinessError also have a separate layer: error
  3. CommonError is an interface with two implementation classes. One is BusinessException, which inherits from Exception and the other is EmBusinessError. It is an enumeration class that specifies the specific errorCode and errorMsg attribute values ​​to construct the BusinessException object.
  4. The specific exception of BusinessException is analyzed in detail, and other non-BusinessException exceptions are unified as unknown errors.
  5. These exceptions will not only occur in UserController, so the exceptions are abstracted into BaseController, and other Controllers inherit BaseController to achieve code reuse.

It is still necessary to understand more about this return value and type. Although this is the second time watching this video, it is still a bit difficult to understand.

Six, otp verification code acquisition

The front end sends a URL request, localhost:8090/user/getotp, the URL is mapped to the Controller layer, the otpCode is generated, bound to the mobile phone number, and finally returned to the user through the SMS channel. Finally, it should be noted that there will be cross-region request problems when ajax requests are sent. The solution provided by SpringBoot is to add the @CrossOrigin annotation at the Controller class level.

At this stage, the teacher did not verify whether the mobile phone number was double registered. I added this feature to it. The process is as follows:

The Controller layer receives the mobile phone number entered by the user and calls the getUserByTelphone() method of the Service layer. The Service layer queries the database through selectByTelphone of userDOMapper. If the mobile phone number already exists, it returns true, otherwise false.

The Controller layer determines whether it has been registered according to the return value of the Service layer. If it is registered repeatedly, it will throw an exception to the front end. If it is not registered, it will go through the normal process of obtaining the verification code.

Summary of otp verification code acquisition:

  1. The Otp verification code is generated by a simple random number.

  2. The Otp verification code is bound to the mobile phone number . The enterprise level is implemented using Redis, but at present this project belongs to the entry level. For the time being, the httpServletRequest object is used to obtain the session method to bind the mobile phone number and the verification code.

  3. The Otp verification code is sent to the front-end method . Because it is a test project, there is no money to access the third-party SMS channel. The teacher directly outputs the bound mobile phone number and verification code on the console. We upgrade here to return to the front end, and then the front end receives the returned JSON value and parses it for automatic filling.

7. User registration process

The user fills in the data on the front end, sends a URL request localhost:8090/user/register, the URL is mapped to the Controller layer, the Controller layer assembles the data into a userModel object and passes it to the Service layer, and the Service layer implements the specific register method. The first thing to do in the register method is to split the userModel, and then call the DAO layer to insert it into the database, paying attention to adding the @Transactional transaction annotation.

Recommended articles on null processing: "java artisan technique - elegant handling of null values"

Regarding the parameter transfer , the front-end Android and the back-end SpringMVC must match the parameter names one by one. For example, I made a mistake, the key of the front-end parameter transfer is username, and the key received by the back-end is name. As a result, it cannot be delivered successfully.

Regarding the primary key foreign key , there is a field user_id in the user_password table, which holds the id of the user_info table as a foreign key. When the Controller layer obtains the data passed by the user, it does not have an id, and it is impossible for the user to know his id in the database when he registers. So we need to go to the UserDOMapper.xml file and manually specify that the id in the insertSelective method is automatically incremented as the primary key. At the same time, it is necessary to query the current id from the database through UserDO after executing the insertSelective method, and then assign it to userModel. Let userModel take it to build userPasswordDO.

Regarding repeated users , we need to fill in the mobile phone number when we register, and the mobile phone number is unique, so when designing the database, we can set the telphone field in the user_info table as a unique index . The reason why there are duplicate users is that in the Service layer, the userDOMapper.insertSelective(userDO); statement in the register method is executed repeatedly. If the unique index is set, an exception will be thrown when the insert is executed again. We can Do a try-catch capture here, and then custom throw an exception that the mobile phone number has been registered .

Regarding cross-domain requests , the problem of cross-domain requests was mentioned when the otp verification code was obtained above . There is only a get request, just write a @CrossOrigin annotation, but the registration here is a POST request, so it needs to be after the @CrossOrigin annotation Add two more parameters, namely @CrossOrigin(allowCredentials = "true", allowedHeaders = "*")

Regarding the password , the password written by the user is a plaintext password, which is sensitive information. The database should not directly store the user's sensitive information. The common method is to perform MD5 encryption at the Controller layer and then pass it to the Service layer for insertion operation.

In fact, there are many optional algorithms for getInstance("MD5"), such as SHA-1, SHA-256, SHA-512. For details, please refer to:

MessageDigest Algorithms

base64 Baidu Encyclopedia

// MD5加密+BASE64编码
public String EncodeByMd5(String str) throws NoSuchAlgorithmException, UnsupportedEncodingException {
    
    
    MessageDigest md5 = MessageDigest.getInstance("MD5");
    BASE64Encoder base64en = new BASE64Encoder();
    String newstr = base64en.encode(md5.digest(str.getBytes("utf-8")));
    return newstr;
}

In other words, these algorithms belonged to the US military at that time, and with the advancement of science and technology and the needs of the people, they were made public.

I don't know the specific design of these algorithms, but the usage is as above, first get a MessageDigest instance of the specified algorithm, and then call md5.digest() to encrypt the UTF-8 encoded string, and the return value of pure MD5 encryption It is a 32-bit string with a fixed length, and finally calls the encode method of BASE64Encoder to encode it to make it an unreadable string. There is no absolute safety in the world, but it is relatively safe in this way. Such a code, without him, can make perfect ears!

Regarding the null processing , first of all, it is clear that the back-end null processing is all at the Service layer. At the beginning, it was the most primitive validation rules. Later, in the section on optimizing validation rules, the teacher gave how to use Hibernate's Validator for optimization, so that the validation rules are universal, so as to realize code reuse. The specific implementation method here is still my first contact, and I need to read it several times to deepen my understanding.

Summary of the user registration process:

At the beginning of the article, I talked about the transformation of user data from the database into a dataobject through Mybatis, and then integrated into a model, and then shielded sensitive information to become a viewobject.

The registration process is the reverse process of the above process, that is, the user fills in the viewobject, converts it into a model, then splits it into dataobjects, and finally inserts it into the database through DAO.

It can be seen from the registration process that what the Controller layer does is to pass the data filled in by the user to the Service layer, and the specific logical judgments are written in the implementation of the Service layer.

In addition, always pay attention to the robustness of the code, and keep in mind the null processing. As a robust project, the front and back ends must have null processing, and the back-end is where the null processing is at the Service layer.

Finally, consider information security. For sensitive user information, such as passwords, it must not be stored in the database in plain text. It must be encrypted at the Controller layer and then passed to the Service layer to perform the insert database operation.

8. User login process

In the user registration process, the back-end Controller layer needs to obtain user input, encapsulate it into a Model, and pass it to the Service layer. The Service layer is split into dataobjects, and the DAO layer is called to insert the DO into the database.

The login and registration processes are similar, except that when calling the DAO layer, it is not an insert operation, but a query operation.

When querying the password, you need to modify two files (Mybatis automatically generates only selectByPrimaryKey)

  1. UserPasswordDOMapper.xml 添加 selectByUserId

  2. UserPasswordDOMapper.java adds selectByUserId

The methods in UserPasswordDOMapper.java are mapped to the database CURD statements in UserPasswordDOMapper.xml one by one.

For other processes, please refer to the registration process, which will not be repeated here.

Nine, the return value of all methods with exceptions in the backend

Our set of code is separated from the front and back ends, and both front and back ends are verified. Some verifications can only be done by the backend,

For example:

  1. Is the phone number repeated?

  2. Whether the verification code is obtained successfully

  3. Whether the login is successful

  4. Whether the registration is successful

  5. Is the parameter legal?

  6. Whether the user's login mobile phone number and password match, etc.

After these verifications, if there is an exception, the corresponding exception will be thrown, the service layer throw Exception will be thrown to the Controller layer that calls the service, and the throw Exception in the Controller will eventually be returned to the front end in the BaseController.

Below I will list the return values ​​of all methods with exceptions in the backend, which will help logical understanding.

Floor class name method name return correct information return exception information
Controller layer BaseController handlerException without return CommonReturnType.create(responseData,“fail”);
Controller layer UserController getUser return CommonReturnType.create(userVO); None, but will throw new BusinessException(EmBusinessError.USER_NOT_EXIST); return via BaseController
Controller layer UserController getOtp return CommonReturnType.create(otpCodeObj, “successGetOtpCode”); None, but will throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "The mobile phone number has been registered repeatedly"); return through BaseController
Controller layer UserController register return CommonReturnType.create(null); None, but will throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "SMS verification code error"); return through BaseController
Controller layer UserController login return CommonReturnType.create(null); None, but will throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR); return via BaseController
Service layer UserServiceImpl register without throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR); return via BaseController throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, result.getErrMsg()); return via BaseController throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "The phone number has been registered repeatedly"); BaseController returns
Service layer UserServiceImpl validateLogin return userModel; throw new BusinessException(EmBusinessError.USER_LOGIN_FAIL); return via BaseController throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR); return via BaseController

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325990507&siteId=291194637