LoginSignup
18
21

More than 5 years have passed since last update.

HttpClientのREST通信サンプル

Last updated at Posted at 2014-08-30

ラジコン操作で作成したWEBサービスをバックエンドJAVAで連携する自作のサンプル。
操作にはBackbone.jsなどで直接REST実装してもよかったが、今後アームの取り付けなど拡張するためJAVAでAPI化した。
(GIT : https://github.com/tsunaki00/radiocontrol)
【イメージ】
rest.png

使用したライブラリ

・httpcore-4.2.2.jar
・httpclient-4.2.3.jar
・jsonic-1.2.11.jar
・s2-tiger-2.4.44.jar(汎用Beanで利用しているので、commons-beanutilsなどでもOK)

階層

 org.tsunaruts.radiocontrolfw
     └─ webservice
             ├ factory
             │  ├─ impl
             │  │    └─DefaultParserFactory.java
             │  └─ AbstractParserFactory.java
             ├ methods
             │    ├─ AbstractWebMethod.java
             │    ├─ GET.java
             │    └─ POST.java
             ├ parser
             │  ├─ impl
             │  │    └─JsonParser.java
             │  └─ AbstractParser.java
             └─ WebService.java

まず外部から呼び出し用のクラス
【WebService.java】

package org.tsunaruts.radiocontrolfw.webservice;

import java.io.IOException;
import java.text.ParseException;
import org.tsunaruts.radiocontrolfw.webservice.methods.GET;
import org.tsunaruts.radiocontrolfw.webservice.methods.POST;

/**
 * <pre>
 * WEBサービス用のメソッドを発行するクラス
 * ※※DIコンテナなどでURLを外部から設定する
 * </pre>
 */
public class WebService {
    /** URL */
    public String url;
    /**
     * 
     * @param url
     */
    public void initialize(String url) {
        this.url = url;
    }

    /**
     * WEBサービスを実行します 
     *  <GET メソッド>
     * @return 実行結果EntityList
     */
    public GET get(String path) {
        return new GET(url, path);
    }
    /**
     * WEBサービスを実行します 
     *  <POST メソッド>
     * @return 実行結果Entity
     * @throws IOException 
     * @throws ParseException 
     */
    public POST post(String path) {
        return new POST(url, path);
    }
}

次にWEBサービスに対応したメソッド
【AbstractWebMethod.java】

package org.tsunaruts.radiocontrolfw.webservice.methods;

import java.io.IOException;
import java.net.URISyntaxException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.seasar.framework.beans.util.BeanMap;
import org.seasar.framework.exception.IORuntimeException;
import org.seasar.framework.exception.ParseRuntimeException;
import org.tsunaruts.radiocontrolfw.webservice.factory.AbstractParserFactory;
import org.tsunaruts.radiocontrolfw.webservice.parser.AbstractParser;

/**
 * <pre>
 * WEBメソッドの抽象クラス
 * </pre>
 */
public abstract class AbstractWebMethod {
    /** WEBサービス提供ホスト名   http://localhost */
    protected String host;
    /** WEBサービス path   /action */
    protected String path;

    /**
     * コンストラクタ
     * @param host hostを設定します Examples : http://localhost
     * @param path pathを設定      Examples : /action
     */
    public AbstractWebMethod(String host, String path) {
        this.host = host;
        this.path = path;
    }

    /**
     * 子クラスでHttpUriRequestを生成
     * @param parameter リクエストパラメータ
     * @return HttpUriRequest
     * @throws URISyntaxException URLの組み立て失敗
     */
    protected abstract HttpUriRequest getHttpUriRequest(BeanMap parameter) throws URISyntaxException;
    /**
     * @see getStringBodyResult
     */
    public String getStringBodyResult(){
        return getStringBodyResult(null);
    }
    /**
     * RESTの戻り値を返します。
     * @param parameter リクエストパラメータ
     * @return RESTレスポンス
     */
    public String getStringBodyResult(BeanMap parameter) {
        try {
            HttpUriRequest request = getHttpUriRequest(parameter);
            List<String> list = execute(request, String.class, false);
            if (list != null && !list.isEmpty()) {
                return list.get(0);
            }
            return null;
        } catch (URISyntaxException e) {
            throw new RuntimeException(e);
        } catch (ParseException e) {
            throw new ParseRuntimeException(e);
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
    }
    public <T> T getSingleResult(Class<T> returnType) {
        return getSingleResult(returnType, null);
    }
    /**
     * RESTの戻り値を返します。
     * @param returnType 戻り値の型
     * @param parameter リクエストパラメータ
     * @return RESTレスポンス
     */
    public <T> T getSingleResult(Class<T> returnType, BeanMap parameter) {
        try {
            HttpUriRequest request = getHttpUriRequest(parameter);
            List<T> list = execute(request, returnType, false);
            if (list != null && !list.isEmpty()) {
                return list.get(0);
            }
            return null;
        } catch (URISyntaxException e) {
            throw new RuntimeException(e);
        } catch (ParseException e) {
            throw new ParseRuntimeException(e);
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
    }
    /**
     * 
     * @return
     */
    public <T> List<T> getResultList(Class<T> returnType) {
        return getResultList(returnType, null);
    }
    /**
     * RESTの戻り値をList返します。
     * @param returnType 戻り値の型
     * @param parameter リクエストパラメータ
     * @return List&lt;RESTレスポンス&gt;
     */
    public <T> List<T> getResultList(Class<T> returnType, BeanMap parameter) {
        try {
            HttpUriRequest request = getHttpUriRequest(parameter);
            List<T> list = execute(request, returnType, true);
            if (list != null && !list.isEmpty()) {
                return list;
            }
            return null;
        } catch (URISyntaxException e) {
            throw new RuntimeException(e);
        } catch (ParseException e) {
            throw new ParseRuntimeException(e);
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
    }
    /**
     * 外部WEBサービスにリクエストを発行
     * @param request リクエストオブジェクト
     * @param returnType 戻り値型
     * @param isList 戻り値の種類
     * @return RESTレスポンス
     * @throws ParseException レスポンスパースエラー
     * @throws IOException リクエスト処理時の例外
     */
    @SuppressWarnings("unchecked")
    private <T> List<T> execute(HttpUriRequest request, Class<T> returnType, boolean isList) throws ParseException, IOException {
        DefaultHttpClient httpClient = new DefaultHttpClient();
        HttpResponse response = httpClient.execute(request);
        HttpEntity httpEntity = response.getEntity();
        String body = EntityUtils.toString(httpEntity, "UTF-8");
        AbstractParser parser = AbstractParserFactory.getParserFactory().getParser(httpEntity.getContentType().getValue());
        if (returnType == String.class) {
            List<T> ret = new ArrayList<>();
            ret.add((T) body);
            return ret;
        }
        if (parser == null) {
            return null;
        }
        if (isList) {
            return parser.parseList(body, returnType);
        } else {
            List<T> ret = new ArrayList<>();
            ret.add(parser.parse(body, returnType));
            return ret;
        }
    }
    /**
     * @see AbstractWebMethod#uriBuild(BeanMap)
     * @return URIBuilder
     */
    protected URIBuilder uriBuild() {
        return uriBuild(null);
    }
    /**
     * URL組み立て
     * @param parameter リクエストパラメータ(GETのみ)
     * @return URIBuilder
     */
    protected URIBuilder uriBuild(BeanMap parameter) {
        URIBuilder builder = new URIBuilder();
        String scheme = "http://";
        if (host.indexOf("://") > -1) {
            scheme = host.substring(0, host.indexOf("://"));
            host   = host.substring(host.indexOf("://") + "://".length());
        }
        builder.setScheme(scheme);
        builder.setHost(host);
        builder.setPath(path);
        if (parameter != null) {
            Iterator<Map.Entry<String, Object>> ite = parameter.entrySet().iterator();
            while(ite.hasNext()) {
                Map.Entry<String, Object> entry = ite.next();
                builder.setParameter(entry.getKey(), (String) entry.getValue());
            }
        }
        return builder;
    }
}

各リクエストメソッド(とりあえずGETとPOST)
【GET.java】

package org.tsunaruts.radiocontrolfw.webservice.methods;

import java.net.URISyntaxException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.seasar.framework.beans.util.BeanMap;

/**
 * <pre>
 *  GETリクエストを実装する
 * </pre>
 */
public class GET extends AbstractWebMethod {

    public GET(String host, String path) {
        super(host, path);
    }

    /* (non-Javadoc) */
    @Override
    protected HttpUriRequest getHttpUriRequest(BeanMap parameter)
                                               throws URISyntaxException {
        return new HttpGet(uriBuild(parameter).build().toString());
    }
}

【POST.java】

package org.tsunaruts.radiocontrolfw.webservice.methods;

import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.seasar.framework.beans.util.BeanMap;

/**
 * <pre>
 *  POSTリクエストを実装する
 * </pre>
 */
public class POST  extends AbstractWebMethod {

    public POST(String host, String path) {
        super(host, path);
    }

    /* (non-Javadoc) */
    @Override
    protected HttpUriRequest getHttpUriRequest(BeanMap parameter)
                                               throws URISyntaxException {
        try {
            URIBuilder builder = uriBuild();
            List<NameValuePair> params = new ArrayList<NameValuePair>();
            if (parameter != null) {
                Iterator<Map.Entry<String, Object>> ite = parameter.entrySet().iterator();
                while(ite.hasNext()) {
                    Map.Entry<String, Object> entry = ite.next();
                    params.add(new BasicNameValuePair(entry.getKey(), (String) entry.getValue()));
                }
            }
            HttpPost post = new HttpPost(builder.build().toString());
            post.setEntity(new UrlEncodedFormEntity(params));
            return post;
        } catch (UnsupportedEncodingException e) {
            throw new URISyntaxException(parameter.toString(), host + path);
        }
    }
}

最後にレスポンス解析周りのクラス
SOAP対応を考えて、AbstractFactoryで実装

【AbstractParserFactory.java】

package org.tsunaruts.radiocontrolfw.webservice.factory;

import org.tsunaruts.radiocontrolfw.webservice.parser.AbstractParser;
import org.tsunaruts.radiocontrolfw.webservice.parser.impl.JsonParser;

/**
 * <pre>
 * contentTypeをみてParserを生成するFactoryクラス
 * </pre>
 */
public abstract class AbstractParserFactory {

    /**
     * 環境に合わせてFactoryクラスを生成する
     * @return AbstractParserFactory
     */
    public static AbstractParserFactory getParserFactory() {
        return new DefaultParserFactory();
    }

    /**
     * contentTypeをみてParserを生成します
     * @param contentType contentType
     * @return
     */
    public abstract AbstractParser getParser(String contentType);
}

package org.tsunaruts.radiocontrolfw.webservice.factory.impl;

import org.tsunaruts.radiocontrolfw.webservice.factory.AbstractParserFactory;
import org.tsunaruts.radiocontrolfw.webservice.parser.AbstractParser;
import org.tsunaruts.radiocontrolfw.webservice.parser.impl.JsonParser;

/**
 * <pre>
 *  デフォルトのFactoryクラス
 * </pre>
 *
 */
public class DefaultParserFactory extends AbstractParserFactory {

    /* (non-Javadoc) */
    @Override
    public AbstractParser getParser(String contentType) {
        // JSONの場合
        if (contentType != null && contentType.indexOf("application/json") > -1) {
            return new JsonParser();
        }
        // SOAP, HTML, TEXTの場合
        return null;
    }

}

【AbstractParser.java】

package org.tsunaruts.radiocontrolfw.webservice.parser;

import java.util.List;

/**
 * <pre>
 * レスポンスを解析クラス
 * </pre>
 */
public interface AbstractParser {
    /**
     * レスポンスを解析します
     * @param body レスポンスボディ
     * @param clazz 戻り
     * @return 取得結果
     */
    <T> T parse(String body, Class<T> clazz);
    /**
     * レスポンスを解析します
     * @param body レスポンスボディ
     * @param clazz 戻り
     * @return 取得結果
     */
    <T> List<T> parseList(String body, Class<T> clazz);

【JsonParser.java】

package org.tsunaruts.radiocontrolfw.webservice.parser.impl;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import net.arnx.jsonic.JSON;
import org.seasar.framework.beans.util.Beans;
import org.tsunaruts.radiocontrolfw.webservice.parser.AbstractParser;

/**
 * <pre>
 * JSONを解析するクラス
 * </pre>
 */
public class JsonParser implements AbstractParser {
    /* (non-Javadoc) */
    @Override
    public <T> T parse(String body, Class<T> clazz) {
        return JSON.decode(body, clazz);
    }

    /* (non-Javadoc) */
    @Override
    public <T> List<T> parseList(String body, Class<T> clazz) {
        List<T> ret = new ArrayList<>();
        @SuppressWarnings("unchecked")
        LinkedHashMap<String, Object>[] mapList = JSON.decode(body, LinkedHashMap[].class);
        if (mapList != null) {
            for (LinkedHashMap<String, Object> map : mapList) {
                ret.add(Beans.createAndCopy(clazz, map).execute());
            }
        }
        return ret;
    }
}

これで完了、リクエストパラメータをそのままJSONで返すACTIONを作成して単体テスト

実装のテスト

package org.tsunaruts.radiocontrolfw.webservice;

import java.util.List;
import org.seasar.framework.beans.util.BeanMap;

public class WebServiceTest {

    public static void main(String[] args) {
        WebService webService = new WebService();
        webService.initialize("http://localhost:8080/radiocontrol");
        BeanMap map = new BeanMap();
        map.put("param1", "test1");
        map.put("param2", "test2");
        List<BeanMap> list = webService.get("/api/control/front")
                                  .getResultList(BeanMap.class, map);
        System.out.println(list);
    }
}
[{param1=test1}, {param2=test2}]

無事に動いた。

【追記】
Stringでレスポンスを返せる修正とFactory周りがおかしかったので反映しました。

18
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
21