Initial knowledge of springboot Web development

Source: Internet
Author: User
Tags assert log log object object

Using validation annotations to implement form validation

Although the front-end H5 and JS can complete the form of field validation, but this can only be to prevent some small white, wrong operation only. If it is a few people with ulterior motives, it is easy to cross these front-end verification, there is a saying that never trust the client to pass over the data. So after the front-end verification, the backend also need to re-form field validation, to ensure that the data to the back end is correct, compliant. This section provides a brief introduction to how to validate forms when springboot.

First create a Springboot project, where the main configuration of the Pom.xml configuration file is as follows:

    <parent> <groupId>org.springframework.boot</groupId> <artifactid>spring-boot-sta Rter-parent</artifactid> <version>2.0.1.RELEASE</version> <relativePath/> <!--Lo Okup parent from repository-</parent> <properties> <project.build.sourceencoding>utf -8</project.build.sourceencoding> <project.reporting.outputencoding>utf-8</ project.reporting.outputencoding> <java.version>1.8</java.version> </properties> &LT;DEP Endencies> <dependency> <groupId>org.springframework.boot</groupId> < Artifactid>spring-boot-starter-web</artifactid> </dependency> <dependency> & Lt;groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-test</artifacti D> <scope>test</scope>       </dependency> <dependency> <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-aop</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> &LT;GROUPID&GT;ORG.SPRINGFRAMEWORK.BOOT&L        T;/groupid> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>

Create a Pojo class that adds validation annotations to the fields in the class that require validation. The code is as follows:

package org.zero01.domain;import javax.validation.constraints.Min;import javax.validation.constraints.NotNull;public class Student {    @NotNull(message = "学生名字不能为空")    private String sname;    @Min(value = 18,message = "未成年禁止注册")    private int age;    @NotNull(message = "性别不能为空")    private String sex;    @NotNull(message = "联系地址不能为空")    private String address;    public String toString() {        return "Student{" +                "sname=‘" + sname + ‘\‘‘ +                ", age=" + age +                ", sex=‘" + sex + ‘\‘‘ +                ", address=‘" + address + ‘\‘‘ +                ‘}‘;    }    ... getter setter 略 ...}

Create a controller class:

