Поиск по этому блогу

пятница, 8 марта 2013 г.

Как подключить сервис отправки SMS сообщений на Java. Написание REST-запросов с помощью HttpsURLConnection, Apache Http Client и использование URLFetchService на Google App Engine

В работе над проектом очередным заданием было реализовать отправку смс сообщений на телефоны клиентов. Найти сервис для отправки смс сообщений было первой задачей. Был выбран сервис https://www.twilio.com/ В статье я раскажу о нескольких вариантах реализации взаимодействия с сервисом для отправки сообщений.

Для реализации взаимодействия с сервисом можно использовать следующие методы
1. HttpsURLConnection
2. Apache Http Client
3. URLFetchService
Сервис отправки смс-сообщений Twilio предоставляет REST API для взаимодействия.

https://www.twilio.com/sms/api

Это REST API кстати предоставлено для многих языков программирования. Также сущестуют разработанные и прдоставляемые сервисом Twilio библиотеки для разных языков программирования. Так что, задача отправки sms-сообщений может решаться совсем легко, достаточно подулючить библиотеку и отправлять запросы. Для начала работы с сервисом и получения триал аккаунта для проведения экспериментов нужно естественно зарегистрироваться.  Триал аккаунт позволит отправлять смс на свой номер. На сайте в личном кабинете можно посмотреть свои AccountSid и AuthToken. 

REST API для взаимодействия с сервисом Twilio

curl -X POST https://api.twilio.com/2010-04-01/Accounts/{AccountSid}/SMS/Messages \ 
--data-urlencode "To=+14155551212" \ 
--data-urlencode "From=+14158675309" \ 
--data-urlencode "Body=Hello world" \ 
-u {AccountSid}:{AuthToken}

Как взаимодействовать с сервисом, используя библиотеки предоставляемые сами Twilio, можно на сайте сервиса. В этой статье я расскажу о методах взаимодействия с сервисом без использования этих библиотек. Одна их причин - такие библиотеки не работают на Google App Engine (на нем не работает так же Apache Http Client).


Во всех вариантах мы работаем с одним и тем URL адресом на который шлем наши запросы

String url = "https://api.twilio.com/2010-04-01/Accounts/"
     + ACCOUNT_SID + "/SMS/Messages";

Используем  логин и пароль

  String accountSid = "{accountSid}";
  String authToken = "{authToken}";

Используем 3 основных параметра: номер отправителя, номер получателя и текст сообещния.

  String toPhoneNumber = "+7xxxxxxxxxx";
  String fromPhoneNumber = "+1xxxxxxxxxx";
  String body = "Hello World";

Зависимость Maven для подключения Base64 для кодирования данных.

<dependency>
 <groupId>commons-codec</groupId>
 <artifactId>commons-codec</artifactId>
 <version>1.7</version>
</dependency>

При помощи URLConnector


Взаимодействие с сервисом при помощи стандартных библиотек Java из пакеджа java.net и java.io. Нестандартная здесь только библиотека для кодирования значений Apache commons-codec.

Алгоритм

1. Кодируем логин и пароль
2. Кодируем значения параметров
3. Формируем тело запроса
4. Формируем URL адрес, по которому будем обращаться к серверу
5. Инициализируем объект класса HttpsURLConnection
6. Устанавливаем, что можно писать в выходной поток
7. Задаем, что запрос является POST-запросом
8. Задаем свойства запроса, в которых указываем какой тип данных мы будем передовать, кодировку и что будем авторизоваться
9. Пишем в выходной поток тело нашего запроса

С помощью библиотеки Apache commons-codec будем кодировать логин и пароль для того, чтобы их в закодированном виде передовать на сервер в заголовке запроса.

String encoding = Base64.encodeBase64String((nameAndPassword
   .getBytes("utf-8")));

HttpsURLConnection connection = (HttpsURLConnection) url
   .openConnection();
connection.setRequestProperty("Authorization", "Basic " + encoding);

Также кодируем значения параметров и формируем строку с этими значениями.

  String charset = "UTF-8";
  String toEncode = URLEncoder.encode(toPhoneNumber, charset);
  String fromEncode = URLEncoder.encode(fromPhoneNumber, charset);
  String bodyEncode = URLEncoder.encode(body, charset);
  String query = "To=" + toEncode + "&From=" + fromEncode + "&Body="
   + bodyEncode;
  connection.setRequestProperty("Content-Type",
   "application/x-www-form-urlencoded;charset=" + "utf-8");

Параметры запроса пишем на сервер с помощью выходного потока.

  OutputStream out = connection.getOutputStream();
  // пишем в выходной поток
  OutputStreamWriter writer = new OutputStreamWriter(out);
  writer.append(query);
  writer.flush();
  writer.close();
  out.close();

