RESTFUL API開發常用接口以及java開發常用操作

目錄

io.spring.platform和org.springframework.cloud

 

commons

java -jar 文件名

REST成熟等級

controller層中常用註解

@RequestParam

@PageableDefault

@PathVariable

@JsonView

@RequestBody

前後端分離中Date的使用

校驗註解

@NotBlank和@Valid和BindingResult

大部分校驗註解

自定義校驗註解

後端錯誤請求拋出

瀏覽器錯誤拋出

自定義後端錯誤返回

RESTful  API的攔截

過濾器(Filter)

過濾器實現流程

添加第三方過濾器

攔截器(Interceptor)

攔截器實現流程

切片(Aspect)

文件的上傳和下載

文件上傳

文件下載

異步處理REST服務

使用Runnable異步處理Rest服務

使用DeferredResult異步處理Rest服務

異步處理配置

swagger自動生成文檔

 WireMock快速僞造RESTful服務

引用properties中的屬性

properties中自動注入properties文件中的屬性

類屬性注入properties文件中的屬性

單個屬性注入properties文件屬性

利用ResourceBundle類獲取properties中屬性

properties亂碼


io.spring.platform和org.springframework.cloud

主要是用來對項目中引入jar的管理,這樣後續引入的jar就不需要引入版本了 

<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>io.spring.platform</groupId>
				<artifactId>platform-bom</artifactId>
				<version>Brussels-SR4</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>Dalston.SR2</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

 

但是很多項目會自動引入,所以上面那種方式基本上已經被淘汰了

<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.2.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

<module>

說明當前的maven項目是以下項目的父模塊

<modules>
		<module>../security-app</module>
		<module>../security-browser</module>
		<module>../security-core</module>
		<module>../security-demo</module>
	</modules>

commons

常用的工具包

                <dependency>
			<groupId>commons-lang</groupId>
			<artifactId>commons-lang</artifactId>
		</dependency>
		<dependency>
			<groupId>commons-collections</groupId>
			<artifactId>commons-collections</artifactId>
		</dependency>
		<dependency>
			<groupId>commons-beanutils</groupId>
			<artifactId>commons-beanutils</artifactId>
		</dependency>

java -jar 文件名

直接命令行啓動jar

java -jar 文件名

REST成熟等級

controller層中常用註解

@RequestParam

映射請求參數棟java方法的參數

@PageableDefault

指定分頁參數默認值

@PathVariable

映射url片段到java方法的參數

//正則表達式,只能接收數字   {id:\\d+}
@RequestMapping(value = "user/{id:\\d+}")
	public String test(@PathVariable(name="id") String id){
		System.out.println(id);
		return "ok";
	}

@JsonView

控制json輸出內容

public class User {
	
	public interface UserSimpleView {};
	public interface UserDetailView extends UserSimpleView {};
	
	private String id;
	
	private String username;
	
	private String password;
	
	private Date birthday;

	@JsonView(UserSimpleView.class)
	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	@JsonView(UserDetailView.class)
	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	@JsonView(UserSimpleView.class)
	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}
	
	@JsonView(UserSimpleView.class)
	public Date getBirthday() {
		return birthday;
	}

	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}
	
}

當方法上使用了 @JsonView之後,只返回user方法中註解了當前註解的屬性

	@GetMapping("/{id:\\d+}")
	@JsonView(User.UserDetailView.class)
	public User getInfo(@ApiParam("用戶id") @PathVariable String id) {
//		throw new RuntimeException("user not exist");
		System.out.println("進入getInfo服務");
		User user = new User();
		user.setUsername("tom");
		return user;
	}

@RequestBody

映射請求體到java 方法的參數,大部分用於處理post請求過來的請求體

        @PostMapping
	public User create(@RequestBody User user) {

		System.out.println(user.getId());
		System.out.println(user.getUsername());
		System.out.println(user.getPassword());
		System.out.println(user.getBirthday());

		user.setId("1");
		return user;
	}

前後端分離中Date的使用

在前後端分離的系統中,前端傳給後端的時間爲時間戳,後端傳回給前端的也是時間戳,因爲在前後端的思想中,後端對應着多個前端,對於日期的轉換成如果由後臺處理,其處理邏輯將非常麻煩。

        //傳到後臺的是時間戳,返回回去的時候不需要做任何修改,又直接返回回去
        @PostMapping
	public User create(@RequestBody User user) {

		System.out.println(user.getBirthday());

		user.setId("1");
		return user;
	}

校驗註解

@NotBlank和@Valid和BindingResult

