博客RESTful API 接口开发

news2024/11/28 4:38:43

目录

1.博客系统规划

2.基础服务搭建 

3.登录接口

4.新增文章接口 

5.查询文章接口 

6.修改文章接口 

7.删除文章接口 

总结 


 

1.博客系统规划

首先规划一下有哪些接口,从博客文章角度来看,需要如下接口:

  • 新增文章接口,传递参数有文章名称、作者名、分类、标签、内容、发布时间、修改时间字段。
  • 修改文章接口,可以对上述的任意字段进行修改,当然逻辑上发布时间不能被修改。
  • 查询文章列表接口,提供分页和不分页两种数据返回。
  • 查询文章详情接口,也就是根据文章ID来查询单篇文章接口。
  • 删除文章接口,根据传递的文章ID删除文章,可以做成软删除,方便数据恢复,建议不直接做物理删除。

从用户登录管理角度,需要如下接口:

  • 用户注册接口,需要填写用户名、密码、邮箱或手机、注册时间、账号状态、账号权限。
  • 用户登录接口,通过密码和账号登录获取 Token。
  • 用户退出接口,退出后注销相应的 Token。

2.基础服务搭建 

采用 SpringBoot + Druid + MyBatis + Redis +Tomcat 架构,首先要安装相关依赖,如pom.xml,代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>myblog</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.9.RELEASE</version>
        <relativePath/>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--    引入处理的json依赖包    -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.73</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

    </dependencies>

</project>

然后是数据库准备,单独创建一个 my_blogs 数据库,并创建表结构。

使用者表my_user

role_id是角色ID,是对应的权限角色表my_roles中的主键,而password 需要进行MD5加密保存,所以将长度设置成 100 更为合理。

字段

类型

长度

默认值

是否为空

索引

备注

id

int

11

主键,自增

主键ID

name

varchar

80

账号名

password

varchar

100

密码

email

varchar

80

邮箱

role_id

int

11

外键

角色ID

status

tinyint

2

状态,1:正常,2:封禁

reg_time

int

11

注册时间

文章表my_articles

其中category_id是外键,对应的是 my_artice_categories 表中的主键。

字段

类型

长度

默认值

是否为空

索引

备注

id

Int

11

主键,自增

主键ID

titile

varchar

80

标题

author

varchar

80

作者名

content

text

内容

category_id

int

11

外键

分类ID

tags

varchar

300

标签,以逗号分隔

is_deleted

tinyint

1

1

是否删除,1表示未删除2表示删除

created

Int

11

创建时间

modified

int

11

修改时间

文章分类表my_artice_categories

Creator_id 对应my_users表中的主键,也就是创建人的id。

字段

类型

长度

默认值

是否为空

索引

备注

id

int

11

主键,自增

主键ID

name

varchar

80

分类名称

creator_id

int

11

创建人ID

desc

varchar

500

分类描述

created

int

11

创建时间

modified

int

11

修改时间

权限表my_roles

字段

类型

长度

默认值

是否为空

索引

备注

id

int

11

主键,自增

主键ID

name

varchar

80

分类名称

creator_id

int

11

创建人ID

desc

varchar

500

分类描述

created

int

11

创建时间

modified

int

11

修改时间

根据上面的设计在数据库中创建四个表即可。 

四张表及外键的sql语句:

create database my_blogs;
use my_blogs;
create table my_user(
    `id` int(11) NOT NULL Primary Key AUTO_INCREMENT comment '主键ID',
    `name` varchar(80) not null  comment '账号名',
    `password` varchar(100) not null  comment '密码',
    `email` varchar(80) NULL  comment '邮箱',
    `role_id` int(11) NOT NULL comment '角色ID',
    `status` tinyint(2) NOT NULL comment '状态,1:正常,2:封禁',
    `reg_time` int(11) NOT NULL comment '注册时间'
);
create table my_roles(
    `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT comment '主键ID',
    `name` varchar(80) not null comment '分类名称',
    `creator_id` int(11) not null comment '创建人ID',
    `desc` varchar(500) NULL comment '分类描述',
    `created` int(11) not null comment '创建时间',
    `modified` int(11) not null comment '修改时间'
);
alter table my_users add constraint fk_roleId_myRoles foreign key (role_id) REFERENCES my_roles(id);

create table my_articles(
    `id` int(11) not null  primary key auto_increment comment '主键ID',
    `title` varchar(80) not null  comment '标题',
    `author` varchar(80) not null comment '作者名',
    `content` text NULL NULL comment '内容',
    `category_id` int(11) not null comment '分类ID',
    `tages` varchar(300) not null comment '标签,以逗号分隔',
    `is_deleted` tinyint(1) default 1 not null  comment '是否删除:1表示未删除,2表示删除',
    `created` int(11) not null comment '创建时间',
    `modified` int(11) not null comment '修改时间'
);

create table my_artice_categories(
    `id` int(11) not null primary key auto_increment comment '主键ID',
    `name` varchar(80) not null comment '分类名称',
    `creator_id` int(11) not null  comment '创建人ID',
    `desc` varchar(500) NULL comment '分类描述',
    `created` int(11) not null comment '创建时间',
    `modified` int(11) null comment '修改时间'
);
alter table my_articles add constraint fk_categoryId_myArticeCategories foreign key (category_id)
references my_artice_categories(id);


alter table my_artice_categories add  constraint  fk_createdId_myUsersId foreign key (creator_id)
references my_users(id);
-- 我自己拟定的权限规则,你也可以自己定义
insert into my_roles(name, creator_id, `desc`, created, modified) VALUES ('管理员权限','1','任何操作',0,0),('默认权限','1','可以上传文章,修改自己文章,查看文章',00000,000000),('低级权限','1','仅查看',0,0);

 创建通用型 JSON 返回数据格式

package org.example.tools;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class JsonResultObject <T>{
    private String code;
    private String message;
    private String erroeMessage;
    private String errorCode;
    private T data;
}

保存登录密码在数据库中也是 md5 加密的,而登录时会对明文传递的密码进行 MD5化,所以要创建 md5 工具类 

package org.example.tools;

import org.springframework.stereotype.Component;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

@Component
public class Md5Utils {
    /**
     * 将字符串 md5化
     * @param plainText
     * @return 16进制表示的字符串
     */
    public static String stringToMD5(String plainText) {
        byte[] secretBytes = null;
        try {
            //使用MessageDigest类获取MD5算法实例,然后将输入字符串转换为字节数组并进行加密
            secretBytes = MessageDigest.getInstance("md5").digest(
                    plainText.getBytes());
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("没有这个MD5算法");
        }
        //将加密后的字节数组转换为BigInteger对象,并使用16进制形式的字符串表示
        String md5code = new BigInteger(1, secretBytes).toString(16);
        for (int i = 0; i < 32 - md5code.length(); i++) {
            //对字符串进行前补0操作,确保字符串长度为32位
            md5code = "0" + md5code;
        }
        return md5code;
    }
}

创建错误枚举类 

package org.example.tools;

import lombok.AllArgsConstructor;

@AllArgsConstructor
public enum ErrorEnum {
    BAD_PARAM("1002","参数有错"),
    NOT_FOUNT("1003","资源不存在"),
    NO_PERMISSION("1004","权限不足"),
    BAD_INPUT_PARAM("1005","入参有问题");
    PASSWORD_OR_USERNAME_WRONG("1006", "密码或者用户错误");
    private String errorMsg;
    private String errorCode;

    public String getErrorMsg() {
        return errorMsg;
    }

    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }

    public String getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(String errorCode) {
        this.errorCode = errorCode;
    }
}

数据库连接池配置,代码如下:

server:
  port: 8080
spring:
  redis:
    host: 192.168.193.141
    port: 6379
    database: 0
    password: 123456
    jedis:
      pool:
        max-active: 50
        max-idle: 20
        max-wait: 3000
        min-idle: 2
    timeout: 5000
  datasource:
    url: jdbc:mysql://localhost:3306/spring_boot?useTimezone=true&serverTimezone=UTC
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
    druid:
      initial-size: 5
      min-idle: 5
      max-active: 20
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      pool-prepared-statements: true
      max-pool-prepared-statement-per-connection-size: 20
      max-wait: 60000
      time-between-eviction-runs-millis: 6000
      min-evictable-idle-time-millis: 3000
      fiters: stat
      async-init: true
      connection-properties: druid.stat.mergeSql=true;druid.stat.SlowSqlMills=5000
      monitor:
        allow: 127.0.0.1
        loginUsername: admin
        loginPassword: admin
        resetEnable: false
  swagger:
    enable: true
mybatis:
  configuration:
    mapUnderscoreToCamelCase: true

除了数据库服务外,还需要Redis服务,如果有需要相关知识的请自行补充。

3.登录接口

登陆接口主要是传递参数账号和密码,然后通过和数据的匹配,完成认证并返回 Token 。这个 Token 有一定的有效时间限制,且用于发布文章等接口的权限操作,

由于登录参数可以对象化,所以创建 loginUser 类,代码如下:

package org.example.tools;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginUser {
    //登录 ID
    private long id;
    //登录账号
    private String username;
    //登录密码
    private String password;
}

创建 my_users 表对应的 pojo 类

package org.example.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class MyUser {
    private int id;
    private String name;
    private String password;
    private String email;
    private int roleId;
    private String status;
    private int regTime;
}

创建 my_users 表对应的 Mapper 类

package org.example.mapper;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.example.pojo.MyUser;
import org.example.tools.LoginUser;

import java.util.List;

@Mapper
public interface MyUserMapper {
    @Select("SELECT * FROM my_users order by reg_time desc")
    List<MyUser> findAll();

    @Select("SELECT * FROM  my_users WHERE id = #{id}")
    MyUser findById(int id);

    @Select("SELECT * FROM my_users WHERE name = #{name}")
    MyUser findByName(String name);

    @Select("UPDATE my_users set status = 2 where id = #{id}")
    MyUser deleteUser(int id);

    @Insert("insert my_users(name, password, email, role_id, status, reg_time) values (#{name},#{password},#{email},#{role_id},#{status},#{regTime})")
    boolean add(MyUser myUser);

    @Select("SELECT id FROM my_users WHERE name=#{username} and password = #{password}")
    Integer doLogin(LoginUser loginUser);
}

编写对应的 Service 文件

package org.example.service;

import org.example.mapper.MyUserMapper;
import org.example.tools.ErrorEnum;
import org.example.tools.JsonResultObject;
import org.example.tools.LoginUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class LoginService {
    @Autowired
    MyUserMapper myUserMapper;
    public JsonResultObject doLogin(LoginUser loginUser){
        JsonResultObject result = new JsonResultObject();
        ErrorEnum enum1 = ErrorEnum.valueOf(ErrorEnum.class,"PASSWORD_OR_USERNAME_WRONG");
        result.setCode("200");
        result.setMessage("");
        result.setErrorCode("");
        result.setErroeMessage("");
        try{
            Integer userId = myUserMapper.doLogin(loginUser);
            if (userId == null){
                result.setErroeMessage("用户名或密码错误");
                result.setErrorCode(enum1.getErrorCode());
                result.setMessage(enum1.getErrorMsg());
            }else {
                result.setMessage("登录成功");
            }
        }catch (Exception e){
            result.setCode("500");
            result.setErrorCode("100211");
            result.setErroeMessage(e.getMessage());
        }
        return result;
    }
}

登录完成后生成 Token ,于是编写一个用于 Token 生成的 Service 类。

package org.example.service;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import org.example.pojo.MyUser;
import org.springframework.stereotype.Service;

import java.util.Date;

@Service
public class TokenService {
    public String getToken(MyUser user){
        String token = "";
        //只有一个小时时间
        Date start = new Date();
        long currentTime = System.currentTimeMillis() + 60*60*1000;
        Date end = new Date(currentTime);
        token = JWT.create()
                .withAudience(String.valueOf(user.getId()))
                .withIssuedAt(start)//开始时间
                .withExpiresAt(end)//过期时间
                .sign(Algorithm.HMAC256(user.getPassword() + "MText!76&sQ^"));
        return token;

    }
}

下面继续编写处理注册和登录功能的 Controller,由于操作的实体类都和 my_users 表有关系,所以命名为UserController,代码如下:

package org.example.controller;


import com.alibaba.fastjson.JSONObject;
import org.example.pojo.MyUser;
import org.example.service.TokenService;
import org.example.service.UserService;
import org.example.tools.JsonResultObject;
import org.example.tools.LoginUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping()
public class UserController {
    @Autowired
    UserService userService;
    @Autowired
    TokenService tokenService;

    @PostMapping("/register")
    public JsonResultObject register(@RequestBody MyUser user){
        return userService.register();
    }
    @PostMapping("/login")
    public JsonResultObject login(@RequestBody LoginUser loginUser){
        JsonResultObject result = userService.login(loginUser);
        if (result.getErroeMessage() != ""){
            return result;
        }else {
            String token = tokenService.getToken((MyUser) result.getData());
            JSONObject returnObject = new JSONObject();
            returnObject.put("token",token);
            result.setData(returnObject);
            return result;
        }
    }
}

在 UserController 中定义类注册接口 /register 和登录接口 /login ,把更多的业务逻辑封装在 UserService 中,而让 Controller 中只做服务调用操做,以达到业务解耦的作用。而UserService中的代码也很清晰。

package org.example.service;

import org.example.mapper.MyUserMapper;
import org.example.pojo.MyUser;
import org.example.tools.ErrorEnum;
import org.example.tools.JsonResultObject;
import org.example.tools.LoginUser;
import org.example.tools.Md5Utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    @Autowired
    MyUserMapper myUserMapper;

    /**
     * 注册
     * @param myUser 传递的用户数据
     * @return  JsonResultObject
     */
    public JsonResultObject register(MyUser myUser) {
        //md5 处理密码
        String password = Md5Utils.stringToMD5(myUser.getPassword());
        myUser.setPassword(password);
        long unixTime = System.currentTimeMillis()/1000L;
        int nowUnixTime = (int) unixTime;
        myUser.setRegTime(nowUnixTime);
        boolean addResult = myUserMapper.add(myUser);
        //初始化 JSON 返回对象
        JsonResultObject jsonResultObject = this.initJsonResultObject();
        if (addResult){
            jsonResultObject.setMessage("新建用户成功");
        }else {
            jsonResultObject.setErroeMessage("新建用户失败");
            jsonResultObject.setErrorCode("202311");
        }
        return jsonResultObject;
    }


    public JsonResultObject login(LoginUser loginUser) {
        JsonResultObject result = this.initJsonResultObject();
        ErrorEnum enum1 = ErrorEnum.valueOf(ErrorEnum.class,"PASSWORD_OR_USERNAME_WRONG");
        try {
            //md5处理密码
            String password = Md5Utils.stringToMD5(loginUser.getPassword());
            loginUser.setPassword(password);
            Integer userId = myUserMapper.doLogin(loginUser);
            if (userId == null){
                result.setErroeMessage("用户名或密码错误");
                result.setErrorCode(enum1.getErrorCode());
                result.setErroeMessage(enum1.getErrorMsg());
            }else {
                //创建一个MyUser对象
                MyUser currentUser = new MyUser();
                currentUser.setId(userId);
                currentUser.setPassword(password);
                currentUser.setName(loginUser.getName());
                result.setData(currentUser);
                result.setMessage("登录成功");
            }
        }catch (Exception e){
            result.setCode("200");
            result.setErroeMessage(e.getMessage());
            result.setErrorCode("100211");
        }
        return result;
    }


    private JsonResultObject initJsonResultObject() {
        JsonResultObject result = new JsonResultObject();
        result.setCode("200");
        result.setMessage("");
        result.setErroeMessage("");
        result.setErrorCode("");
        return  result;
    }


}

注册与登录成功 

4.新增文章接口 

在完成了登录注册功能接口后,开始编写文章相关接口,先从新增文章开始,设计接口交互。充分考虑各种条件,如:必须是登录的用户才能进行发布文章,所以该接口需要进行 Token验证,毕竟不是谁都能直接发布文章的,不然乱套了。

有人可能会想在接口逻辑中对 Hreader 中的 Token 进行检查,把判断写在 Controller 中,这相当于 hard code ,这样的处理并不优雅。这时,拦截器就是值得考虑的选择了。这里分别编写 UserLoginToken 和 PassToken 以及相关拦截器,接下来从现实的角度上编程,从 pojo 开始编写。

拦截器:

package org.example.intercepetor;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import org.example.anno.PassToken;
import org.example.anno.UserLoginToken;
import org.example.common.BusinessException;
import org.example.pojo.MyUser;
import org.example.service.UserService;
import org.example.tools.LoginUser;
import org.example.tools.UserContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

public class AuthInterceptor implements HandlerInterceptor {
    @Autowired
    UserService userService;
    //@Autowired
    //RedisUtil redisUtil;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("token");
        //排除访问的是静态资源,而不是映射访问
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        //获取访问的方法
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        if (method.isAnnotationPresent(PassToken.class)) {
            PassToken passToken = method.getAnnotation(PassToken.class);
            if (passToken.requried()) {
                return true;
            }
        }
        if (method.isAnnotationPresent(UserLoginToken.class)) {
            UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
            if (userLoginToken.requried()) {
                //判空
                if (token == null) {
                    throw new BusinessException("4001", "no token");
                }
                String userId;
                try {
                    //获取token的受众列表中的第一个受众,将其赋值给变量userId。
                    userId = JWT.decode(token).getAudience().get(0);
                } catch (Exception e) {
                    throw new BusinessException("4003", "decode token fails");
                }

                // Check the expire of token.
//                String tokenKey = userId + ":" + token;
//                boolean hasExisted = redisUtil.hasKey(tokenKey);
//                System.out.println("exist or not:" + hasExisted);
//                if (hasExisted == false) {
//                    throw new BusinessException("4005", "token expired!");
//                }
                int userIdt = Integer.parseInt(userId);
                System.out.println("userId is "+userIdt);
                MyUser myUser = userService.findUserById(userIdt);
                if (myUser == null){
                    throw new RuntimeException("user no exists");
                }
                try {
                    //验证JWT 令牌
                    JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(myUser.getPassword()+"MText!76&sQ^")).build();
                    //可以验证过期时间
                    jwtVerifier.verify(token);
                    //设置当前登录用户
                    LoginUser loginUser = new LoginUser();
                    loginUser.setId(userIdt);
                    UserContext.setUser(loginUser);
                }catch (JWTVerificationException e){
                    System.out.println(e.getMessage());
                    throw new BusinessException("4002",e.getMessage());
                }
            }
        }
        return true;

    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

注册拦截器

package org.example.intercepetor;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class AuthInterceptorRegister implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authInterceptor()).addPathPatterns("/**");
    }

    @Bean
    public AuthInterceptor authInterceptor(){
        return new AuthInterceptor();
    }
}

 对应的相关注解

//跳过验证
package org.example.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface PassToken {
     boolean requried()  default true;
}

//进行验证

package org.example.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface UserLoginToken {
    boolean requried()default true;
}

创建 my_articles 对应的 MyArticle.Java 文件 

package org.example.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class MyArticle {
    //文章ID
    private int id;
    //文章标题
    //使用注解实现字段检查 ,NotBlank用于验证字符串类型不为空
    @NotBlank(message = "文章标题不能为空")
    private String title;
    //文章作者
    @NotBlank(message = "作者名不能是空")
    private String author;
    //文章内容
    @NotBlank(message = "文章内容不能是空")
    private String content;
    //文章分类ID
    @NotNull(message = "分类ID不能为空")
    private int categoryId;
    //标签 以逗号分隔
    private String tags;
    //是否删除 1.表示删除 2.表示已删除
    private int is_deleted;
    //创建时间
    private int created;
    //修改时间
    private int modified;
}

然后在 MyArticleMapper.java 编写新建文章方法,代码如下。

package org.example.mapper;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.example.pojo.MyArticle;

@Mapper
public interface MyArticleMapper {

    @Insert("INSERT INTO my_articles(title, author, content, category_id, tages, created,modified) VALUES (#{title},#{author},#{content},#{categoryId},#{tags},#{created},#{modified})")
    boolean add(MyArticle myArticle);
}

下一步创建对应的 ArticleService.java ,增加 add 方法。

package org.example.service;

import org.example.mapper.MyArticleMapper;
import org.example.pojo.MyArticle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class ArticleService {
    @Autowired
    MyArticleMapper myArticleMapper;

    //创建文章
    public boolean add(MyArticle myArticle) {
        long unixTime = System.currentTimeMillis() / 1000L;
        int nowUnixTime = (int) unixTime;
        myArticle.setCreated(nowUnixTime);
        myArticle.setModified(nowUnixTime);
        return myArticleMapper.add(myArticle);
    }
}

最后创建 Controller ,只写一个创建文章的接口,代码如下。

package org.example.controller;

import org.example.pojo.MyArticle;
import org.example.service.ArticleService;
import org.example.tools.ErrorEnum;
import org.example.tools.JsonResultObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("")
public class ArticleController {
    @Autowired
    ArticleService articleService;

    @PostMapping("/article")
    //防止传入空参  默认会把处理的结果传给BindingResult对象。
    public JsonResultObject add(@Validated @RequestBody MyArticle myArticle, BindingResult bindingResult){
        try {
            if (bindingResult.hasErrors()){
                return new JsonResultObject("400","新发布文章失败!",bindingResult.getFieldError().getDefaultMessage(),"Bad Params",null);
            }
            boolean addResult = articleService.add(myArticle);
            if (addResult){
                return new JsonResultObject("200","新发布文章成功","","",null);
            }else {
                ErrorEnum enum1 = ErrorEnum.valueOf(ErrorEnum.class,"BAD_INPUT_PARAM");
                return new JsonResultObject("200","新发布文章失败",enum1.getErrorMsg(), enum1.getErrorCode(), null);
            }
        }catch (Exception e){
            return new JsonResultObject<>("400","发布最新文章失败!", e.getMessage(), "Bad Params",null);
        }
    }
}

测试成功: 

可以加上 Token 验证,只需要在请求头中填写好 Token 信息。

5.查询文章接口 

相比于新增文章接口,查询文章接口就比较简单,不用进行 Token 验证。查询分为两种:一种是查询文章列表,另一种是查询文章详情。 

先在 Controller 文件中编写获取文章列表的接口,具体代码如下。 

//获取文章列表 不分页
    @RequestMapping("/articles")
    public JsonResultObject getAll() {
        List<MyArticle> articles = articleService.findAll();
        JsonResultObject result = new JsonResultObject("200", "get   articles", "", "", articles);
        return result;
    }

    //获取文章列表 分页
    @RequestMapping("/articles/{pageNum}")
    public JsonResultObject getListByPageNum(@PathVariable int pageNum) {
        List<MyArticle> articles = articleService.getListByPageNum(pageNum);
        JsonResultObject result = new JsonResultObject("200", "get   articles", "", "", articles);
        return result;

    }

然后在对应的 Service 文件中增加对应的方法。

    //获取文章列表 不分页
    public List<MyArticle> findAll() {
        return myArticleMapper.findAll();
    }
    //获取文章列表 分页
    public List<MyArticle> getListByPageNum(int pageNum) {
        if (pageNum <= 0){
            pageNum = 1;
        }
        int offset = (pageNum-1)*30;
        return myArticleMapper.getListByPageNum(offset);
    }

最后在对应的 Mapper 文件中增加相应的方法。

    //查询文章列表 不分页
    @Select("select * from my_articles where is_deleted = 1")
    List<MyArticle> findAll();

    //查询文章列表 分页
    @Select("select * from my_articles where is_deleted = 1 limit #{offset},30")
    List<MyArticle> getListByPageNum(int offset);

测试 

分页

不分页

编写获取文章详情的接口 

//获取文章详情的接口
    @RequestMapping("/article/{id}")
    public JsonResultObject detail(@PathVariable int id){
        MyArticle article = articleService.detail(id);
        JsonResultObject result = new JsonResultObject("200","get   articles","","",article);
        return result;
    }

对应的 Service 中代码如下

//文章详情
    public MyArticle detail(int id) {
        return myArticleMapper.detail(id);
    }

对应的 Mapper 中代码如下

    //获取文章详情
    @Select("select * from my_articles where id = #{id}")
    MyArticle detail(int id);

6.修改文章接口 

修改文章接口需要传递包含 id 在内的文章数据,先编写 pojo 的修改文章。 

    //更新文章
    @Update("update my_articles set author= #{author},content=#{content},category_id=#{categoryId},tages = #{tags},modified=#{modified} where id =#{id}")
    public boolean update(MyArticle myArticle);

Service文件中只需要添加如下代码即可

    //修改文章
    public boolean update(MyArticle myArticle){
        long unixTime = System.currentTimeMillis() / 1000L;
        int nowUnixTime = (int) unixTime;
        myArticle.setModified(nowUnixTime);
        return myArticleMapper.update(myArticle);
    }

在 Controller 中完成业务判断和调用,和 add 方法类似,只是使用的注解变成了 Put 

    //修改文章
    @PutMapping("/article")
    @UserLoginToken
    public JsonResultObject update(@Validated @RequestBody MyArticle myArticle,BindingResult bindingResult){
        if (bindingResult.hasErrors()){
            return new JsonResultObject<>("400","修改文章失败!",bindingResult.getFieldError().getDefaultMessage(),"40002",null);
        }else {
            if (myArticle.getId()==0){
                return new JsonResultObject("400","文章修改失败!","no Id","40003",null);
            }else {
                boolean updateResult = articleService.update(myArticle);
                if (updateResult){
                    return new JsonResultObject<>("200","修改文章成功","","",null);
                }else {
                    ErrorEnum enum1 = ErrorEnum.valueOf(ErrorEnum.class,"BAD_PARAM");
                    return new JsonResultObject("200","修改文章失败", enum1.getErrorMsg(), enum1.getErrorCode(), null);
                }
            }
        }
    }

修改成功

7.删除文章接口 

删除文章实际上是一种软删除,修改 is_deleted 字段为 2 。 

先在 Mapper 文件中编写删除的映射方法,代码如下

    //删除文章 软删除
    @Update("update my_articles set is_deleted = 2 where id = #{id}")
    public boolean delete(int id);

然后在 Service 中增加调用的方法,代码如下

    //删除
    public boolean delet(int id){
        return myArticleMapper.delete(id);
    }

最后在 Controller 文件中增加删除的路由和调用代码。

    //Delet
    @DeleteMapping("/article/{id}")
    public JsonResultObject delete(@PathVariable int id) {
        boolean deleteResult = articleService.delet(id);
        if (deleteResult) {
            return new JsonResultObject("200", "删除文章成功", "", "", null);
        } else {
            ErrorEnum enum1 = ErrorEnum.valueOf(ErrorEnum.class, "BAD_PARAM");
            return new JsonResultObject("200", "删除文章失败!", enum1.getErrorMsg(), enum1.getErrorCode(), null);
        }
    }

删除成功  

总结 

ending......... 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1266824.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Windows下搭建Tomcat HTTP服务,发布公网远程访问

文章目录 前言1.本地Tomcat网页搭建1.1 Tomcat安装1.2 配置环境变量1.3 环境配置1.4 Tomcat运行测试1.5 Cpolar安装和注册 2.本地网页发布2.1.Cpolar云端设置2.2 Cpolar本地设置 3.公网访问测试4.结语 前言 Tomcat作为一个轻量级的服务器&#xff0c;不仅名字很有趣&#xff0…

第二证券:五日线是什么颜色的线?

从概念解说上来看&#xff0c;五日线是一种短期移动平均线&#xff0c;通常使用股票五日收盘价的平均值进行核算&#xff0c;然后描绘出股票短线的走势趋势。在技能图上&#xff0c;五日线通常以赤色或蓝色表示&#xff0c;不同的平台能够设定不同的色彩。 在实践应用过程中&a…

海康运行管理中心 RCE漏洞复现

0x01 产品简介 海康威视是以视频为核心的智能物联网解决方案和大数据服务提供商。海康运行管理中心是一款功能强大、易于使用的安防管理平台&#xff0c;能满足用户对视频监控、报警管理、设备配置和数据统计等方面的需求&#xff0c;帮助用户建立高效、智能的安防系统。 0x02…

前端算法专栏-数组-75.颜色分类

介绍 Hi 大家好。我是程序员库里&#xff0c;今天新开一个前端算法专栏。 接下来会分类给大家分享常考算法题目。 很多朋友也是看着这套系列算法拿到很多offer&#xff01;所以也是想分享给更多朋友&#xff0c;帮助到有需要的朋友。 分类 数组-三路快排 题目 75. 颜色分…

20-多线程

20.1线程简介 世间有很多工作都是可以同时完成的。例如&#xff0c;人体可以同时进行呼吸、血液循环、思考问题等活用户既可以使用计算机听歌&#xff0c;也可以使用它打印文件。同样&#xff0c;计算机完全可以将多种活动同时进这种思想放在 Java 中被称为并发&#xff0c;而…

JAVA基础进阶(八)

一、Map的体系结构 单列集合的顶层接口是Collection接口,双列集合的顶层接口是Map接口。 双列集合的元素是成对出现的&#xff0c;每一个元素称之为一个键值对。 二、Map系列集合的特点 Map系列集合的特点有以下几点: 键不能重复、值可以重复每一个元素都是一个键值对HashMa…

Xilinx Zynq-7000系列FPGA多路视频处理:图像缩放+视频拼接显示,提供工程源码和技术支持

目录 1、前言免责声明 2、相关方案推荐FPGA图像处理方案FPGA图像缩放方案FPGA视频拼接叠加融合方案推荐 3、设计思路详解HLS 图像缩放介绍Video Mixer介绍 4、vivado工程介绍PL 端 FPGA 逻辑设计PS 端 SDK 软件设计 5、工程移植说明vivado版本不一致处理FPGA型号不一致处理其他…

【C++ Primer Plus学习记录】do while循环

do while循环是出口条件循环。这意味着这种循环将首先执行循环体&#xff0c;然后再判定测试表达式&#xff0c;决定是否应继续执行循环。如果条件为false&#xff0c;则循环终止&#xff1b;否则&#xff0c;进入新一轮的执行和测试。这样的循环通常至少执行一次&#xff0c;因…

Element的安装与基本使用

文章目录 一.什么是Element?二.Element的安装(Vscode)三.在Vue项目中引入ElementUI组件库四.编写Element的Vue组件文件五.演示如何使用Element官网中的组件根组件中引入Element组件内容进行网页展示 一.什么是Element? Element是饿了么团队研发的一套为开发者与设计师等准备…

Unity工具脚本-检测资源文件夹是否有预制件是指定层级

效果&#xff1a; 先在菜单栏里面找到Tools/CheckPrefabLayers打开窗口 代码&#xff1a; using System.Collections; using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine;public class CheckPrefabLayers : EditorWindow {public in…

典型的SAST支持检测标准

这里我们列举了Coverity、Cobot、代码卫士、Klocwork、QAC、C test几款典型的SAST工具&#xff0c;看看他们都是支持那些C、C标准&#xff08;主要是C、C标准&#xff0c;其它语言较少&#xff09;呢&#xff1f; 这可以作为厂商研发的方向标。 &#xff08;结束&#xff09;

好物分享(领劵优惠好手)

本次分享一个领取领优惠、充值、会员、购物优惠等功能 部分截图 需要的点击下方按钮 前往体验 感谢大家的支持 更多内容请关注微信小程序&#xff0c;源码、插件、模板及时更新

Breadcrumb面包屑(antd-design组件库)简单用法和自定义分隔符

1.Breadcrumb面包屑 显示当前页面在系统层级结构中的位置&#xff0c;并能向上返回。 2.何时使用 当系统拥有超过两级以上的层级结构时&#xff1b; 当需要告知用户『你在哪里』时&#xff1b; 当需要向上导航的功能时。 组件代码来自&#xff1a; 面包屑 Breadcrumb - Ant Des…

【功能测试】软件系统测试报告

1.引言 1.1.目的 本测试报告为 xxx 系统测试报告&#xff0c;本报告目的在于总结测试阶段的测试及测试结果分析&#xff0c;描述系统是否达到需求的目的。 本报告预期参考人员包括测试人员、测试部门经理、开发人员、项目管理人员等。 1.2.参考文档 《xxxx系统需求规格说明…

【MATLAB】RLMD分解+FFT+HHT组合算法

有意向获取代码&#xff0c;请转文末观看代码获取方式~也可转原文链接获取~ 1 基本定义 RLMD分解FFTHHT组合算法是一种强大的分析方法&#xff0c;结合了局部均值分解&#xff08;LMD&#xff09;、快速傅里叶变换&#xff08;FFT&#xff09;和希尔伯特-黄变换&#xff08;H…

第二证券:机构密集调研消费电子、半导体产业链

据上海证券报记者核算&#xff0c;近一个月来&#xff0c;共有41家消费电子类公司和92家半导体公司&#xff08;核算标准&#xff1a;申万职业2021&#xff0c;下同&#xff09;发布出资者调研纪要。其间&#xff0c;有的公司款待了16个批次估计超200家安排&#xff0c;更有公司…

P8A009-A011活动目录安全

设置目录数据库访问权限 【预备知识】 活动目录&#xff08;Active Directory&#xff09;是面向Windows Standard Server、Windows Enterprise Server以及 Windows Datacenter Server的目录服务。&#xff08;Active Directory不能运行在Windows Web Server上&#xff0c;但…

SpringBoot : ch10 整合Elasticsearch

前言 欢迎阅读本文&#xff0c;本文将介绍如何在Spring Boot应用程序中整合Elasticsearch。随着信息量的不断增加&#xff0c;对数据的高效管理和检索变得尤为重要。Elasticsearch作为一个强大的开源搜索和分析引擎&#xff0c;为我们提供了一个灵活且高效的解决方案。 在本文…

sqli-labs靶场详解(less1-less10)

目录 less-1 less-2 less 3 less 4 less 5 less-6 less-7 less-8 less-9 less-10 1-10关代码分析 less-1 判断注入点 ?id1 正常 ?id1 报错&#xff1a;to use near 1 ?id1\ 报错&#xff1a;to use near 1\ ?id1 and 11 正常 ?id1 and 11 报错&#xff1a;to …

Debian10安装VMware Tools

一、原系统 首先我在界面按CTRLALTT和CTRLSiftT都没有反应&#xff0c;没关系&#xff0c;我有办法 系统版本 管理员用户 步骤一&#xff1a;打开VMware Tools文件 步骤二、将文件复制到自己熟悉的文件内 步骤三、命令行查看文件是否复制成功存在 步骤四、解压VMware-tools…