Полный листинг

package ru.ipoint.danilov;
 
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
 
import javax.net.ssl.HttpsURLConnection;
 
import org.apache.commons.codec.binary.Base64;
 
public class App {
    public static void main(String[] args) {
 
 try {
 
  // Данные для отправки SMS
  String toPhoneNumber = "+7xxxxxxxxxx";
  String fromPhoneNumber = "+1xxxxxxxxxx";
  String body = "Hello World";
 
  // Логин и пароль
  String accountSid = "{accountSid}";
  String authToken = "{authToken}";
  String nameAndPassword = accountSid + ":" + authToken;
  // кодируем логин и пароль
  String encoding = Base64.encodeBase64String((nameAndPassword
   .getBytes("utf-8")));
 
  String charset = "UTF-8";
  // кодируем значения параметров, которые будем передовать
  String toEncode = URLEncoder.encode(toPhoneNumber, charset);
  String fromEncode = URLEncoder.encode(fromPhoneNumber, charset);
  String bodyEncode = URLEncoder.encode(body, charset);
 
  // формируем строку с параметрами
  String query = "To=" + toEncode + "&From=" + fromEncode + "&Body="
   + bodyEncode;
 
  // создаем адрес по котору будем обращаться
  String adddr = "https://api.twilio.com/2010-04-01/Accounts/" 
                        + accountSid + "/SMS/Messages";
 
  URL url = new URL(adddr);
 
  // открываем подключение - кодированное https
  HttpsURLConnection connection = (HttpsURLConnection) url
   .openConnection();
 
  connection.setDoInput(true);
  connection.setDoOutput(true);
  connection.setRequestMethod("POST");
 
  connection.setRequestProperty("Authorization", "Basic " + encoding);
  connection.setRequestProperty("Accept-Charset", "utf-8");
  connection.setRequestProperty("Content-Type",
   "application/x-www-form-urlencoded;charset=" + "utf-8");
 
  // получаем выходной поток
  OutputStream out = connection.getOutputStream();
  // пишем в выходной поток
  OutputStreamWriter writer = new OutputStreamWriter(out);
  writer.append(query);
  writer.flush();
  writer.close();
  out.close();
 
  // получаем код ответа от сервера
  System.out.println(connection.getResponseCode());
 
 } catch (IOException e) {
  e.printStackTrace();
 }
 
    }
 
}

Реализация при помощи Apache Http Client


Для взаимодействия с сервисом Twilio в этот раз будем использовать Apache Http Client. Подключается эта библиотека при помощи следующий зависимости.

Зависимость Maven для подключения Apache Http Client.

<dependency>
 <groupId>org.apache.httpcomponents</groupId>
 <artifactId>httpclient</artifactId>
 <version>4.2.3</version>
</dependency>

Алгоритм

1. Формируем URL адрес, по которому будем обращаться к сервису
2. Инициализируем объект класса DefaultHttpClient
3. Инициализируем объект класса UsernamePasswordCredentials, в котором будут храниться логин и пароль
4. Создаем новый POST-запрос
5. Добавляем к созданному запросу заголовок, в который помещаем закодированные логин и пароль
6. Создаем список пар для хранения параметров
7. Создаем кодированную сущность для хранения параметров и передаем в нее список пар параметров
8. Задаем кодированной сущности тип данных и кодировку
9. Добавляем к запросу созданную сущность
10. Выполняем наш POST-запрос

При помощи Apache Http Client можно легко формировать и отправлять запросы на сервер. Создается клиент очень просто.

   DefaultHttpClient httpClient = new DefaultHttpClient();

Для хранения логина и пароля существует специальный класс UsernamePasswordCredentials

   UsernamePasswordCredentials credentials = 
   new UsernamePasswordCredentials(ACCOUNT_SID, AUTH_TOKEN);

POST-запрос к серверу создается также очень просто

   HttpPost postRequest = new HttpPost(url);

Для того, чтобы пройти аутентификацию на сервере необходимо добавить к заголовку запроса закодированные логи и пароль. В библиотеке Apache Http Client есть инструменты для кодирования.

   postRequest.addHeader(new BasicScheme().authenticate(credentials,
     postRequest, new BasicHttpContext()));

Параметры, которые мы будем передовать на сервер, добавим в список.

  List<NameValuePair> nvps = new ArrayList<NameValuePair>();
  nvps.add(new BasicNameValuePair("To", toNumber));
  nvps.add(new BasicNameValuePair("From", fromNumber));
  nvps.add(new BasicNameValuePair("Body", body));