package org.zero01.controller;import org.springframework.validation.BindingResult;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RestController;import org.zero01.domain.Student;import javax.validation.Valid;@RestControllerpublic class StudentController {    @PostMapping("register.do")    public Student register(@Valid Student student, BindingResult bindingResult){        if (bindingResult.hasErrors()) {            // 打印错误信息            System.out.println(bindingResult.getFieldError().getDefaultMessage());            return null;        }        return student;    }}

Start the run class with the following code:

package org.zero01;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class SbWebApplication {    public static void main(String[] args) {        SpringApplication.run(SbWebApplication.class, args);    }}

Use postman for testing, under 18 years of age:

Console Print Results:

未成年禁止注册

case where the non-empty field is empty:

Console Print Results:

学生名字不能为空
Logging request logs using AOP

We all know that the two core modules in spring are AOP and IOC, in which AOP is programming for aspect, which is a programming idea or paradigm, and it is not a language specific syntax.

When we develop our business code, we often have a lot of code that is generic and repetitive, and we can extract it as a slice, put it in a slice class, and do a uniform processing, which is to specify which tangent lines are to be woven into.

For example, like logging, checking whether a user is logged in, checking whether a user has administrator privileges, and so on, is a very common and repetitive function code that can be extracted as a slice. The AOP module in the framework can help us to implement AOP programmatically, making AOP easier.

This section will take the previous section to demonstrate how to use AOP to implement simple logging of HTTP request logs. First create a slice class as follows:

Package Org.zero01.aspect;import Org.aspectj.lang.joinpoint;import Org.aspectj.lang.annotation.*;import Org.slf4j.logger;import Org.slf4j.loggerfactory;import Org.springframework.stereotype.component;import Org.springframework.web.context.request.requestcontextholder;import Org.springframework.web.context.request.servletrequestattributes;import javax.servlet.http.httpservletrequest;@ Aspect@componentpublic class Httpaspect {private static final Logger Logger = Loggerfactory.getlogger (httpaspect.class    );    @Pointcut ("Execution (public * org.zero01.controller.studentcontroller.* (..))") public void log () {} @Before ("Log ()") public void Beforelog (Joinpoint joinpoint) {//Log Format: URL method cli Entip classmethod param servletrequestattributes attributes = (servletrequestattributes) requestcontextholder.getre        Questattributes ();        HttpServletRequest request = Attributes.getrequest ();        Logger.info ("url = {}", Request.getrequesturl ()); Logger.info ("metHod = {} ", Request.getmethod ());        Logger.info ("ClientIP = {}", Request.getremotehost ()); Logger.info ("Class_method = {}", Joinpoint.getsignature (). Getdeclaringtypename () + "." + joinpoint.getsignature ().        GetName ());    Logger.info ("param = {}", Joinpoint.getargs ());  } @AfterReturning (returning = "Object", Pointcut = "log ()") public void Afterreturninglog (Object object) {//    The Print method returns the value content Logger.info ("response = {}", object); }}

Use Postman to access the following methods:

After successful access, the console output logs are as follows:

Thus, we have completed the logging of the HTTP request log.

Encapsulates a unified return data object

We always need to return a variety of different types of data to the client in the Controller class method. For example, sometimes you need to return a collection object, sometimes return a string, sometimes return a custom object, and so on. Also, in a method, different objects may be returned because of different processing results. So what do we do when a method needs to return different objects based on different processing results? One might think that it is possible to set the return type of the method to object, which is true, but the data format returned is not uniform. The front end receives the data, very inconvenient to display, the back end writes the interface document the time also is not good to write. So we should return the format of the data uniformly, and using object cannot do that.

So we need to wrap the returned data in one object uniformly, and then unify the method in the controller class to set the object to the return value type, so that the data format we return has a standard. So let's develop an object like this, start by creating a new enumeration class, because we need to encapsulate some of the common constant data in the enumeration class, and then just modify the enumeration class when the data changes. If you write these constant data hard-coded in code, you have to modify it one by one, it is very difficult to maintain. The code is as follows:

package org.zero01.enums;public enum ResultEnum {    UNKONW_ERROR(-1, "未知错误"),    SUCCESS(0, "SUCCESS"),    ERROR(1, "ERROR"),    PRIMARY_SCHOOL(100, "小学生"),    MIDDLE_SCHOOL(101, "初中生");    private Integer code;    private String msg;    ResultEnum(Integer code, String msg) {        this.code = code;        this.msg = msg;    }    public Integer getCode() {        return code;    }    public String getMsg() {        return msg;    }}

Then we create our return data wrapper object, and before that we need to define a standard format for this data. The format I define here is as follows:

{    "code": 0,    "msg": "注册成功",    "data": {        "sname": "Max",        "age": 18,        "sex": "woman",        "address": "湖南"    }}

Once you have defined the format of the data, you can develop our return data encapsulation object. Create a new class with the following code:

Package Org.zero01.domain;import org.zero01.enums.resultenum;/** * @program: Sb-web * @description: Server Unified return Data encapsulate Object * @    AUTHOR:01 * @create: 2018-05-05 18:03 **/public class Result<t> {//error/Correct code private Integer code;    Hint Information private String msg;    Data returned by private T;        Private Result (Integer code, String msg) {this.code = code;    this.msg = msg;    } private Result (Integer code) {this.code = code;        } private Result (Integer code, String msg, T data) {This.code = code;        this.msg = msg;    This.data = data;    } private Result () {} public Integer GetCode () {return code;    } public void Setcode (Integer code) {this.code = code;    } public String getmsg () {return msg;    } public void Setmsg (String msg) {this.msg = msg;    } public T GetData () {return data;    } public void SetData (T data) {this.data = data; } public static <T> ResulT<t> createbysuccessresultmessage (String msg) {return new result<t> (ResultEnum.SUCCESS.getCode (), msg)    ; } public static <T> result<t> Createbysuccesscoderesult (Integer code, String msg) {return new Resul    T<t> (Code, MSG); } public static <T> result<t> Createbysuccessresult (String msg, T data) {return new result<t>    (ResultEnum.SUCCESS.getCode (), MSG, data); } public static <T> result<t> Createbysuccessresult () {return new result<t> (resultenum.succes    S.getcode ()); } public static <T> result<t> Createbyerrorresult () {return new result<t> (ResultEnum.ERROR.ge    Tcode ()); } public static <T> result<t> Createbyerrorresult (String msg, T data) {return new result<t> (R    EsultEnum.ERROR.getCode (), MSG, data); } public static <T> result<t> Createbyerrorcoderesult (Integer errorCode, String msg) {return new Result<t> (ErrorCode, msg); } public static <T> result<t> createbyerrorresultmessage (String msg) {return new result<t> (Re    SultEnum.ERROR.getCode (), MSG); }}

Then modify our previous registration interface code as follows:

@PostMapping("register.do")public Result<Student> register(@Valid Student student, BindingResult bindingResult) {    if (bindingResult.hasErrors()) {        return Result.createByErrorResultMessage(bindingResult.getFieldError().getDefaultMessage());    }    return Result.createBySuccessResult("注册成功", student);}

Using postman for testing, the data is normal:

The student's name is empty:

As you can see, the returned data format is the same, the value of the Code field is used to determine whether it is a result of a success or an error, the value of the MSG field is a hint, and the data field is the storage of the specific information. With such a unified format, the front end is also parsing this JSON data, our backend in the writing interface document is good to write.

Unified exception Handling

A system or application in the process of running, due to a variety of factors, there must be a case of throwing exceptions. In the event of an exception to the system, the data may not be returned due to service interruption, or it will return a data that does not match the data format we have defined, which is a problem we do not want to appear. So we have to do a globally uniform exception handling, intercept the anomalies that occur in the system, and process them. Let's use a small example as a demonstration.

For example, there is now a business need as follows:

    • Get the age of a student to judge, less than 10, throw an exception and return "primary school" message, greater than 10 and less than 16, throw an exception and return "junior high School" prompt information.

First we need to customize an exception because the default exception constructor accepts only one string type of data, and the data we return has a code, so we have to define an exception class ourselves. The code is as follows:

package org.zero01.exception;/** * @program: sb-web * @description: 自定义异常 * @author: 01 * @create: 2018-05-05 19:01 **/public class StudentException extends RuntimeException {    private Integer code;    public StudentException(Integer code, String msg) {        super(msg);        this.code = code;    }    public Integer getCode() {        return code;    }}

Creates a new ErrorHandler class that is used for global exception interception and handling. The code is as follows:

Package Org.zero01.handle;import Lombok.extern.slf4j.slf4j;import Org.springframework.web.bind.annotation.controlleradvice;import Org.springframework.web.bind.annotation.exceptionhandler;import Org.springframework.web.bind.annotation.responsebody;import Org.zero01.domain.result;import Org.zero01.enums.resultenum;import org.zero01.exception.studentexception;/** * @program: Sb-web * @description:  Global exception Handling class * @author: $ * @create: 2018-05-05 18:48 **///defines a global exception handling class @controlleradvice//Lombok, a note for log printing @slf4jpublic class ErrorHandler {//declares an exception handling method, passing the class of which exception object, means that the method intercepts which exception object includes its subclass @ExceptionHandler (value = exception.class) @Res Ponsebody public Result Exceptionhandle (Exception e) {if (e instanceof studentexception) {Studentex            Ception studentexception = (studentexception) e;        Returns the unified data format return Result.createbyerrorcoderesult (Studentexception.getcode (), Studentexception.getmessage ()); }//Print Exception Log log.error ("[System Exception]{}",e);    Returns the unified data format return Result.createbyerrorcoderesult (ResultEnum.UNKONW_ERROR.getCode (), "Unknown error inside the server"); }}

Note: I use here to the Lombok, if the Lombok unfamiliar, you can refer to my previous write a Lombok quick start

In the previous control class, add the following code:

@Autowiredprivate IStudentService iStudentService;@GetMapping("check_age.do")public void checkAge(Integer age) throws Exception {    iStudentService.checkAge(age);    age.toString();}

We all know that the specific logic is written on the service layer, so create a new service package and create a new interface in the package. The code is as follows:

package org.zero01.service;public interface IStudentService {    void checkAge(Integer age) throws Exception;}

You then create a new class that implements the interface. The code is as follows:

package org.zero01.service;import org.springframework.stereotype.Service;import org.zero01.enums.ResultEnum;import org.zero01.exception.StudentException;@Service("iStudentService")public class StudentService implements IStudentService {    public void checkAge(Integer age) throws StudentException {        if (age < 10) {            throw new StudentException(ResultEnum.PRIMARY_SCHOOL.getCode(), ResultEnum.PRIMARY_SCHOOL.getMsg());        } else if (age > 10 && age < 16) {            throw new StudentException(ResultEnum.MIDDLE_SCHOOL.getCode(), ResultEnum.MIDDLE_SCHOOL.getMsg());        }    }}

Once you've written the above code, you're ready to start testing. age &lt; 10The situation:

age &gt; 10 && age &lt; 16The situation:

The age field is empty and a system exception occurs:

Because we print the log, we also output the log information when the system exception occurs, so that we cannot locate the exception:

As you can see from the above test results, even if we throw an exception, the data format we return is still fixed, so we don't return a different data format because of a system exception.

Unit Test

We usually do a unit test when we finish developing a feature in our project. To ensure the delivery of the project, our code is tested and functional, which is a basic accomplishment of the developer. So this section provides a brief introduction to the test of the service layer and how the controller layer is tested.

The first is the service layer test method, service layer unit test and we usually write the test is not much different. In the project's test directory, create a new test class with the following code:

package org.zero01;import org.junit.Assert;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;import org.zero01.domain.Result;import org.zero01.domain.Student;import org.zero01.service.IStudentService;/** * @program: sb-web * @description: Student测试类 * @author: 01 * @create: 2018-05-05 21:46 **/@RunWith(SpringRunner.class)@SpringBootTestpublic class StudentServiceTest {    @Autowired    private IStudentService iStudentService;    @Test    public void findOneTest() {        Result<Student> result = iStudentService.findOne(1);        Student student = result.getData();        Assert.assertEquals(18, student.getAge());    }}

Execute the test case and run the following result:

Let's modify the age of 15 to simulate a situation where the test does not pass:

The service layer test is relatively simple and is described here. Let's take a look at how the controller layer is tested. There is a handy feature in idea that can help us generate test methods, and in the Controller class that needs to be tested, press Ctrl + Shift + t to quickly create a test method. Below, click Create New Test:

Then select the method you want to test:

The resulting test case code is as follows:

package org.zero01.controller;import org.junit.Test;import static org.junit.Assert.*;public class StudentControllerTest {    @Test    public void checkAge() {    }}

Then we complete this test code, the controller layer of the test and service layer is not the same, because the need to access the URL, rather than directly invoke the method to test. The test code is as follows:

package org.zero01.controller;import org.junit.test;import Org.junit.runner.RunWith; Import Org.springframework.beans.factory.annotation.autowired;import Org.springframework.boot.test.autoconfigure.web.servlet.autoconfiguremockmvc;import Org.springframework.boot.test.context.springboottest;import Org.springframework.test.context.junit4.springrunner;import Org.springframework.test.web.servlet.mockmvc;import Org.springframework.test.web.servlet.request.mockmvcrequestbuilders;import Org.springframework.test.web.servlet.result.MockMvcResultMatchers, @RunWith (Springrunner.class) @ Springboottest@autoconfiguremockmvcpublic class Studentcontrollertest {@Autowired private mockmvc mockmvc; @Test public void Checkage () throws Exception {Mockmvc.perform (Mockmvcrequestbuilders.get ("/check_age.do")// Use GET request. param ("Age", "+"))//url parameter. Andexpect (Mockmvcresultmatchers.status (). IsOk ()); Determine if the returned status is normal}} 

Run the test case because we have previously implemented a function to record HTTP access logs, so you can determine directly from the console's output log whether the interface has been requested:

Unit testing is introduced here, after all, generally we do not test the controller layer in code, but instead use the tools such as postman or Restlet client to test.

Initial knowledge of springboot Web development

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.