记录一个开发常见的风格
1.RESTful
好的,我们来详细讲解一下 RESTful(通常写作 RESTful 或 REST)架构风格。这是现代 Web 服务和 API 设计的核心范式。
什么是 RESTful?
REST(REpresentational State Transfer)是一种软件架构风格,由 Roy Fielding 在 2000 年提出。它定义了一套约束条件,用于创建可扩展、松耦合的网络服务(Web APIs)。
RESTful 是指符合 REST 原则的 API 或服务。
RESTful API 关键设计
1. 资源(Resources)
- 所有数据抽象为资源(用户、订单、产品等)
- 用 URI(统一资源标识符) 定位资源
示例:/books
→ 所有书籍/books/42
→ ID=42 的书籍
2. HTTP 方法(动词)
方法 | 作用 | 幂等性 | 示例 |
---|---|---|---|
GET | 获取资源 | 是 | GET /users |
POST | 创建新资源 | 否 | POST /users |
PUT | 更新/替换整个资源 | 是 | PUT /users/1 |
PATCH | 部分更新资源 | 否 | PATCH /users/1 |
DELETE | 删除资源 | 是 | DELETE /users/1 |
幂等性:多次执行效果相同(如重复
PUT
不改变结果)
3. 状态码(Status Codes)
用 HTTP 状态码表示结果:
200 OK
:成功201 Created
:资源创建成功400 Bad Request
:客户端错误401 Unauthorized
:未认证404 Not Found
:资源不存在500 Internal Server Error
:服务器错误
一个完整的 RESTful 示例
用户管理 API
功能 | HTTP 方法 | URI | 说明 |
---|---|---|---|
获取所有用户 | GET | /users | 返回用户列表 |
创建用户 | POST | /users | 提交新用户数据 |
获取单个用户 | GET | /users/{id} | 返回 ID 对应的用户 |
更新用户 | PUT | /users/{id} | 替换整个用户数据 |
部分更新 | PATCH | /users/{id} | 只更新指定字段(如邮箱) |
删除用户 | DELETE | /users/{id} | 删除该用户 |
常见误区
- ❌ 用动词命名 URI(如
/getUser
→ 应改用GET /users
) - ❌ 忽略 HTTP 状态码(所有响应都返回
200
) - ❌ 在 GET 请求中使用 Body 传递参数
- ❌ 返回 HTML 页面而非结构化数据(JSON/XML)
示例代码:
@Data
public class Result<T> implements Serializable {
private Integer code; //编码:1成功,0和其它数字为失败
private String msg; //错误信息
private T data; //数据
public static <T> Result<T> success() {
Result<T> result = new Result<T>();
result.code = 1;
return result;
}
public static <T> Result<T> success(T object) {
Result<T> result = new Result<T>();
result.data = object;
result.code = 1;
return result;
}
public static <T> Result<T> success(String msg,T object) {
Result<T> result = new Result<T>();
result.data = object;
result.msg = msg;
result.code = 1;
return result;
}
public static <T> Result<T> error(String msg) {
Result result = new Result();
result.msg = msg;
result.code = 0;
return result;
}
}
@PostMapping("/upload")
public Result<String> uploadFile(@RequestParam("file") MultipartFile file) {
boolean saved = markdownDocsService.saveFile(file);
if (saved)
return Result.success("上传成功");
return Result.error("上传失败");
}
2.全局异常处理
1.我们定义一个异常的枚举类用来返回异常信息
@Getter
@AllArgsConstructor
@ToString
public enum ResponseEnum {
SUCCESS(1, "成功"),
ERROR(-1, "服务器内部错误"),
//-1xx 服务器错误
BAD_SQL_GRAMMAR_ERROR(-101, "sql语法错误"),
SERVLET_ERROR(-102, "servlet请求异常"), //-2xx 参数校验
DELETE_FILE_FAILED(103, "删除文件错误"),
UPLOAD_ERROR(-103, "文件上传错误"),
EXPORT_DATA_ERROR(104, "数据导出失败"),
DATA_NOt_EXISITS(105, "数据不存在"),
PARAM_IS_NULL_ERROR(-105, "参数不能为空"),
IMPORT_DATA_ERROR(106, "数据导入失败"),
FILE_IS_EMPTY(107, "上传文件内容为空"),
DISALLOW_DELETE_DATA(108, "该数据有关联数据不能删除"),
TOO_MANY_REQUESTS(109, "请求太频繁了"),
//-2xx 参数校验
USRE_ID_NULL_ERROR(-201, "用户Id不能为空"),
MOBILE_NULL_ERROR(-202, "手机号码不能为空"),
MOBILE_ERROR(-203, "手机号码不正确"),
PASSWORD_NULL_ERROR(204, "密码不能为空"),
CODE_NULL_ERROR(205, "验证码不能为空"),
CODE_ERROR(206, "验证码错误"),
MOBILE_EXIST_ERROR(207, "手机号已被注册"),
LOGIN_MOBILE_ERROR(208, "用户不存在"),
LOGIN_PASSWORD_ERROR(209, "密码错误"),
LOGIN_LOKED_ERROR(210, "用户被锁定"),
LOGIN_AUTH_ERROR(-211, "未登录"),
REGISTER_FAIL(212, "注册失败"),
USER_REPEAT(-212, "重复注册"),
USER_PWD_ERROR(213, "用户密码错误"),
ACCOUNT_UNREGISTER(-213, "用户未注册"),
ACCOUNT_UNLOGIN(214, "用户未登录"),
USER_NOT_EXIST(215, "用户不存在"),
USER_ISNOT_ROLE(215, "用户角色不匹配"),
USERNAME_IS_EXIST(216, "用户名已存在"),
ORG_NOT_EXIST(217, "机构信息不存在"),
DELETE_SUCCESS(218, "删除成功"),
DELETE_FAIL(219, "删除失败"),
UPDATE_SUCCESS(220, "更新成功"),
UPDATE_FAIL(221, "更新失败"),
UPDATE_ERROR(222, "更新操作异常"),
USER_BIND_IDCARD_EXIST_ERROR(-301, "身份证号码已绑定"),
USER_NO_BIND_ERROR(302, "用户未绑定"),
LEND_INVEST_ERROR(305, "当前状态无法投标"),
LEND_FULL_SCALE_ERROR(306, "已满标,无法投标"),
NOT_SUFFICIENT_FUNDS_ERROR(307, "余额不足,请充值"),
PAY_UNIFIEDORDER_ERROR(401, "统一下单错误"),
ALIYUN_SMS_LIMIT_CONTROL_ERROR(-502, "短信发送过于频繁"),//业务限流
ALIYUN_SMS_ERROR(-503, "短信发送失败"),//其他失败
WEIXIN_CALLBACK_PARAM_ERROR(-601, "回调参数不正确"),
WEIXIN_FETCH_ACCESSTOKEN_ERROR(-602, "获取access_token失败"),
WEIXIN_FETCH_USERINFO_ERROR(-603, "获取用户信息失败"),
USER_TYPE_ERROR(-604, "用户信息异常"),;
private final Integer code;//状态码
private final String message;//消息
ResponseEnum(int code, String message) {
this.code = code;
this.message = message;
}
}
2.自定义一个异常类
/**
* 业务异常
*/
@Getter
public class BusinessException extends RuntimeException {
//状态码
private Integer code;
public BusinessException(String message, Integer code) {
super(message);
this.code = code;
}
public BusinessException(ResponseEnum responseEnum) {
super(responseEnum.getMessage());
this.code = responseEnum.getCode();
}
public BusinessException(ResponseEnum responseEnum, Throwable cause) {
super(responseEnum.getMessage(),cause);
this.code = responseEnum.getCode();
}
}
3.全局异常处理
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 未定义异常处理
*/
@ExceptionHandler(Exception.class)
public Result<Object> exception(Exception e) {
log.error(e.getMessage(), e);
return Result.error("系统异常");
}
/**
* 参数绑定异常处理
*/
@ExceptionHandler(BindException.class)
public Result<Object> handleBindException(BindException e) {
return Result.error(e.getMessage());
}
@ExceptionHandler(ExecutionException.class)
public Result<Object> handleExecutionException(ExecutionException e) {
log.error(e.getMessage(), e);
Result<Object> result = new Result<>();
result.setMsg(REGISTER_FAIL.getMessage());
result.setCode(REGISTER_FAIL.getCode()); // 设置特定错误码
return result;
}
/**
* 自定义业务异常处理
*/
@ExceptionHandler(BusinessException.class)
public Result<Object> handleBusinessException(BusinessException e) {
log.error(e.getMessage(), e.getCause());
Result<Object> result = Result.error(e.getMessage());
result.setCode(e.getCode()); // 设置自定义错误码
return result;
}
@ExceptionHandler(NotLoginException.class)
public Result <Object> handleNotLoginException(NotLoginException e) {
log.error(e.getMessage(), e);
Result<Object> result = Result.error(ResponseEnum.ACCOUNT_UNLOGIN.getMessage());
result.setCode(ResponseEnum.ACCOUNT_UNLOGIN.getCode()); // 设置特定错误码
return result;
}
@ExceptionHandler(SQLException.class)
public Result<Object> handleSQLException(SQLException e) {
log.error(e.getMessage(), e);
Result<Object> result = Result.error(ResponseEnum.IMPORT_DATA_ERROR.getMessage());
result.setCode(ResponseEnum.IMPORT_DATA_ERROR.getCode()); // 设置特定错误码
return result;
}
/**
* Servlet请求相关异常处理
*/
@ExceptionHandler({
NoHandlerFoundException.class,
HttpRequestMethodNotSupportedException.class,
HttpMediaTypeNotSupportedException.class,
MissingPathVariableException.class,
MissingServletRequestParameterException.class,
HttpMessageNotReadableException.class,
HttpMessageNotWritableException.class,
MethodArgumentNotValidException.class,
HttpMediaTypeNotAcceptableException.class,
ServletRequestBindingException.class,
ConversionNotSupportedException.class,
MissingServletRequestPartException.class,
AsyncRequestTimeoutException.class
})
public Result<Object> handleServletException(Exception e) {
log.error(e.getMessage(), e);
Result<Object> result = Result.error(ResponseEnum.SERVLET_ERROR.getMessage());
result.setCode(ResponseEnum.SERVLET_ERROR.getCode()); // 设置特定错误码
return result;
}
}
4.在处理业务是我们就可以利用抛出异常的方法处理错误情况
public MarkdownDocsServiceImpl() {
try {
// 使用 ClassPathResource 确保路径正确指向 resources/document 目录
ClassPathResource resource = new ClassPathResource(MD_SAVE_PATH);
this.rootLocation = resource.getFile().toPath();
} catch (Exception e) {
throw new RuntimeException("初始化文件存储路径失败: " + MD_SAVE_PATH, e);
}
}
3.讲到这里了,再给大家讲一个 ClassPathResour
这个我也是今天刚用,很神奇,我的业务需求是我想存储前端的md文件到我的项目中下次启动的时候可以进行处理,当然还要立刻处理一下,存储是为了持久化,数据库我也想过,暂时还没有实现,我先尝试了这一种
ClassPathResource resource = new ClassPathResource(MD_SAVE_PATH);
String MD_SAVE_PATH ="classpath:document"
他会把文件存储到target的classes里面,很神奇我还在研究它的原理,