Для отпправки в запросе параметров необходимо создать закодированную сущность, задать ей тип данных, которые она будет хранить и передать ей список пар (каждая пара - имя параметра и его значение). После чего нужно закодированную сущность прикрепить к запросу.

  AbstractHttpEntity entity = new UrlEncodedFormEntity(nvps,"utf-8");
  entity.setContentType
   ("application/x-www-form-urlencoded; charset=UTF-8");
  entity.setContentEncoding("UTF-8");
  postRequest.setEntity(entity);

В самом конце нам необходимо выполнить подготовленный запрос.

httpClient.execute(postRequest);

Полный листинг

package ru.ipoint.danilov;
 
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
 
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.auth.AuthenticationException;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.AbstractHttpEntity;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.BasicHttpContext;
 
public class App {
 
    // данные для авторизации на сервисе
    public static String ACCOUNT_SID = "{ACCOUNT_SID}";
 
    public static String AUTH_TOKEN = "{AUTH_TOKEN}";
 
    public static void main(String[] args) {
 
 try {
  // данные, которые будут использоваться для отправки SMS 
  String toNumber = "+7xxxxxxxxxx";
 
  String fromNumber = "+1xxxxxxxxxx";
 
  String body = "Hello World SMS";
 
  // URL адрес, по которому будем отправлять запросы
  String url = "https://api.twilio.com/2010-04-01/Accounts/"
     + ACCOUNT_SID + "/SMS/Messages";
 
  // Создаем http-клиента для взаимодействия с сервером
  DefaultHttpClient httpClient = new DefaultHttpClient();
 
  // Создаем пару имя пользователя и пароль
  UsernamePasswordCredentials credentials = 
   new UsernamePasswordCredentials(ACCOUNT_SID, AUTH_TOKEN);
 
  // Создаем новый POST-запрос на сервер
  HttpPost postRequest = new HttpPost(url);
 
  // добавляем к запросу заголовок с данными для авторизации 
  // но предварительно шифруем эти данные
  postRequest.addHeader(new BasicScheme().authenticate(credentials,
   postRequest, new BasicHttpContext()));
 
  // создаем список пар для хранения параметров
  // каждая пара - имя параметра и его значение
  List<NameValuePair> nvps = new ArrayList<NameValuePair>();
  nvps.add(new BasicNameValuePair("To", toNumber));
  nvps.add(new BasicNameValuePair("From", fromNumber));
  nvps.add(new BasicNameValuePair("Body", body));
  // создаем кодированную сущность 
  // и добавляем в неё данные с заданной кодировкой
  AbstractHttpEntity entity = new UrlEncodedFormEntity(nvps,"utf-8");
  // устанавливаем сущности тип данных, которые будем передовать
  entity.setContentType
   ("application/x-www-form-urlencoded; charset=UTF-8");
  entity.setContentEncoding("UTF-8");
 
  // добавляем к запросу параметры, 
  // которые будем передовать на сервер
  postRequest.setEntity(entity);
 
  // отправляем запрос на сервер и получаем ответ
  HttpResponse response = httpClient.execute(postRequest);
 
  // Выводим код статуса ответа от сервера
  System.out.println(response.getStatusLine().getStatusCode());
 
 } catch (IOException e) {
 
  e.printStackTrace();
 
 } catch (AuthenticationException e) {
 
  e.printStackTrace();
 }
 
    }
}

Реализация при помощи URLFetchService


Наше приложение работает на GAE (Google App Engine) и поэтому на нем невозможно использовать библиотеки Twilio для REST, а также Apache Http Client, так как они пытаются открывать свои порты для передачи запросов. Поэтому мы будем использовать URLFetchService


Алгоритм

1. Инициализируем объект класса URLFetchService
2. Формируем URL адрес, по которому будем обращаться к сервису
3. Создаем запрос к сервису
4. Кодируем имя пользователя и пароль
5. Добавляем к запросу заголовок с закодированным именем пользователя и паролем
6. Добавляем к запросу заголовок, в котором указываем тип данных, которые будем передавать на сервер
7. Формируем тело запроса, кодируем значения параметров
8. Добавляем тело запроса к запросу
9. Отправляем запрос на сервер

Для того, чтобы пройти аутентификацию на сервере необходимо в заголовке запроса к серверу отправить наши аутентификационные данные - это логин и пароль. Но не просто передать в заголовке логин и пароль в открытом виде, а зашифровать их  и уже в зашифрованном виде передовать.

Сначала шифруем аутентификационные данные

  String accountSid = "{accountSid}";
  String authToken = "{authToken}";
  String nameAndPassword = accountSid + ":" + authToken;
  byte[] b = Base64.encodeBase64(nameAndPassword.getBytes("utf-8"));
  String authorizationString = new String(b);

