Spring-based RESTful API development: making web applications more open and flexible

RESTful API development based on Spring: making web applications more open and flexible

  • 1. RESTful API
    • 1. What is an API?
    • 2. What is REST
    • 3. What is a RESTful API?
  • 2. Introduction to Spring Framework
    • 1. Basic concepts of the Spring framework
    • 2. Advantages of the Spring Framework
    • 3. Modules of the Spring Framework
  • 3. Spring MVC framework
    • 1. Introduction to SpringMVC Framework
    • 2. The basic process of the SpringMVC framework
    • 3. The core components of the SpringMVC framework
  • 4. Design of RESTful API
    • 1. Design principles of RESTful API
    • 2. RESTful API naming method
    • 3. URL specification of RESTful API
  • Five, SpringBoot framework
    • 1. Basic concepts of SpringBoot
    • 2. SpringBoot configuration method
    • 3. Advantages of Spring Boot
  • Six, use the Spring framework to develop RESTful API
    • 1. Create a Spring Boot project
    • 2. Add SpringBoot related dependencies
    • 3. Create a RESTful API
    • 4. Configure the RESTful API
  • Seven, using the Spring framework to achieve RESTful API security
    • 1. Authentication method based on HTTP protocol
      • basic authentication
      • digest authentication
    • 2. OAuth2-based authentication method
    • 3. JWT-based authentication method
  • 8. Common problems and solutions
    • 1. How to handle HTTP requests and responses
    • 2. How to handle exceptions
    • 3. How to troubleshoot RESTful API performance problems

1. RESTful API

1. What is the API

The full name of API is Application Programming Interface, that is, application programming interface. In computer programming, API is some pre-defined functions, the purpose is to provide a communication interface between application programs so that systems can communicate with each other, and applications can communicate with each other.

2. What is REST

REST (Representational State Transfer) is the state transfer of the presentation layer. It is a lightweight transmission method based on the HTTP protocol and is often used in Web service design.

The REST style opposes converting all operations into CURD (Create, Update, Read, Delete) operations, and advocates resource-oriented design, where resources can be entity data files and so on.

3. What is RESTful API

RESTful API is a Web API built on the HTTP protocol. It follows the REST design specification and uses HTTP requests to communicate through the network to implement operations such as adding, deleting, modifying, and checking resources. Compared with traditional Web API, RESTful API is more flexible, scalable and maintainable, and has become one of the preferred technologies for modern Web services.

2. Introduction to Spring Framework

1. Basic concepts of Spring framework

The Spring Framework is an open source enterprise-level application development framework that helps developers implement loosely coupled, modularized, and reusable code through IoC (Inversion of Control) and AOP (Aspect Oriented Programming) technologies.

2. Advantages of the Spring Framework

  • The loose coupling between codes can be realized through the IoC mechanism, which reduces the coupling degree between programs;
  • The AOP mechanism can realize unified processing of system-level functions such as logs, security, and transactions, which improves the maintainability and scalability of the system;
  • The modular design of the Spring framework enables developers to use the required modules without introducing all modules, which improves the scalability of the system.

3. Spring framework modules

The Spring framework is divided into more than 20 modules including:

  • Core Container: Support for IoC and DI functions
  • Spring AOP and Instrumentation?
  • Data Access and Integration (Data Access/Integration): supports technologies such as JDBC, ORM, transaction management and NoSQLe
  • Message: Support Java-based message service
  • Test (Test): Provides a Mock module for automated testing
  • Web: Includes modules such as Web MVC, Web sockets, and Web testing

Among them, Spring MVC is a Web framework in the Spring framework that helps developers realize the design, development and deployment of Web applications through the MVC (Model-View-Controller) model.

3. SpringMVC Framework

1. Introduction to SpringMVC Framework

SpringMVC is a module in the Spring framework and is a web framework based on the MVC pattern, through which web applications can be easily built.

2. Basic process of SpringMVC framework

The basic process of the SpringMVC framework is as follows:

/* DispatcherServlet is the front controller
 * Handle all requests and responses
 */
public class DispatcherServlet {<!-- -->

  // 1. The client sends a request to the front controller DispatcherServlet
  public void service(HttpServletRequest request, HttpServletResponse response) {<!-- -->

    // 2. After receiving the request, DispatcherServlet calls HandlerMapping to resolve the corresponding Handler of the request
    Handler handler = HandlerMapping. getHandler(request);

    // 3. The parser returns a Handler, that is, the processor, which will be processed by the DispatcherServlet
    ModelAndView mv = handler. handle(request, response);

    // 5. After receiving the ModelAndView, the DispatcherServlet renders the data to the ViewResolver to generate the data
    View view = ViewResolver.resolve(mv.getViewName());
    view.render(mv.getModel(), request, response);
  }
}

