使用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&¶ms.getEncode().equals("text")){
ret = (String) map.get("hitokoto");
}
if(params.getCharset()!=null&¶ms.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 编码的内容 |
666
maiyang 2021-04-21