Потом добавляем зашифрованные данные в заголовок запроса.

HTTPRequest request = new HTTPRequest(url, HTTPMethod.POST);
request.addHeader(new HTTPHeader("Authorization", "Basic "
   + authorizationString));

Параметры запроса нужно отправлять тоже в зашифрованном виде. Причем шифровать нужно только значения параметров. Иначе запрос не будет правильно обрабатываться сервером.

Формируем значения параметров.

  String toPhoneNumber = recipientPhone;
  String fromPhoneNumber = "+16208429686";
  String body = message;

Кодируем значения параметров и формируем строку с параметрами для отправки.

StringBuilder sb = new StringBuilder();
sb.append("To=" + URLEncoder.encode(toPhoneNumber, "utf-8"));
sb.append("&From=" + URLEncoder.encode(fromPhoneNumber, "utf-8"));
sb.append("&Body=" + URLEncoder.encode(body, "utf-8"));

Наконец, добавляем к запросу параметры, которые нужно отправить.

request.setPayload(sb.toString().getBytes());

Полный листинг

package com.ipoint.trucking.web.server.utils;
 
import java.net.URL;
import java.net.URLEncoder;
import org.apache.commons.codec.binary.Base64;
import com.google.appengine.api.urlfetch.HTTPHeader;
import com.google.appengine.api.urlfetch.HTTPMethod;
import com.google.appengine.api.urlfetch.HTTPRequest;
import com.google.appengine.api.urlfetch.HTTPResponse;
import com.google.appengine.api.urlfetch.URLFetchService;
import com.google.appengine.api.urlfetch.URLFetchServiceFactory;
 
public class SmsUtils {
 
    private SmsUtils() {
 
    }
 
    /**
     * Sends sms message.
     * 
     * @param recipientPhone
     *            (e.g. +7904xxxxxxx)
     * @param message
     */
    public static void sendSms(String recipientPhone, String message) {
 try {
  String accountSid = "{accountSid}";
  String authToken = "{authToken}";
 
                // собираем SidId и authToken в одну строку для кодирования
                String nameAndPassword = accountSid + ":" + authToken;
 
                // получаем экземпляр urlFetchService
  URLFetchService urlFetchService = URLFetchServiceFactory
   .getURLFetchService();
 
                //Указываем URL адрес на который будет отправлять REST запрос 
  URL url = new URL("https://api.twilio.com/2010-04-01/Accounts/"
   + accountSid + "/SMS/Messages");
 
                // Создаем запрос и указываем его тип - POST 
  HTTPRequest request = new HTTPRequest(url, HTTPMethod.POST);
 
                // переводим строку с SidId и authToken в массив байт, 
                // после чего полученный массив кодируем
  byte[] b = Base64.encodeBase64(nameAndPassword.getBytes("utf-8"));
 
                // закодированный массив байт перводим в строку   
                String authorizationString = new String(b);
 
                // добавляем к запросу заголовок, в котором передаем данные
                // для аунтефикации
  request.addHeader(new HTTPHeader("Authorization", "Basic "
   + authorizationString));
 
                // добавляем к запросу заголовок, в котором указываем, 
                // что будем передовать закодированные данные
  request.addHeader(new HTTPHeader("Content-Type",
   "application/x-www-form-urlencoded; charset=UTF-8"));
 
                // инициализируем строки: номер, на который будем отправлять,
                // номер, с которого будем отправлять и текст сообщения
  String toPhoneNumber = recipientPhone;
  String fromPhoneNumber = "+16208429686";
  String body = message;
 
                // Создаем тело запроса, в которое добавляем все параметры
                // Очень важно тут заметить, что КОДИРОВАТЬ НУЖНО ТОЛЬКО 
                // ЗНАЧЕНИЯ ПАРАМЕТРОВ
                StringBuilder sb = new StringBuilder();
         sb.append("To=" + URLEncoder.encode(toPhoneNumber, "utf-8"));
         sb.append("&From=" + URLEncoder.encode(fromPhoneNumber, "utf-8"));
         sb.append("&Body=" + URLEncoder.encode(body, "utf-8"));
 
                // добавляем к запросу данные, которые нужно отправить на сервер
  request.setPayload(sb.toString().getBytes());
 
                // отправляем наш запрос на сервер, 
                // в ответ на запрос получаем ответ от сервера 
  HTTPResponse response = urlFetchService.fetch(request);
 
                // показываем код ответа от сервера 
  System.out.println("response code: " + response.getResponseCode()
                        + " content: " + new String(response.getContent()));
 } catch (Exception e) {
  System.out.println(e.getMessage());
 }
    }
}

Комментариев нет:

Отправить комментарий