當數據傳入的時候,判斷是否爲空,兩個註解要一起使用,否則不會生效。BindingResult類可以使得即使觸發了@Valid也會進入到代碼中執行(通過BindingResult獲取錯誤信息),而不是直接給前端返回錯誤碼。

        @NotBlank(message = "密碼不能爲空")
	private String password;
       public User update(@Valid @RequestBody User user, BindingResult errors) {

		System.out.println(user.getId());
		System.out.println(user.getUsername());
		System.out.println(user.getPassword());
		System.out.println(user.getBirthday());

		user.setId("1");
		return user;
	}

大部分校驗註解

Constraint 詳細信息
@Valid 被註釋的元素是一個對象,需要檢查此對象的所有字段值
@Null 被註釋的元素必須爲 null
@NotNull 被註釋的元素必須不爲 null
@AssertTrue 被註釋的元素必須爲 true
@AssertFalse  被註釋的元素必須爲 false
@Min(value) 被註釋的元素必須是一個數字,其值必須大於等於指定的最小值
@Max(value) 被註釋的元素必須是一個數字,其值必須小於等於指定的最大值
@DecimalMin(value) 被註釋的元素必須是一個數字,其值必須大於等於指定的最小值
@DecimalMax(value) 被註釋的元素必須是一個數字,其值必須小於等於指定的最大值
@Size(max, min)    被註釋的元素的大小必須在指定的範圍內
@Digits (integer, fraction) 被註釋的元素必須是一個數字,其值必須在可接受的範圍內
@Past 被註釋的元素必須是一個過去的日期
@Future     被註釋的元素必須是一個將來的日期
@Pattern(value)  被註釋的元素必須符合指定的正則表達式
@Email 被註釋的元素必須是電子郵箱地址
@Length 被註釋的字符串的大小必須在指定的範圍內
@NotEmpty 被註釋的字符串的必須非空
@Range  被註釋的元素必須在合適的範圍內
@NotBlank  被註釋的字符串的必須非空
@URL(protocol=,host=, port=,regexp=, flags=) 被註釋的字符串必須是一個有效的url
@CreditCardNumber      被註釋的字符串必須通過Luhn校驗算法,銀行卡,信用卡等號碼一般都用Luhn計算合法性

自定義校驗註解

創建註解,該註解的處理邏輯主要在 MyConstraintValidator類中,裏面的三個元素必須存在,message是出現錯誤之後返回給前端的信息

@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = MyConstraintValidator.class)
public @interface MyConstraint {
	
	String message();

	Class<?>[] groups() default { };

	Class<? extends Payload>[] payload() default { };

}

 MyConstraintValidator,校驗邏輯主要在isvalid方法中

public class MyConstraintValidator implements ConstraintValidator<MyConstraint, Object> {

	@Autowired
	private HelloService helloService;
	
	@Override
	public void initialize(MyConstraint constraintAnnotation) {
		System.out.println("my validator init");
	}

	@Override
	public boolean isValid(Object value, ConstraintValidatorContext context) {
		helloService.greeting("tom");
		System.out.println(value);
		return true;
	}

}

後端錯誤請求拋出

瀏覽器錯誤拋出

直接在resources文件夾下創建文件以及頁面即可

自定義後端錯誤返回

創建異常類

public class UserNotExistException extends RuntimeException {

	/**
	 * 
	 */
	private static final long serialVersionUID = -6112780192479692859L;
	
	private String id;
	
	public UserNotExistException(String id) {
		super("user not exist");
		this.id = id;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

}

創建ControllerExceptionHandler類,當controller層返回異常時,自定義返回內容

@ControllerAdvice
public class ControllerExceptionHandler {

	@ExceptionHandler(UserNotExistException.class)
	@ResponseBody
	@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
	public Map<String, Object> handleUserNotExistException(UserNotExistException ex) {
		Map<String, Object> result = new HashMap<>();
		result.put("id", ex.getId());
		result.put("message", ex.getMessage());
		return result;
	}

}

RESTful  API的攔截

過濾器(Filter)

@Component
public class TimeFilter implements Filter {

	/* (non-Javadoc)
	 * @see javax.servlet.Filter#destroy()
	 */
	@Override
	public void destroy() {
		System.out.println("time filter destroy");
	}

	/* (non-Javadoc)
	 * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
	 */
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		System.out.println("time filter start");
		long start = new Date().getTime();
		chain.doFilter(request, response);
		System.out.println("time filter 耗時:"+ (new Date().getTime() - start));
		System.out.println("time filter finish");
	}

	/* (non-Javadoc)
	 * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
	 */
	@Override
	public void init(FilterConfig arg0) throws ServletException {
		System.out.println("time filter init");
	}

}

