1. 概述
在本文檔系列中的第二篇文章中,我們將構建一些簡單的功能,通過應用程序向 Reddit 發帖,利用其 API。
2. 必要安全措施
首先,我們來處理安全方面。
為了 提交鏈接到Reddit,我們需要定義一個 OAuth 保護的資源,其 範圍 為“submit”:
@Bean
public OAuth2ProtectedResourceDetails reddit() {
AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
details.setId("reddit");
details.setClientId(clientID);
details.setClientSecret(clientSecret);
details.setAccessTokenUri(accessTokenUri);
details.setUserAuthorizationUri(userAuthorizationUri);
details.setTokenName("oauth_token");
details.setScope(Arrays.asList("identity", "submit"));
details.setGrantType("authorization_code");
return details;
}
請注意,我們還指定了 範圍 “identity”,因為我們也需要訪問用户帳户信息。
3. 是否需要驗證碼?
新用户在 Reddit 上提交內容時 必須填寫驗證碼,在達到 Reddit 內一定活躍度閾值之前。
對於這些用户,我們首先需要檢查是否需要驗證碼:
private String needsCaptcha() {
String result = redditRestTemplate.getForObject(
"https://oauth.reddit.com/api/needs_captcha.json", String.class);
return result;
}
private String getNewCaptcha() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity req = new HttpEntity(headers);
Map<String, String> param = new HashMap<String, String>();
param.put("api_type", "json");
ResponseEntity<String> result = redditRestTemplate.postForEntity(
"https://oauth.reddit.com/api/new_captcha", req, String.class, param);
String[] split = result.getBody().split(""");
return split[split.length - 2];
}
4. “<em>提交帖子</em>” 表單
接下來,讓我們創建用於向 Reddit 提交新帖子的主要表單。提交鏈接需要以下詳細信息:
- 標題 – 文章標題
- URL – 文章的 URL
- 子版塊 – 提交鏈接的子版塊
讓我們看看如何顯示這個簡單的提交頁面:
@RequestMapping("/post")
public String showSubmissionForm(Model model) throws JsonProcessingException, IOException {
String needsCaptchaResult = needsCaptcha();
if (needsCaptchaResult.equalsIgnoreCase("true")) {
String iden = getNewCaptcha();
model.addAttribute("iden", iden);
}
return "submissionForm";
}
當然,基本的 submissionForm.html:
<form>
<input name="title"/>
<input name="url" />
<input name="sr"/>
<input type="checkbox" name="sendReplies" value="true"/>
<div th:if="${iden != null}">
<input type="hidden" name="iden" value="${iden}"/>
<input name="captcha"/>
<img src="http://www.reddit.com/captcha/${iden}" alt="captcha" width="200"/>
</div>
<button type="submit" onclick="submitPost()">Post</button>
</form>
<script>
function submitPost(){
var data = {};
$('form').serializeArray().map(function(x){data[x.name] = x.value;});
$.ajax({
url: "api/posts",
data: JSON.stringify(data),
type: 'POST',
contentType:'application/json'
}).done(function(data) {
if(data.length < 2){ alert(data[0]);}
else{
window.location.href="submissionResponse?msg="+
data[0]+"&url="+data[1];
}
}).fail(function(error) { alert(error.responseText); });
}
</script>
submissionForm 的參數向 Reddit 發送 POST 請求。
@Controller
@RequestMapping(value = "/api/posts")
public class RedditPostRestController {
@Autowired
private RedditService service;
@RequestMapping(method = RequestMethod.POST)
@ResponseBody
public List<String> submit(@Valid @RequestBody PostDto postDto) {
return service.submitPost(postDto);
}
}
以下是實際的方法實現:
public List<String> submitPost(PostDto postDto) {
MultiValueMap<String, String> param1 = constructParams(postDto);
JsonNode node = redditTemplate.submitPost(param1);
return parseResponse(node);
}
private MultiValueMap<String, String> constructParams(PostDto postDto) {
MultiValueMap<String, String> param = new LinkedMultiValueMap<String, String>();
param.add("title", postDto.getTitle());
param.add("sr", postDto.getSubreddit());
param.add("url", postDto.getUrl());
param.add("iden", postDto.getIden());
param.add("captcha", postDto.getCaptcha());
if (postDto.isSendReplies()) {
param.add("sendReplies", "true");
}
param.add("api_type", "json");
param.add("kind", "link");
param.add("resubmit", "true");
param.add("then", "comments");
return param;
}
以下是簡單的解析邏輯,處理來自 Reddit API 的 response:
private List<String> parseResponse(JsonNode node) {
String result = "";
JsonNode errorNode = node.get("json").get("errors").get(0);
if (errorNode != null) {
for (JsonNode child : errorNode) {
result = result + child.toString().replaceAll("\"|null", "") + "<br>";
}
return Arrays.asList(result);
} else {
if ((node.get("json").get("data") != null) &&
(node.get("json").get("data").get("url") != null)) {
return Arrays.asList("Post submitted successfully",
node.get("json").get("data").get("url").asText());
} else {
return Arrays.asList("Error Occurred while parsing Response");
}
}
}
所有這些都與一個基本的 DTO 一起工作:
public class PostDto {
@NotNull
private String title;
@NotNull
private String url;
@NotNull
private String subreddit;
private boolean sendReplies;
private String iden;
private String captcha;
}
最後 – submissionResponse.html:
<html>
<body>
<h1 th:text="${msg}">Hello</h1>
<h1 th:if="${param.containsKey('msg')}" th:text="${param.msg[0]}">Hello</h1>
<h2 th:if="${param.containsKey('url')}"><a th:href="${param.url[0]}">Here</a></h2>
</body>
</html>
6. 結論
在本快速教程中,我們實現了基本的 提交至 Reddit功能——雖然簡單但完全可用。
在隨後的案例研究中,我們將實現 安排稍後發佈功能到我們的應用程序中。
@Controller
@RequestMapping(value = "/api/posts")
public class RedditPostRestController {
@Autowired
private RedditService service;
@RequestMapping(method = RequestMethod.POST)
@ResponseBody
public List<String> submit(@Valid @RequestBody PostDto postDto) {
return service.submitPost(postDto);
}
}public List<String> submitPost(PostDto postDto) {
MultiValueMap<String, String> param1 = constructParams(postDto);
JsonNode node = redditTemplate.submitPost(param1);
return parseResponse(node);
}
private MultiValueMap<String, String> constructParams(PostDto postDto) {
MultiValueMap<String, String> param = new LinkedMultiValueMap<String, String>();
param.add("title", postDto.getTitle());
param.add("sr", postDto.getSubreddit());
param.add("url", postDto.getUrl());
param.add("iden", postDto.getIden());
param.add("captcha", postDto.getCaptcha());
if (postDto.isSendReplies()) {
param.add("sendReplies", "true");
}
param.add("api_type", "json");
param.add("kind", "link");
param.add("resubmit", "true");
param.add("then", "comments");
return param;
}private List<String> parseResponse(JsonNode node) {
String result = "";
JsonNode errorNode = node.get("json").get("errors").get(0);
if (errorNode != null) {
for (JsonNode child : errorNode) {
result = result + child.toString().replaceAll("\"|null", "") + "<br>";
}
return Arrays.asList(result);
} else {
if ((node.get("json").get("data") != null) &&
(node.get("json").get("data").get("url") != null)) {
return Arrays.asList("Post submitted successfully",
node.get("json").get("data").get("url").asText());
} else {
return Arrays.asList("Error Occurred while parsing Response");
}
}
}public class PostDto {
@NotNull
private String title;
@NotNull
private String url;
@NotNull
private String subreddit;
private boolean sendReplies;
private String iden;
private String captcha;
}<html>
<body>
<h1 th:text="${msg}">Hello</h1>
<h1 th:if="${param.containsKey('msg')}" th:text="${param.msg[0]}">Hello</h1>
<h2 th:if="${param.containsKey('url')}"><a th:href="${param.url[0]}">Here</a></h2>
</body>
</html>