/* The function of HandlerMapping is to map to the corresponding Handler processor according to the URL
 * @RequestMapping annotation is used to specify the URL
 */
public class HandlerMapping {<!-- -->

  // HandlerMapping mapping table
  private static Map<String, Handler> mapping = new HashMap<>();

  // Initialize the mapping table
  static {<!-- -->
    mapping. put("/list", new ListHandler());
    mapping. put("/detail", new DetailHandler());
  }

  // Get the Handler processor according to the URL
  public static Handler getHandler(HttpServletRequest request) {<!-- -->
    String url = request. getRequestURI();
    return mapping. get(url);
  }
}

/* Handler is the controller, also known as the processor
 * Used to handle specific types of requests, returning a ModelAndView object
 */
public interface Handler {<!-- -->

  // Controller handles client requests
  ModelAndView handle(HttpServletRequest request, HttpServletResponse response);
}

/* ModelAndView contains the Model data and the name of the View
 * Model is used to set the data returned by the controller
 */
public class ModelAndView {<!-- -->

  // Model data
  private Map<String, Object> model = new HashMap<>();

  // The name of the View
  private String viewName;

  // Get Model
  public Map<String, Object> getModel() {<!-- -->
    return model;
  }

  // set Model
  public void setModel(Map<String, Object> model) {<!-- -->
    this.model = model;
  }

  // Get the name of the View
  public String getViewName() {<!-- -->
    return viewName;
  }

  // Set the name of the View
  public void setViewName(String viewName) {<!-- -->
    this. viewName = viewName;
  }
}

/* ViewResolver is the view resolver
 * Used to resolve the corresponding View object according to the name of the View
 */
public class ViewResolver {<!-- -->

  // Obtain the corresponding View object according to the name of the View
  public static View resolve(String viewName) {<!-- -->
    if ("jsp".equals(viewName)) {<!-- -->
      return new JspView();
    } else if ("json".equals(viewName)) {<!-- -->
      return new JsonView();
    } else if ("xml".equals(viewName)) {<!-- -->
      return new XmlView();
    } else {<!-- -->
      throw new RuntimeException("Unknown view name: " + viewName);
    }
  }
}

/* View is the view
 * Render Model data to produce results
 */
public interface View {<!-- -->

  // render Model data
  void render(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response);
}

/* JspView renders Model data and generates Result
 * Below is just sample code
 */
public class JspView implements View {<!-- -->

  // render Model data
  public void render(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) {<!-- -->
    String viewName = model. getViewName();
    // According to the name of the View, use RequestDispatcher.forward() to forward the data to the corresponding JSP page
    request.getRequestDispatcher("/" + viewName + ".jsp").forward(request, response);
  }
}

/* JsonView is used to convert Model data into JSON format
 * Below is just sample code
 */
public class JsonView implements View {<!-- -->

  // render Model data
  public void render(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) {<!-- -->
    // Serialize Model data into JSON format
    String json = JSON.toJSONString(model);
    response.setContentType("application/json");
    response.setCharacterEncoding("UTF-8");
    response.getWriter().write(json);
  }
}

/* XmlView is used to convert Model data into XML format
 * Below is just sample code
 */
public class XmlView implements View {<!-- -->

  // render Model data
  public void render(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) {<!-- -->
    // Serialize Model data into XML format
    String xml = XMLSerializer. serialize(model);
    response.setContentType("text/xml");
    response.setCharacterEncoding("UTF-8");
    response.getWriter().write(xml);
  }
}

3. The core components of the SpringMVC framework

The core components of the SpringMVC framework include:

  • DispatcherServlet: The front controller is used to handle all requests and responses
  • HandlerMapping: The request-to-Handler mapper is used to determine the handler corresponding to the request
  • HandlerAdapter: The adapter converts different types of requests into a form that the Handler class can handle
  • Handler: The controller is also known as the processor for processing specific types of requests, returning ModelAndView
  • ViewResolver: The view resolver is used to resolve the View corresponding to the request
  • View: View, which renders data to produce results

4. Design of RESTful API

1. Design principles of RESTful API