過濾器實現流程

 

添加第三方過濾器

因爲第三方過濾器中沒有加component註解,所以需要我們寫一個配置類來添加

@Configuration
public class WebConfig{
	
	@Bean
	public FilterRegistrationBean timeFilter() {
		
		FilterRegistrationBean registrationBean = new FilterRegistrationBean();
		
		TimeFilter timeFilter = new TimeFilter();
		registrationBean.setFilter(timeFilter);
		
		List<String> urls = new ArrayList<>();
		urls.add("/*");     //攔截所有的請求
		registrationBean.setUrlPatterns(urls);
		
		return registrationBean;
		
	}

}

過濾器攔截還是存在很多問題的,只能知道攔截下來的http請求和響應的內容,不知道具體是哪個方法去處理的。這個時候需要後面的攔截器來具體處理。

攔截器(Interceptor)

@Component
public class TimeInterceptor implements HandlerInterceptor {

	/* (non-Javadoc)
	 * @see org.springframework.web.servlet.HandlerInterceptor#preHandle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object)
	 */
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		System.out.println("preHandle");
		
		System.out.println(((HandlerMethod)handler).getBean().getClass().getName());
		System.out.println(((HandlerMethod)handler).getMethod().getName());
		
		request.setAttribute("startTime", new Date().getTime());
		return true;
	}

	/* (non-Javadoc)
	 * @see org.springframework.web.servlet.HandlerInterceptor#postHandle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object, org.springframework.web.servlet.ModelAndView)
	 */
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		System.out.println("postHandle");
		Long start = (Long) request.getAttribute("startTime");
		System.out.println("time interceptor 耗時:"+ (new Date().getTime() - start));

	}

	/* (non-Javadoc)
	 * @see org.springframework.web.servlet.HandlerInterceptor#afterCompletion(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object, java.lang.Exception)
	 */
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		System.out.println("afterCompletion");
		Long start = (Long) request.getAttribute("startTime");
		System.out.println("time interceptor 耗時:"+ (new Date().getTime() - start));
		System.out.println("ex is "+ex);

	}

}
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
	@SuppressWarnings("unused")
	@Autowired
	private TimeInterceptor timeInterceptor;
	
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(timeInterceptor);
	}
	

}

攔截器實現流程

切片(Aspect)

切片也就是對spring aop的運用,我們由於攔截器不能指定requestmapping方法,不是非常靈活,而aop的話可以指定方法,並且對方法進行增強。

@Aspect
@Component
public class TimeAspect {
	
	@Around("execution(* com.imooc.web.controller.UserController.*(..))")
	public Object handleControllerMethod(ProceedingJoinPoint pjp) throws Throwable {
		
		System.out.println("time aspect start");
		
		Object[] args = pjp.getArgs();
		for (Object arg : args) {
			System.out.println("arg is "+arg);
		}
		//args保持的是存入的參數
		long start = new Date().getTime();
		
		Object object = pjp.proceed();
		//object是調用方法返回的對象
		System.out.println("time aspect 耗時:"+ (new Date().getTime() - start));
		
		System.out.println("time aspect end");
		
		return object;
	}

}

 總結:過濾器無法拿到處理的方法的信息,攔截器無法拿到處理的方法傳入的數據,切片可以完成前面兩個的不足。

restful攔截 API的執行流程


   說明:

文件的上傳和下載

文件上傳

@RestController
@RequestMapping("/file")
public class FileController {

	private String folder = "/Users/java/workspace/src/main/java/com/web/controller";

	@PostMapping
	public FileInfo upload(MultipartFile file) throws Exception {

		System.out.println(file.getName());
		System.out.println(file.getOriginalFilename());
		System.out.println(file.getSize());

		File localFile = new File(folder, new Date().getTime() + ".txt");

		file.transferTo(localFile);

		return new FileInfo(localFile.getAbsolutePath());
	}

}
public class FileInfo {
	
	public FileInfo(String path){
		this.path = path;
	}
	
	private String path;

	public String getPath() {
		return path;
	}

	public void setPath(String path) {
		this.path = path;
	}
	
}

文件下載

@RestController
@RequestMapping("/file")
public class FileController {

	private String folder = "/Users/web";


	@GetMapping("/{id}")
	public void download(@PathVariable String id, HttpServletRequest request, HttpServletResponse response) throws Exception {

		try (InputStream inputStream = new FileInputStream(new File(folder, id + ".txt"));
				OutputStream outputStream = response.getOutputStream();) {
			
			response.setContentType("application/x-download");
			response.addHeader("Content-Disposition", "attachment;filename=test.txt");
			
			IOUtils.copy(inputStream, outputStream);
			outputStream.flush();
		} 

	}

}

