前言
刚开始接触SpringBoot,查阅了一些关于接口的文章,如果有写的不对的地方,欢迎批评指正~
注解
1.@RestController
@RestController出现的频率很高,那它到底是什么意思呢?
Spring中 @RestController 的作用等同于@Controller + @ResponseBody 所以要理解@RestController注解还得了解@Controller 和 @ResponseBody的注解
@Controller
@Controller是一种特殊化的@Component类,在实际操作中@Controller用来表示Spring某个类是否可以接受HTTP请求,它通常与@ResponseBody绑定使用。
@Component
1.把普通POJO(Plain Ordinary Java Object简单的java对象)实例化到spring容器中,相当于配置文件中的<bean id="" class=""/>
2.泛指组件,当组件不好归类的时候,可以使用@Component注解进行标注
@ResponseBody
在实际操作中我们只需要在Controller层使用@RequestBody注解就可以将对象进行反序列化;而若需要对Controller的方法进行序列化,我们需要在返回值上使用@ResponseBody;也可以将@ResponseBody注解在Controller类上,这样可以将这个类中所有的方法序列化。
总结:
1.@RestController为开发提供了方便,在提供json接口时需要的配置操作再也不需要自己配置了
2.@RestController注解相当于@ResponseBody和@Controller的结合
3.@RestController注解时,返回的是内容实例
2.@RequestMapping
@RequestMapping是一个用来处理请求地址映射的注解,可用于类或者方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径
@GetMapping是一个组合注解,是@RequestMapping(method = RequestMethod.GET)的缩写。
@PostMapping是一个组合注解,是@RequestMapping(method = RequestMethod.POST)的缩写。
请求相关
这里尝试写一个请求
@RestController
public class UserController {
@RequestMapping("/getUserInfo")
public Map<String, Object> getUserInfo() {
Map<String, Object> map = new HashMap<>();
map.put("account", "123");
map.put("name", "张三");
return map;
}
}
请求.png
备注:
1.ip地址是本机IPv4 地址
2.RequestMapping 这里不管是post请求还是get 请求都是一样的返回值
post请求
定义一个Person实体类
public class Person {
private long id;
private String name;
private int age;
private String hobby;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
@Override
public String toString() {
return "name:" + name + ";age=" + age + ";hobby:" + hobby;
}
}
在之前的UserController类里面添加如下方法:
@PostMapping(path = "/setUserInfo")
public String setUserInfo(Person person) {
System.out.println(person.toString());
return person.toString();
}
效果如下:
备注:
但是发现 postMan 采用 raw 方式
data如下:
{
"name":"lxc",
"age":13,
"hobby":"test"
}
返回的结果:name:null;age=0;hobby:null
如何接受json数据
新写一个接口如下:
@PostMapping(path = "/setUserInfo2")
public String setUserInfo2(@RequestBody Person person) {
return person.toString();
}
postJson.png
备注:需要修改请求头的content-Type 为application/json
上传文件
这里直接贴代码
package com.example.demo;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
@RestController
public class FileUploadController {
@PostMapping("/singleFile")
@ResponseBody
public String singleFile(@RequestParam("file") MultipartFile file) {
//判断非空
if (file.isEmpty()) {
return "上传的文件不能为空";
}
try {
String path = "D:\\Program Files\\upload\\";
// Log.info(this.getClass().getName() + "图片路径:" + path);
File f = new File(path);
//如果不存在该路径就创建
if (!f.exists()) {
f.mkdir();
}
File dir = new File(path + file.getOriginalFilename());
// 文件写入
file.transferTo(dir);
return "上传单个文件成功";
} catch (Exception e) {
e.printStackTrace();
return "上传单个文件失败";
}
}
//文件下载相关代码
@RequestMapping("/download")
public String downloadFile(HttpServletRequest request, HttpServletResponse response) {
String fileName = "test.txt";// 设置文件名,根据业务需要替换成要下载的文件名
if (fileName != null) {
//设置文件路径
String realPath = "D://Program Files//upload";
File file = new File(realPath, fileName);
if (file.exists()) {
response.setContentType("application/force-download");// 设置强制下载不打开
response.addHeader("Content-Disposition", "attachment;fileName=" + fileName);// 设置文件名
byte[] buffer = new byte[1024];
FileInputStream fis = null;
BufferedInputStream bis = null;
try {
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
OutputStream os = response.getOutputStream();
int i = bis.read(buffer);
while (i != -1) {
os.write(buffer, 0, i);
i = bis.read(buffer);
}
System.out.println("success");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
return null;
}
}
上传结果.png
下载结果.png
这里只是简单的举例子,当然还有多文件上传等,这个可以之后去慢慢摸索...emm..
参数校验
统一的返回结果
有时候需要统一处理返回结果,这里简单写个例子
package com.example.demo.result;
import java.io.Serializable;
/**
* 统一的返回结果集
*/
public class JsonResult implements Serializable {
// 成功
public static final int SUCCESS = 0;
// 失败
public static final int FAIL = 1;
private int status;
private int code;
private String msg;
private Object data;
public JsonResult(int status, int code, String msg, Object data) {
this.status = status;
this.code = code;
this.msg = msg;
this.data = data;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static JsonResult success(Object data) {
return new JsonResult(SUCCESS, 200, "ok", data);
}
public static JsonResult success(String msg) {
return new JsonResult(SUCCESS, 200, msg, null);
}
public static JsonResult fail(String msg) {
return fail(400, msg);
}
public static JsonResult fail(int code, String msg) {
return new JsonResult(FAIL, code, msg, null);
}
}
创建测试用户类
package com.example.demo.user;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
public class User implements Serializable {
@Length(min = 5, max = 10, message = "用户名长度不合法")
@NotNull(message = "用户名不能为空")
private String username;
@Length(min = 6, max = 16, message = "密码长度不合法")
@NotNull(message = "密码不能为空")
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
UserController新增测试接口
@PostMapping("/login")
public JsonResult login(@Validated @RequestBody User user) {
return JsonResult.success("登陆成功");
}
//测试get请求
@GetMapping("/getLogin")
public JsonResult getLogin(@Validated User user, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return JsonResult.fail(bindingResult.getFieldError().getDefaultMessage());
}
return JsonResult.success("登陆成功");
}
结果1.png
结果2.png
统一的异常处理
package com.example.demo.exception;
import com.example.demo.result.JsonResult;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.validation.ConstraintViolationException;
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* post请求参数校验抛出的异常
*
* @param e
* @return
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public JsonResult methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
//获取异常中随机一个异常信息
System.out.println("methodArgumentNotValidExceptionHandler");
String defaultMessage = e.getBindingResult().getFieldError().getDefaultMessage();
return JsonResult.fail(defaultMessage);
}
/**
* get请求参数校验抛出的异常
*
* @param e
* @return
*/
@ExceptionHandler(BindException.class)
public JsonResult bindExceptionHandler(BindException e) {
System.out.println("bindExceptionHandler");
//获取异常中随机一个异常信息
String defaultMessage = e.getBindingResult().getFieldError().getDefaultMessage();
return JsonResult.fail(defaultMessage);
}
/**
* 请求方法中校验抛出的异常
*
* @param e
* @return
*/
@ExceptionHandler(ConstraintViolationException.class)
public JsonResult constraintViolationExceptionHandler(ConstraintViolationException e) {
//获取异常中第一个错误信息
System.out.println("constraintViolationExceptionHandler");
String message = e.getConstraintViolations().iterator().next().getMessage();
return JsonResult.fail(message);
}
}
常用校验注解
@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 被注释的元素必须在合适的范围内
自定义校验
有时候需求复杂,需要自己去写一个
package com.example.demo.verify;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
//用于校验手机号的逻辑类
@Constraint(validatedBy = PhoneValidator.class)
public @interface Phone {
//手机号的校验格式
String regexp() default "^[1][3-9][0-9]{9}$";
//出现错误返回的信息
String message() default "手机号格式错误";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
public @interface List {
Phone[] value();
}
}
package com.example.demo.verify;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
//校验注解的类必须实现ConstraintValidator,第一个泛型是注解,第二个泛型是校验参数的类型(手机号是String类型)
public class PhoneValidator implements ConstraintValidator<Phone, String> {
private String regexp;
//初始化方法
@Override
public void initialize(Phone constraintAnnotation) {
//获取校验的手机号的格式
this.regexp = constraintAnnotation.regexp();
}
//value是@Phone注解所注解的字段值
//校验,返回true则通过校验,返回false则校验失败,错误信息为注解中的message
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
return value.matches(regexp);
}
}
package com.example.demo;
import com.example.demo.result.JsonResult;
import com.example.demo.verify.Phone;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.constraints.NotNull;
@RestController
@Validated
public class PhoneController {
@GetMapping("/sendPhone")
public JsonResultsendPhone(@Phone @NotNull(message = "手机号不能为空") String phone) {
return JsonResult.success("正确的手机号");
}
}
自定义校验测试结果.png
分组校验
在上面的测试用户类新增两个接口
public interface CheckName {
}
public interface CheckPassword {
}
修改属性的groups
@Length(min = 5, max = 10, message = "用户名长度不合法", groups = CheckName.class)
@NotNull(message = "用户名不能为空")
private String username;
@Length(min = 6, max = 16, message = "密码长度不合法", groups = CheckPassword.class)
@NotNull(message = "密码不能为空")
private String password;
修改UserController类下的login进行测试
@PostMapping("/login")
public JsonResult login(@Validated(value = User.CheckName.class) @RequestBody User user) {
return JsonResult.success("登陆成功");
}
分组校验.png
我们会发现密码的校验没有进行
网友评论