The design of RESTful API should follow the following principles:

  1. Adopt a resource-based URL structure
  2. Map HTTP verbs (GET, POST, PUT, DELETE, etc.) to resource operations
  3. Return standard HTTP status codes, such as 200, 404, 500, etc.
  4. Transfer data using standard formats (e.g. JSON, XML)
  5. Stateless design without sessions

2. RESTful API naming method

The naming method of RESTful API follows the following rules:

  1. Resources are plural nouns
  2. The URL does not contain verbs, assuming the resource is (animals), you can use the /animals path to represent all animal resources (similar to all car resources under the /cars path)
  3. Use the multi-level separator “/” in the URL, such as /animals/1 means the animal resource numbered 1
  4. When using HTTP verbs, you should think of GET as a query command, and POST, PUT, and DELETE as add, change, and delete operations. Use selective URL path patterns (restful path patterns) to achieve more specific refinement operations, such as /animals/dogs for querying all dog resources

3. URL specification for RESTful API

The URL design of the RESTful API needs to follow the following specifications:

  1. Use plural nouns instead of singular (e.g. use /animals instead of /animal)
  2. Use hyphens (-) instead of underscores (_) to separate words
  3. URL paths must be all lowercase
  4. Resource operations that need to be case-sensitive should use the verb that represents the operation in the path, such as /animals/DELETE is used to delete the specified animal resource

5. SpringBoot framework

SpringBoot is an open source framework based on the Spring framework, which simplifies the development of Spring applications. Spring Boot lets you create Spring applications faster and easier.

1. Basic concepts of SpringBoot

SpringBoot is a framework for quickly creating Spring applications based on the Spring framework. It emphasizes that convention is better than configuration, greatly reduces the configuration work of developers, and makes it easier for developers to create and deploy Spring applications.

2. SpringBoot configuration method

SpringBoot provides a variety of configuration methods including:

  1. Property configuration file (application.properties or application.yml): The configuration parameters of the application can be set by reading the property configuration file when the application starts.
  2. Java Config: Configure the parameters of the application through the Java Config configuration class.
  3. Command line parameters: You can set the configuration parameters of the application by passing parameters when starting the application.

3. Advantages of SpringBoot

The advantages of SpringBoot include:

  • Simplifies the development and deployment process of Spring applications. SpringBoot provides an embedded web server that simplifies the deployment process of web applications.
  • Convention over configuration. SpringBoot provides many default configurations, which can simplify the work of application configuration.
  • Avoid cumbersome configuration files. The configuration method of SpringBoot can make the configuration of the application easier to understand.
  • Automatic configuration. SpringBoot provides an automatic configuration mechanism that can automatically configure various components of the application.

6. Using Spring framework to develop RESTful API

1. Create a Spring Boot project

To create a Spring Boot project in Eclipse, you can use the Spring Tool Suite plug-in, or you can use the Spring Boot CLI command line tool to create it.

# Spring Boot CLI to create the command line mode of the project
$ spring init --dependencies=web myproject

2. Add SpringBoot related dependencies

Add Spring Boot-related dependencies to the pom.xml file of the project, as follows:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

3. Create a RESTful API

The following principles need to be followed when creating a RESTful API:

  1. Use HTTP verbs (GET, POST, PUT, DELETE) to indicate the type of operation for a resource.
  2. Use the URL of the resource to indicate the access address of the resource, such as /orders.
  3. The returned HTTP status code indicates the status information of the resource, and provides specific error information at the same time.
  4. Use the JSON format to represent the data information of the resource.

Sample code:

@RestController // Declare that the Controller class is a RESTful API controller
@RequestMapping("/orders") // Specify the access path of the API
public class OrderController {<!-- -->

    // API for obtaining order information, using the HTTP GET method
    @GetMapping("/{id}")
    public ResponseEntity<Order> findById(@PathVariable Long id) {<!-- -->
        // read order information from database
        Order order = orderRepository. findOne(id);
        // If the order does not exist, return a 404 status code
        if (order == null) {<!-- -->
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }
        // Return order information and 200 status code
        return new ResponseEntity<>(order, HttpStatus.OK);
    }

    // API to create a new order, using the HTTP POST method
    @PostMapping
    public ResponseEntity<Void> create(@RequestBody Order order, UriComponentsBuilder ucBuilder) {<!-- -->
        // save order to database
        orderRepository.save(order);
        // Create the URL address of the order
        HttpHeaders headers = new HttpHeaders();
        headers.setLocation(ucBuilder.path("/orders/{id}").buildAndExpand(order.getId()).toUri());
        // Return status code 201, indicating that the order was created successfully
        return new ResponseEntity<>(headers, HttpStatus.CREATED);
    }