異步處理REST服務

使用Runnable異步處理Rest服務

@RestController
public class AsyncController {

	
	
	private Logger logger = LoggerFactory.getLogger(getClass());
	
	@RequestMapping("/order")
	public Callable<String> order() throws Exception {
		logger.info("主線程開始");
		Callable<String> result = new Callable<String>() {
			@Override
			public String call() throws Exception {
				logger.info("副線程開始");
				Thread.sleep(1000);
				logger.info("副線程返回");
				return "success";
			}
		};
              return result;
	}
}
}

使用DeferredResult異步處理Rest服務

類似於對於每一個應用創建一個線程,線程1發送處理請求,線程2監聽接收請求,消息隊列簡單來說就是MQ,應用2線程就是具體處理請求的應用。

異步處理配置

swagger自動生成文檔

這個是後端寫好代碼之後,給前端的接口文檔

添加相關依賴

		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger2</artifactId>
			<version>2.7.0</version>
		</dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger-ui</artifactId>
			<version>2.7.0</version>
		</dependency>

添加註解

輸入(注意大小寫)

http://localhost:8081/swagger-ui.html

在方法上面添加ApiOperation註解,就會在swagger文檔中對調用方法描述

	@PostMapping
	@ApiOperation(value = "創建用戶")
	public User create(@Valid @RequestBody User user) {

		System.out.println(user.getId());
		System.out.println(user.getUsername());
		System.out.println(user.getPassword());
		System.out.println(user.getBirthday());

		user.setId("1");
		return user;
	}

也可以在屬性上添加ApiModelProperty註解,就會在swagger文檔中對屬性的描述

@ApiModelProperty(value = "用戶名")
	private String username;

對於傳入參數的說明使用ApiParam註解

 

	@GetMapping("/{id:\\d+}")
	@JsonView(User.UserDetailView.class)
	public User getInfo(@ApiParam("用戶id") @PathVariable String id) {
//		throw new RuntimeException("user not exist");
		System.out.println("進入getInfo服務");
		User user = new User();
		user.setUsername("tom");
		return user;
	}

 WireMock快速僞造RESTful服務

WireMock就是一個獨立的服務器,前端可以先去WireMock中請求,當後端開發完畢之後,前端修改端口即可

進入官網:http://wiremock.org/

下載

執行命令:

$ java -jar wiremock-standalone-2.25.1.jar

在項目中添加依賴

                <dependency>
			<groupId>com.github.tomakehurst</groupId>
			<artifactId>wiremock</artifactId>
		</dependency>
/**
 * 
 */
package com.imooc.wiremock;

import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.configureFor;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.removeAllMappings;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;

import java.io.IOException;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.core.io.ClassPathResource;


public class MockServer {

	/**
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
		configureFor(8062);
		removeAllMappings();

		mock("/order/1", "01");
		mock("/order/2", "02");
	}

	private static void mock(String url, String file) throws IOException {
		ClassPathResource resource = new ClassPathResource("mock/response/" + file + ".txt");
		String content = StringUtils.join(FileUtils.readLines(resource.getFile(), "UTF-8").toArray(), "\n");
		stubFor(get(urlPathEqualTo(url)).willReturn(aResponse().withBody(content).withStatus(200)));
	}

}

 01.txt

{
	"id":1,
	"type":"C"
}

02.txt

{
	"id":2,
	"type":"B"
}

引用properties中的屬性

properties中自動注入properties文件中的屬性

在resourc文件下創建config.properties

# dcm文件存儲路徑
project.dcm_image=/dcm_image
# 標註等其餘文件
project.other_file=${project.dcm_image}/other_file

類屬性注入properties文件中的屬性

創建config.java類,@Component將Config對象注入到Spring容器中,@PropertySource("classpath:config.properties")表示將config.properties注入容器,@ConfigurationProperties(prefix = "project")將config.properties前綴爲project和類的變量一一對應
 

@Component
@PropertySource("classpath:config.properties")
@ConfigurationProperties(prefix = "project")
@Data
public class Config {
    private String dcm_image;
    private String other_file;
}

單個屬性注入properties文件屬性

在properties文件注入後,如果引用單個文件,可以用@{}

@Value("${project.dcm_image}")

利用ResourceBundle類獲取properties中屬性

ResourceBundle bundle = ResourceBundle.getBundle("config");
#config.properties 省略後綴名
String filePath = bundle.getString("project.dcm_image");

properties亂碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章