Reddit APIへのリンクを投稿する

1概要

この連載第2回の記事:/case-study-a-reddit-app-with-spring[シリーズ]では、アプリケーションからRedditに投稿するための簡単な機能をAPIを介して作成します。

** 2必要なセキュリティ

まず、セキュリティの問題を解決しましょう。

  • Redditへのリンクを** 送信するためには、 Scope が " submit "のOAuth保護リソースを定義する必要があります。

@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;
}

ユーザーアカウント情報にもアクセスする必要があるため、 scope identity ”も指定しています。

3キャプチャは必要ですか?

Reddit を初めて使用するユーザーは、送信するためにCaptcha を入力する必要があります。それは彼らがReddit内で特定のカルマしきい値を通過する前です。

これらのユーザーの場合、最初にCaptchaが必要かどうか確認する必要があります。

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 “ 投稿投稿 ”フォーム

次に、Redditに新しい投稿を送信するためのメインフォームを作成しましょう。

リンクを送信するには、以下の詳細が必要です。

  • title - 記事のタイトル

  • url - 記事のURL

  • subreddit - リンクを送信するサブ赤字

それでは、この簡単な送信ページを表示する方法を見てみましょう。

@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>

それでは最後のステップ、Reddit APIを介して実際のリンクを送信することを見てみましょう。

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からの応答を処理する** 簡単な解析ロジックです。

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. 結論

このクイックチュートリアルでは、基本的な サブミットto Reddit 機能を実装しました - 単純だが完全に機能的です。

このケーススタディの次の部分では、[後で投稿する予定の投稿]機能をアプリに実装します。

このチュートリアルの 完全な実装 はhttps://github.com/eugenp/reddit-app/[githubプロジェクト]にあります - これはEclipseベースのプロジェクトなので、インポートして実行するのは簡単なはずです。です。