    // API for modifying order information, using the HTTP PUT method
    @PutMapping("/{id}")
    public ResponseEntity<Order> update(@PathVariable Long id, @RequestBody Order order) {<!-- -->
        // read order information from database
        Order currentOrder = orderRepository. findOne(id);
        // If the order does not exist, return a 404 status code
        if (currentOrder == null) {<!-- -->
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }
        // Update order information
        currentOrder.setName(order.getName());
        currentOrder.setPrice(order.getPrice());
        orderRepository.save(currentOrder);
        // Return order information and 200 status code
        return new ResponseEntity<>(currentOrder, HttpStatus.OK);
    }

    // API for deleting order information, using the HTTP DELETE method
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> delete(@PathVariable Long id) {<!-- -->
        // read order information from database
        Order order = orderRepository. findOne(id);
        // If the order does not exist, return a 404 status code
        if (order == null) {<!-- -->
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }
        // delete order information
        orderRepository. delete(order);
        // Return 204 status code, indicating successful deletion
        return new ResponseEntity<>(HttpStatus. NO_CONTENT);
    }
}

4. Configure RESTful API

When the application starts, you can configure the relevant parameters of the RESTful API by writing a configuration file, as follows:

# Configure port number, set to 8080
server.port=8080
# Configure Context Path, indicating that the name of the web application is /myapp
server.servlet.contextPath=/myapp
# Configure database connection information
spring.datasource.url=jdbc:mysql://localhost:3306/testdb
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
# Configure JPA related parameters
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=create
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

7. Using the Spring framework to achieve RESTful API security

In order to ensure data security and user authority control when using RESTful API, it is necessary to implement the security of RESTful API. This article will introduce authentication methods based on the HTTP protocol, OAuth2, and JWT, and provide code samples and Chinese comments.

1. Authentication method based on HTTP protocol

The HTTP protocol provides two basic authentication methods: basic authentication and digest authentication

Basic Authentication

Basic authentication is an authentication method based on user name and password. When the client sends a request to the server, it carries the Authorization field in the header information in the format of “Authorization: Basic username:password”, and the username and password are transmitted in base64 encoded form.

Spring Security provides a variety of basic authentication methods, and the following dependencies need to be added to the pom.xml configuration file:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

