隐约雷鸣,阴霾天空。

使用SpringBoot写一个一言api

使用SpringBoot写一个一言api

高中的时候很喜欢这东西,不过那时候因为技术原因写不出来,别人的代码也看不懂,今天突然想起这个,就随手写了一个。

Github: https://github.com/Aurora1468/hitokoto-project-for-Spring-Boot

首先去一言的句子库把句子下下来:

一言的句子库: [https://github.com/hitokoto-osc/sentences-bundle]

放在resource文件夹下就行:

到这里准备工作就完成了,然后开始写controller,首先在方法外面读取文件。

ObjectMapper mapper = new ObjectMapper();
    ArrayList<ArrayList<Object>> lists = new ArrayList<>();
    {
        try {
            lists.add(mapper.readValue(new ClassPathResource("public/sentences/a.json").getInputStream(), ArrayList.class));
            lists.add(mapper.readValue(new ClassPathResource("public/sentences/b.json").getInputStream(), ArrayList.class));
            lists.add(mapper.readValue(new ClassPathResource("public/sentences/c.json").getInputStream(), ArrayList.class));
            lists.add(mapper.readValue(new ClassPathResource("public/sentences/d.json").getInputStream(), ArrayList.class));
            lists.add(mapper.readValue(new ClassPathResource("public/sentences/e.json").getInputStream(), ArrayList.class));
            lists.add(mapper.readValue(new ClassPathResource("public/sentences/f.json").getInputStream(), ArrayList.class));
            lists.add(mapper.readValue(new ClassPathResource("public/sentences/g.json").getInputStream(), ArrayList.class));
            lists.add(mapper.readValue(new ClassPathResource("public/sentences/h.json").getInputStream(), ArrayList.class));
            lists.add(mapper.readValue(new ClassPathResource("public/sentences/i.json").getInputStream(), ArrayList.class));
            lists.add(mapper.readValue(new ClassPathResource("public/sentences/j.json").getInputStream(), ArrayList.class));
            lists.add(mapper.readValue(new ClassPathResource("public/sentences/k.json").getInputStream(), ArrayList.class));
            lists.add(mapper.readValue(new ClassPathResource("public/sentences/l.json").getInputStream(), ArrayList.class));
        } catch (IOException e) {
            e.printStackTrace();
        }

需要注意的是不能使用getFile(),不然打成jar包的时候会出现找不到文件路径的问题。

然后就是常规的请求:

@RequestMapping("/hitokotoApi")
    public String hitokotoApi(Param params) throws UnsupportedEncodingException {
        
        int jsonIndex;
        
        if(params.getC() == null){
            jsonIndex = (int)(Math.random() * 12);
        } else
            jsonIndex = params.getC().toCharArray()[0]-'a';
        ArrayList<Object> list = lists.get(jsonIndex);
        HashMap<String,Object> map = (HashMap) list.get((int)(Math.random() * list.size()));
        String ret = map.toString();
        if(params.getEncode()!=null&&params.getEncode().equals("text")){
            ret = (String) map.get("hitokoto");
        }
        if(params.getCharset()!=null&&params.getCharset().equals("gbk")){
            ret=new String(ret.getBytes(),"GBK");
        }
        accessNum++;
        System.out.println("被访问了:" + accessNum + "次");
        return ret;
    }

为了防止被过度使用(虽然估计也没几个人会用2333),添加了一个频率检测,如下:

//定义一个注解
@Documented
@Target(ElementType.METHOD) // 说明该注解只能放在方法上面
@Retention(RetentionPolicy.RUNTIME)
public @interface LimitRequest {
    long time() default 5000; // 限制时间 单位:毫秒
    int count() default 3; // 允许请求的次数
}
//具体实现
@Aspect
@Component
public class LimitRequestAspect {

    private static ConcurrentHashMap<String, ExpiringMap<String, Integer>> book = new ConcurrentHashMap<>();

    // 定义切点
    // 让所有有@LimitRequest注解的方法都执行切面方法
    @Pointcut("@annotation(limitRequest)")
    public void excudeService(LimitRequest limitRequest) {
    }

    @Around("excudeService(limitRequest)")
    public Object doAround(ProceedingJoinPoint pjp, LimitRequest limitRequest) throws Throwable {

        // 获得request对象
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();

        // 获取Map对象, 如果没有则返回默认值
        // 第一个参数是key, 第二个参数是默认值
        ExpiringMap<String, Integer> uc = book.getOrDefault(request.getRequestURI(), ExpiringMap.builder().variableExpiration().build());
        Integer uCount = uc.getOrDefault(request.getRemoteAddr(), 0);



        if (uCount >= limitRequest.count()) { // 超过次数,不执行目标方法
            return "接口请求超过次数";
        } else if (uCount == 0){ // 第一次请求时,设置有效时间
//            /** Expires entries based on when they were last accessed */
//            ACCESSED,
//            /** Expires entries based on when they were created */
//            CREATED;
            uc.put(request.getRemoteAddr(), uCount + 1, ExpirationPolicy.CREATED, limitRequest.time(), TimeUnit.MILLISECONDS);
        } else { // 未超过次数, 记录加一
            uc.put(request.getRemoteAddr(), uCount + 1);
        }
        book.put(request.getRequestURI(), uc);

        // result的值就是被拦截方法的返回值
        Object result = pjp.proceed();

        return result;
    }


}

然后把这个注解添加到需要检测频率的方法上面就行了。

最后把它部署到服务器上,因为是jar包,所以一句命令就行了,然后在nginx里配置一下代理就可以不用端口访问了:

location / {

            #这里是编写监听到的请求所转发的端口号,即tomcat端口
            proxy_pass http://localhost:12345;
            #设置nginx 的默认显示页,可有可无
            index  index.html index.htm;
            #设置http请求的请求头,使其在跨域访问上不会被浏览器阻止。ps:这里设置我发现没有用,后来还是在ajax过滤器中添加的 请求头,如果大家有知道这里怎么修改的,请留言大家一起学习。
            add_header 'Access-Control-Allow-Origin' '*';
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
            add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
        }

上面这个配置文件是百度找的,随便修改了一下。

使用

你可以使用get或者post请求来使用这个api,如:

https://api.acgnx.top/hitokotoApi
https://api.acgnx.top/hitokotoApi?encode=text

上面这个请求不附带参数的时候是完全随机的,如果附带请求的话,如下:

参数可选说明
c见后表句子类型
encode见后表返回编码
charset见后表字符集

句子类型(参数):

参数说明
a动画
b漫画
c游戏
d文学
e原创
f来自网络
g其他
h影视
i诗词
j网易云
k哲学
l抖机灵
其他作为 动画 类型处理

返回编码(参数)

参数说明
text返回纯洁文本
json返回格式化后的 JSON 文本
其他返回格式化后的 JSON 文本

字符集(参数)

参数说明
utf-8返回 utf-8 编码的内容
gbk返回 gbk 编码的内容。
其他返回 utf-8 编码的内容

添加新评论