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 responsesHandlerMapping
: The request-to-Handler mapper is used to determine the handler corresponding to the requestHandlerAdapter
: The adapter converts different types of requests into a form that the Handler class can handleHandler
: The controller is also known as the processor for processing specific types of requests, returning ModelAndViewViewResolver
: The view resolver is used to resolve the View corresponding to the requestView
: 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:
- Adopt a resource-based URL structure
- Map HTTP verbs (GET, POST, PUT, DELETE, etc.) to resource operations
- Return standard HTTP status codes, such as 200, 404, 500, etc.
- Transfer data using standard formats (e.g. JSON, XML)
- Stateless design without sessions
2. RESTful API naming method
The naming method of RESTful API follows the following rules:
- Resources are plural nouns
- 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)
- Use the multi-level separator “/” in the URL, such as /animals/1 means the animal resource numbered 1
- 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:
- Use plural nouns instead of singular (e.g. use /animals instead of /animal)
- Use hyphens (-) instead of underscores (_) to separate words
- URL paths must be all lowercase
- 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:
- 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.
- Java Config: Configure the parameters of the application through the Java Config configuration class.
- 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:
- Use HTTP verbs (GET, POST, PUT, DELETE) to indicate the type of operation for a resource.
- Use the URL of the resource to indicate the access address of the resource, such as /orders.
- The returned HTTP status code indicates the status information of the resource, and provides specific error information at the same time.
- 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.