Then configure Spring Security in the SecurityConfig file:

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {<!-- -->
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {<!-- -->
        auth. inMemoryAuthentication()
            .withUser("user").password("{noop}password").roles("USER");
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {<!-- -->
        http. authorizeRequests()
            .antMatchers("/admin/**").hasRole("ADMIN")
            .anyRequest().authenticated()
            .and()
            .httpBasic();
    }
}

Digest Authentication

Digest authentication is an authentication method that performs digest calculation on messages during the HTTP request and response process. When the client sends a request to the server, it carries the Authentication field in the header information, and the format is “Authentication: Digest {digest}”, where digest is the data obtained by digesting the message body.

Spring Security also provides the implementation of basic authentication methods. It needs to be configured in the SecurityConfig file:

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {<!-- -->
    @Override
    protected void configure(HttpSecurity http) throws Exception {<!-- -->
        http. authorizeRequests()
            .antMatchers("/admin/**").hasRole("ADMIN")
            .anyRequest().authenticated()
            .and()
            .headers().addHeaderWriter(
                    new DigestAuthHeaderWriter())
                .and()
            .httpDigest();
    }
}

2. Authentication method based on OAuth2

OAuth2 is an authentication method based on the HTTP protocol, which is often used for resource authorization and access management. OAuth2 can implement four authorization methods including authorization code, implicit authorization, password authorization and client certificate.

When using Spring Security to implement OAuth2 authentication, the following dependencies need to be added to the pom.xml configuration file:

<dependency>
    <groupId>org.springframework.security.oauth.boot</groupId>
    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
    <version>2.1.4.RELEASE</version>
</dependency>

Then configure it in the SecurityConfig file:

@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationConfig extends AuthorizationServerConfigurerAdapter {<!-- -->
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private DataSource dataSource;

    @Bean
    public TokenStore tokenStore() {<!-- -->
        return new JdbcTokenStore(dataSource);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {<!-- -->
        clients.jdbc(dataSource)
            .withClient("client").secret("{noop}secret")
            .authorizedGrantTypes("password", "refresh_token")
            .scopes("read", "write")
            .accessTokenValiditySeconds(3600)
            .refreshTokenValiditySeconds(7200);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {<!-- -->
        endpoints. authenticationManager(authenticationManager)
            .userDetailsService(userDetailsService)
            .tokenStore(tokenStore());
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {<!-- -->
        security. passwordEncoder(NoOpPasswordEncoder. getInstance())
            .allowFormAuthenticationForClients();
    }
}

3. JWT-based authentication method

JWT (Json Web Token) is an open standard (RFC 7519) for transmitting declared information on the Internet. JWT usually consists of three parts: header information, payload and signature. Both the header information and the payload are data in JSON format, which can contain a variety of information.

When using Spring Security to implement JWT authentication, the following dependencies need to be added to the pom.xml configuration file:

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

Then write the JWT tool class for generating and verifying JWT:

@Component
public class JwtUtils {<!-- -->
    private static final String SECRET_KEY = "mysecretkey";

    public String generateToken(UserDetails userDetails) {<!-- -->
        Map<String, Object> claims = new HashMap<>();
        return Jwts. builder()
            .setClaims(claims)
            .setSubject(userDetails.getUsername())
            .setIssuedAt(new Date(System.currentTimeMillis()))
            .setExpiration(new Date(System.currentTimeMillis() + 60 * 60 * 1000))
            .signWith(SignatureAlgorithm.HS512, SECRET_KEY)
            .compact();
    }

    public String getUsernameFromToken(String token) {<!-- -->
        return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody().getSubject();
    }

    public boolean validateToken(String token, UserDetails userDetails) {<!-- -->
        final String username = getUsernameFromToken(token);
        return (username.equals(userDetails.getUsername()) & amp; & amp; !isTokenExpired(token));
    }

    private boolean isTokenExpired(String token) {<!-- -->
        final Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }

    private Date getExpirationDateFromToken(String token) {<!-- -->
        return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody().getExpiration();
    }
}

Configure in the SecurityConfig file:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {<!-- -->
    @Autowired
    private JwtUtils jwtUtils;

    @Override
    protected void configure(HttpSecurity http) throws Exception {<!-- -->
        http. authorizeRequests()
            .antMatchers("/admin/**").hasRole("ADMIN")
            .anyRequest().authenticated()
            .and()
            .addFilterBefore(new JwtAuthenticationFilter(jwtUtils), UsernamePasswordAuthenticationFilter.class);
            .csrf().disable();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {<!-- -->
        auth. inMemoryAuthentication()
            .withUser("user").password("{noop}password").roles("USER")
            .and()
            .withUser("admin").password("{noop}password").roles("ADMIN");
    }
}

8. Common problems and solutions

1. How to handle HTTP requests and responses

Spring MVC annotations can be used for annotations when processing HTTP requests and responses. Sample code:

@RestController
@RequestMapping("/api")
public class ApiController {<!-- -->
    @GetMapping("/books")
    public List<Book> getBooks() {<!-- -->
        List<Book> books = bookDao. findAll();
        return books;
    }

    @PostMapping("/books")
    public ResponseEntity<Void> addBook(@RequestBody Book book) {<!-- -->
        bookDao. save(book);
        HttpHeaders headers = new HttpHeaders();
        headers.setLocation(uriComponentsBuilder.path("/api/books/{id}").buildAndExpand(book.getId()).toUri());
        // return status code 201, indicating successful creation
        return new ResponseEntity<>(headers, HttpStatus.CREATED);
    }
}

2. How to handle exceptions

In the RESTful API, when an exception occurs, the corresponding error message should be returned. It can be processed using Spring MVC annotations. Sample code:

@RestControllerAdvice
public class GlobalExceptionHandler {<!-- -->
    @ExceptionHandler(Exception. class)
    public ResponseEntity<ErrorResponse> handleException(Exception ex) {<!-- -->
        ErrorResponse errorResponse = new ErrorResponse();
        errorResponse.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        errorResponse.setMessage(ex.getMessage());
        return new ResponseEntity<>(errorResponse, HttpStatus. INTERNAL_SERVER_ERROR);
    }
}

3. How to troubleshoot RESTful API performance problems

Performance issues are a very important part in the development process of RESTful API. Performance testing can be done using various tools such as JMeter, Gatling, etc.