使用apifox工具获得掘金的发帖接口
掘金相对于csdn来说,是先创建文章(api.juejin.cn/content_api…),返回一个文章id
然后拿着这个文章id作为draft_id,调用发帖服务(api.juejin.cn/content_api…),才能完成文章的发送
注意哦:(参考下图)
- 分类id(category_id)和标签id(tag_ids)是必填项
- 编辑摘要(brief_content)必须是50-100字,不然后面测试的时候会报参数错误
:::
通过ai工具生成相应的DTO与Service
@Data
public class JueJinCreateRequest {
private String category_id="6809637769959178254";
private List<String> tag_ids= Collections.singletonList("6809640408797167623");
private String link_url="";
private String cover_image="";
private String title;
private String brief_content="11111111111111111111111111111111111111111111111111111111111111111111111111111111";
private Integer edit_type=10;
private String html_content="deprecated";
private String mark_content;
private List<String> theme_ids=new ArrayList<>();
private List<String> pics=new ArrayList<>();
}
@Data
public class JueJinCreateResponse {
private Integer err_no;
private String err_msg;
private ArticleData data;
@Data
public static class ArticleData {
private String id;
private String article_id;
private String user_id;
private String category_id;
private List<String> tag_ids;
private String link_url;
private String cover_image;
private Integer is_gfw;
private String title;
private String brief_content;
private Integer is_english;
private Integer is_original;
private Integer edit_type;
private String html_content;
private String mark_content;
private String ctime;
private String mtime;
private Integer status;
private Integer original_type;
private List<String> theme_ids;
}
}
@Data
public class JueJinPublishRequest {
private String draft_id;
private Boolean sync_to_org=false;
private List<String> column_ids=new ArrayList<>();
private List<String> theme_ids=new ArrayList<>();
private Integer encrypted_word_count=1077879;
private Integer origin_word_count=7;
}
@Data
public class JueJinPublishResponse {
private Integer err_no;
private String err_msg;
private ArticleData data;
@Data
public static class ArticleData {
private String article_id;
private String user_id;
private String category_id;
private List<String> tag_ids;
private Integer visible_level;
private String link_url;
private String cover_image;
private Integer is_gfw;
private String title;
private String brief_content;
private Integer is_english;
private Integer is_original;
private Integer user_index;
private Integer original_type;
private String original_author;
private String content;
private String ctime;
private String mtime;
private String rtime;
private Integer status;
private Integer verify_status;
private Integer audit_status;
private String mark_content;
private String org_id;
private Integer homepage_top_time;
private Integer homepage_top_status;
}
}
API服务接口
public interface IJueJinService {
/**
* 创建文章
*
* @param cookie Cookie信息
* @param authorization 授权信息
* @param request 创建请求
* @return 创建响应
*/
@POST("content_api/v1/article_draft/create")
Call<JueJinCreateResponse> createArticle(
@Header("Cookie") String cookie,
@Header("authorization") String authorization,
@Body JueJinCreateRequest request
);
/**
* 发布文章
*
* @param cookie Cookie信息
* @param authorization 授权信息
* @param request 发布请求
* @return 发布响应
*/
@POST("content_api/v1/article/publish")
Call<JueJinPublishResponse> publishArticle(
@Header("Cookie") String cookie,
@Header("authorization") String authorization,
@Body JueJinPublishRequest request
);
}
创建IJueJinService接口的实现类实例
@Bean
public IJueJinService jueJinService(OkHttpClient okHttpClient) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.juejin.cn/")
.client(okHttpClient)
.addConverterFactory(JacksonConverterFactory.create())
.build();
return retrofit.create(IJueJinService.class);
}
配置HTTP客户端配置类
@Configuration
public class HttpClientConfig {
/**
* 配置OkHttpClient
*
* @return OkHttpClient实例
*/
@Bean
public OkHttpClient okHttpClient() {
return new OkHttpClient.Builder()
.addInterceptor(new ContentTypeInterceptor())
.build();
}
/**
* Content-Type拦截器
* 为所有请求添加Content-Type: application/json头
*/
static class ContentTypeInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
// 为请求添加Content-Type头
Request newRequest = originalRequest.newBuilder()
.header("Content-Type", "application/json")
.build();
return chain.proceed(newRequest);
}
}
}
之后就可以进行接口调用测试了
测试接口
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class APITest {
@Autowired
private IJueJinService jueJinService;
@Resource
private JueJinApiProperties juejinApiProperties;
/**
* 创建文章
*
* @throws IOException
*/
@Test
public void testCreateArticle() throws IOException {
// 准备请求数据
JueJinCreateRequest request = new JueJinCreateRequest();
request.setCategory_id("6809637769959178254"); // 后端
request.setTag_ids(Arrays.asList("6809640408797167623")); // Java
request.setLink_url("");
request.setCover_image("");
request.setTitle("Java开发实践:掘金API集成测试");
request.setBrief_content("本文介绍了如何使用Java集成掘金API进行文章发布本文介绍了如何使用Java集成掘金API进行文章发布");
request.setEdit_type(10); // Markdown编辑模式
request.setHtml_content("deprecated");
request.setMark_content("hello");
request.setTheme_ids(new ArrayList<>());
request.setPics(new ArrayList<>());
// 设置Cookie和授权信息(实际测试时需要替换为真实值)
String cookie = juejinApiProperties.getCookie();
String authorization = juejinApiProperties.getAuthorization();
// 调用API
Call<JueJinCreateResponse> call = jueJinService.createArticle(cookie, authorization, request);
log.info("Sending request to JueJin API: {}", request);
// 执行请求并验证响应
Response<JueJinCreateResponse> response = call.execute();
log.info("Received response from JueJin API: {}", response.body());
// 验证响应
if (response.isSuccessful() && response.body() != null) {
JueJinCreateResponse responseBody = response.body();
assertEquals(0, responseBody.getErr_no().intValue());
assertEquals("success", responseBody.getErr_msg());
assertNotNull(responseBody.getData());
assertNotNull(responseBody.getData().getId());
System.out.println(responseBody.getData().getId());
} else {
log.error("API call failed: {}", response.errorBody() != null ? response.errorBody().string() : "Unknown error");
}
}
/**
* 发布文章
*
* @throws IOException
*/
@Test
public void testPublishArticle() throws IOException {
// 准备请求数据
JueJinPublishRequest request = new JueJinPublishRequest();
request.setDraft_id("7490003622233489435"); // 草稿ID,实际测试时需要替换为真实值
request.setSync_to_org(false);
request.setColumn_ids(new ArrayList<>());
request.setTheme_ids(new ArrayList<>());
request.setEncrypted_word_count(1077885);
request.setOrigin_word_count(1);
// 设置Cookie和授权信息(实际测试时需要替换为真实值)
String cookie = juejinApiProperties.getCookie();
String authorization = juejinApiProperties.getAuthorization();
// 调用API
Call<JueJinPublishResponse> call = jueJinService.publishArticle(cookie, authorization, request);
log.info("Sending publish request to JueJin API: {}", request);
// 执行请求并验证响应
Response<JueJinPublishResponse> response = call.execute();
log.info("Received response from JueJin API: {}", response.body());
// 验证响应
if (response.isSuccessful() && response.body() != null) {
JueJinPublishResponse responseBody = response.body();
assertEquals(0, responseBody.getErr_no().intValue());
assertEquals("success", responseBody.getErr_msg());
assertNotNull(responseBody.getData());
assertNotNull(responseBody.getData().getArticle_id());
} else {
log.error("API call failed: {}", response.errorBody() != null ? response.errorBody().string() : "Unknown error");
}
}
!其中JueJinApiProperties相比csdn来说添加了authorization这个参数,yml文件别忘配!
接口测试通过就可以进行下一步了
整合服务
接下来要做的就是把创建文章(create)与发表文章(publish)的接口结合起来
domain层还是沿用小傅哥的例子
infrastructure层进行writeArticle方法的编写
@Override
public ArticleFunctionResponse writeArticle(ArticleFunctionRequest request) throws IOException {
// 创建文章
JueJinCreateRequest articleRequestDTO = new JueJinCreateRequest();
articleRequestDTO.setTitle(request.getTitle());
articleRequestDTO.setMark_content(request.getMarkdowncontent());
if (request.getMarkdowncontent().length() > 50) {
articleRequestDTO.setBrief_content(request.getMarkdowncontent().substring(0, 70)); // 取前70个字符 (要求:50-100)
}
Call<JueJinCreateResponse> call = juejinService.createArticle(juejinApiProperties.getCookie(), juejinApiProperties.getAuthorization(), articleRequestDTO);
Response<JueJinCreateResponse> response = call.execute();
log.info("请求JueJin创建文章 \nreq:{} \nres:{}", JSON.toJSONString(articleRequestDTO), JSON.toJSONString(response));
if (!response.isSuccessful()) {
return null;
}
JueJinCreateResponse articleResponseDTO = response.body();
if (articleResponseDTO == null) {
return null;
}
ArticleFunctionResponse articleFunctionResponse = new ArticleFunctionResponse();
articleFunctionResponse.setCode(articleResponseDTO.getErr_no());
articleFunctionResponse.setMsg(articleResponseDTO.getErr_msg());
// 发送文章
JueJinPublishRequest jueJinPublishRequest = new JueJinPublishRequest();
jueJinPublishRequest.setDraft_id(articleResponseDTO.getData().getId());
Call<JueJinPublishResponse> jueJinPublishResponseCall = juejinService.publishArticle(juejinApiProperties.getCookie(), juejinApiProperties.getAuthorization(), jueJinPublishRequest);
Response<JueJinPublishResponse> jueJinPublishResponse = jueJinPublishResponseCall.execute();
if (!jueJinPublishResponse.isSuccessful()) {
return null;
}
JueJinPublishResponse jueJinPublishResponseBody = jueJinPublishResponse.body();
if (jueJinPublishResponseBody != null) {
articleFunctionResponse.setCode(jueJinPublishResponseBody.getErr_no());
articleFunctionResponse.setMsg(jueJinPublishResponseBody.getErr_msg());
}
return articleFunctionResponse;
}
最后对整合后的接口测试
@Test
public void testSaveArticle_domain() throws IOException {
String json = "{\"content\":\"<h2>场景:</h2>\\n<p>在某互联网大厂的面试室,一位严肃的面试官正准备提问,而对面坐着一位看似紧张却又想显得轻松的程序员小张。</p>\\n<p><strong>面试官</strong>:我们先来聊聊Java核心知识。第一个问题,Java中的JVM是如何管理内存的?</p>\\n<p><strong>程序员小张</strong>:哦,这个简单!JVM就像一个巨大的购物车,负责把所有的变量都放进去,呃……然后就……管理起来?</p>\\n<p><strong>面试官</strong>:嗯,第二个问题,请说说HashMap的工作原理。</p>\\n<p><strong>程序员小张</strong>:HashMap嘛,就是……呃,一个很大的箱子,大家都往里面扔东西,有时候会打架……</p>\\n<p><strong>面试官</strong>:那么第三个问题,能不能讲讲Spring和SpringBoot的区别?</p>\\n<p><strong>程序员小张</strong>:Spring是……呃,春天?SpringBoot就是穿靴子的春天嘛!哈哈……</p>\\n<p><strong>面试官</strong>:好,今天的问题就问到这里。回去等通知吧。</p>\\n<h2>答案解析:</h2>\\n<ol>\\n<li>\\n<p><strong>JVM内存管理</strong>:JVM内存管理包括堆内存和栈内存,堆内存用于存储对象实例,栈内存用于执行线程时的栈帧。</p>\\n</li>\\n<li>\\n<p><strong>HashMap原理</strong>:HashMap通过哈希函数将键映射到对应的值,并通过链表解决哈希冲突。</p>\\n</li>\\n<li>\\n<p><strong>Spring与SpringBoot区别</strong>:Spring是一个大型应用框架,而SpringBoot是基于Spring的快速开发套件,简化了Spring应用的配置。</p>\\n</li>\\n</ol>\\n\",\"cover_images\":[],\"cover_type\":0,\"description\":\"在互联网大厂的面试中,严肃的面试官与搞笑的程序员上演了一场精彩的对话。面试官提出Java核心知识、HashMap、Spring等问题,程序员则用幽默的方式作答。本文不仅展现了轻松的面试氛围,还附上了详细的技术问题答案解析,帮助读者更好地理解相关知识。\",\"is_new\":1,\"level\":\"0\",\"markdowncontent\":\"## 场景:\\n\\n在某互联网大厂的面试室,一位严肃的面试官正准备提问,而对面坐着一位看似紧张却又想显得轻松的程序员小张。\\n\\n**面试官**:我们先来聊聊Java核心知识。第一个问题,Java中的JVM是如何管理内存的?\\n\\n**程序员小张**:哦,这个简单!JVM就像一个巨大的购物车,负责把所有的变量都放进去,呃……然后就……管理起来?\\n\\n**面试官**:嗯,第二个问题,请说说HashMap的工作原理。\\n\\n**程序员小张**:HashMap嘛,就是……呃,一个很大的箱子,大家都往里面扔东西,有时候会打架……\\n\\n**面试官**:那么第三个问题,能不能讲讲Spring和SpringBoot的区别?\\n\\n**程序员小张**:Spring是……呃,春天?SpringBoot就是穿靴子的春天嘛!哈哈……\\n\\n**面试官**:好,今天的问题就问到这里。回去等通知吧。\\n\\n## 答案解析:\\n\\n1. **JVM内存管理**:JVM内存管理包括堆内存和栈内存,堆内存用于存储对象实例,栈内存用于执行线程时的栈帧。\\n\\n2. **HashMap原理**:HashMap通过哈希函数将键映射到对应的值,并通过链表解决哈希冲突。\\n\\n3. **Spring与SpringBoot区别**:Spring是一个大型应用框架,而SpringBoot是基于Spring的快速开发套件,简化了Spring应用的配置。\",\"not_auto_saved\":\"0\",\"pubStatus\":\"draft\",\"readType\":\"public\",\"resource_id\":\"\",\"resource_url\":\"\",\"source\":\"pc_mdeditor\",\"status\":0,\"sync_git_code\":0,\"tags\":\"Java,面试,互联网,程序员,Spring,SpringBoot,HashMap,JVM\",\"title\":\"互联网大厂Java面试:严肃面试官与搞笑程序员的对决\",\"vote_id\":0}";
ArticleFunctionRequest request = JSON.parseObject(json, ArticleFunctionRequest.class);
ArticleFunctionResponse response = jueJinArticleService.saveArticle(request);
log.info("测试结果:{}", JSON.toJSONString(response));
}
完美收工 ^_^