Начал писать своё приложение для Яндекс Директа (я и программист, и занимаюсь интернет-маркетингом, используя Яндекс Директ). Устал вручную делать некоторые операции и решил вспомнить о том, что пишу на нескольких языках программирования. Так как задачка задумана как консольная, то писать её на PHP (который я не особо и люблю...) или Python, смысла большого не было. Консольная потому, что запускаться будет из crontab-а, то есть по времени/расписанию.
Уважаю и пишу на Java (ещё с версии 0.99 альфа - 95 год?... - не помню). Решил и эту задачку писать на Java. Не на C++ же её писать? Можно, конечно, и на C налабать, но... Java лучше.
Начал разбираться в документации Директа и обнаружил, что в разных документах (фи в сторону Яндекса!) иногда написано по-разному... Как выяснить что правильно? Нормальный программист скажет - тестировать!
Залез в Директ, зарегистрировал приложение, получил токен. Осталось тестировать, то есть писать и пробовать. Посмотрел документацию, что нужно знать? Ага! JSON, которым не пользовался. Запросы к HTTP/HTTPS серверу. Тоже особо не занимался (специфика больше к RDBMS). Посмотрел на стандартный набор - JSON отсутствует. Поискал, нашёл много и разных реализаций, но так как тяготею к стандартам (java.*, javax.*), то выбрал jsonp реализацию. По-крайней мере, не надо мозг ломать - javax.json.* структура классов. Вроде всё легко и просто. Разобрался как сделать с помощью Java нужную структуру JSON.
Вот текстовик, чем разбирался (JsonObjGen.java):
package test;
import java.io.*;
import java.util.*;
import javax.json.*;
public class JsonObjGen
{
public static void main(String v[])
{
for(String s:v)
{
new JsonObjGen(s);
}
}
public JsonObjGen(String s)
{
StringTokenizer st = null;
JsonObject jo = null;
JsonObjectBuilder jb = Json.createObjectBuilder();
jb = jb.add("var", Json.createArrayBuilder().addNull());
System.out.println(jb.build());
jb = Json.createObjectBuilder().add("a", "b");
jo = jb.build();
System.out.println(jo);
jb = Json.createObjectBuilder().add("obj",
Json.createObjectBuilder().add("inside",
Json.createArrayBuilder().addNull()));
jo = jb.build();
System.out.println(jo);
jb = Json.createObjectBuilder().add("obje",
Json.createObjectBuilder().add("invar",
Json.createArrayBuilder().add(
Json.createObjectBuilder().addNull("inset"))));
jo = jb.build();
System.out.println(jo);
jb = Json.createObjectBuilder().add("method", "get")
.add("params", Json.createObjectBuilder()
.add("SelectionCriteria", Json.createObjectBuilder())
.add("FieldNames", Json.createArrayBuilder()
.add("Id").add("Names")));
jo = jb.build();
System.out.println(jo);
}
}
Генерирует следующие объекты:
{"var":[null]}
{"a":"b"}
{"obj":{"inside":[null]}}
{"obje":{"invar":[{"inset":null}]}}
{"method":"get","params":{"SelectionCriteria":{},"FieldNames":["Id","Names"]}}
Пускается так: java test.JsonObjGen любой аргумент
После того как выяснил как создаются Json объекты, попытался залезть в Яндекс Директ тестовые кампании, которые уже были созданы. Не получилось. Проверил то же, но с помощью curl (на всех Linux-ах есть) - работает! Стал тестировать и пришёл к выводу, что http:// требует своего Output/InputStream. а https:// - своего. Потратил сутки на выяснение всяких деталей, но сейчас уже всё по полочкам разобрано и понятно.
Вот исходный текст (TestOAuthYaToken.java):
package test;
import givme.direct.*;
import java.io.*;
import java.net.*;
import java.util.*;
import javax.json.*;
import javax.net.ssl.*;
/*
* Написание этой тестовой задачки было продиктовано необходимостью
* проверить JsonReader.readObject() и Json.writeObject(JsonObject)
* при работе через http/https соединение.
*/
/*
* Запуск: java test.TestOAuthYaToken http/https://json.server.some.where/goal,
* где - goal - цель на json сервере.
* Я пускал для проверки так:
* java test.TestOAuthYaToken http://jsonplaceholder.typicode.com/posts
* или
* java test.TestOAuthYaToken https://api-sandbox.direct.yandex.ru/json/v5/campaigns
*/
/*
* Программка является тестовой, для проверки OAuth.yandex.ru токена.
* Который получался для приложения Яндекс Директа собственного написания.
* Токен получается браузером, как описано в документации на Яндекс Директ.
* По URL: https://oauth.yandex.ru/authorize?response_type=token&client_id=Id,
* где Id - идентификатор приложения для Яндекс Директ.
* Вид токена приблизительно: AgAAAAA0Vx_wAAX....
*
* Если response_type=token поменять на response_type=code, то получать будете код.
* Он, в отличие от токена, который живёт некоторое время. каждый раз разный.
* Вид приблизительно: 9433211.
*/
public class TestOAuthYaToken
{
public static void main(String v[])
{
for(String s:v)
{
reqResp(s);
}
/*
if(v.length == 0)
{
reqResp("https://"+POST.Id+":"+POST.Pass+"@"+POST.oauthServer+"/"+POST.requestCode+POST.Id);
}
*/
}
static void reqResp(String dst)
{
Base64.Encoder be = Base64.getEncoder();
HttpURLConnection hc = null;
HttpsURLConnection hs = null;
InputStream is = null;
JsonObject jo = null;
JsonObjectBuilder jb = Json.createObjectBuilder();
JsonReader jr = null;
JsonWriter jw = null;
OutputStream os = null;
URL url = null;
URLConnection uc = null;
try
{
url = new URL(dst);
System.out.println(url);
uc = url.openConnection();
uc.setRequestProperty("Authorization",
"Bearer " + POST.AuthT0);
uc.addRequestProperty("Content-Type", "application/json;charset=utf-8");
uc.setDoInput(true);
uc.setDoOutput(true);
/*
* Почему пришлось здесь сделать этот if(uc instanceof ...)?
* Всё просто! Так как делалось для того, чтобы проверить
* (были проблемы с отсылкой данных), то пришлось делить по протоколам. Это тестовая, но вполне
* работоспособная задача.
* В чём была проблема? Проблема была в том, что при использовании
* http:// нужно использовать ИМЕННО OutputStream от HttpURLConnection,
* а в случае httpS:// нужно использовать HttpsURLConnection. Иначе
* не работает этот самый Output/Input Streams. Эту проблему и решал.
*/
/*
* А проверял сначала на http://jsonplaceholder.typicode.com/posts, а он
* имеет http://, а не https:// протокол. Можно и https://, но там ciphers
* не совпадают с набором и у моей Java и handshake failed.
*/
if(uc instanceof HttpURLConnection)
{
hc = (HttpURLConnection)uc;
hc.setRequestMethod("POST");
}
else
{
hs = (HttpsURLConnection)uc;
hs.setRequestMethod("POST");
}
jb = Json.createObjectBuilder().add("method", "get")
.add("params", Json.createObjectBuilder()
.add("SelectionCriteria", Json.createObjectBuilder())
.add("FieldNames", Json.createArrayBuilder()
.add("Id").add("Name").add("DailyBudget").add("ClientInfo")
.add("Statistics")
));
jo = jb.build();
/*
* После использования JsonObjectBuilder функции build()
* JsonObjectBuilder обнуляется! И если нужен, то надо собирать
* его по-новой! Или (проще!) сохранить результат в JsonObject.
*/
uc.connect();
if(uc instanceof HttpURLConnection)
os = hc.getOutputStream();
else
os = hs.getOutputStream();
jw = Json.createWriter(os);
jw.writeObject(jo);
System.out.println(jo);
/*
* Открытие InputStream только после открытия OutputStream.
*/
if(uc instanceof HttpURLConnection)
is = hc.getInputStream();
else
is = hs.getInputStream();
jr = Json.createReader(is);
jo = jr.readObject();
System.out.println(uc.getHeaderFields());
System.out.println(jo);
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
Второй файл (POST.java):
package givme.direct;
public interface POST
{
public String method = "POST";
public String server = "api.direct.yandex.ru";
public String oauthServer = "oauth.yandex.ru";
public String sandboxServer = "api-sandbox.direct.yandex.ru";
public String contentType = "application/json;charset=utf-8";
public String Authorization = "Authorization: Bearer ";
public String ConTyJSON = "Content-Type: application/json;charset=utf-8";
public String JSON = "/json/v5/";
public String SOAP = "/v5/";
public String WSDL = "/v5/";
// Request Code/Token
public String requestCode = "authorize?response_type=code&client_id="; // client_id=Id
public String requestToken = "authorize?response_type=token&client_id="; // client_id=Id
// Token
public String tokenRequest = "https://oauth.yandex.ru/token";
public String tokenGrant = "grant_type=";
public String code = "code="; // code=Код Подтверждения из запроса токена
// https://oauth.yandex.ru/authorize?response_type=token&client_id=Id
public String clientId = "client_id="; // client_id=Id
public String clientSecret = "client_secret="; // client_secret=Pass
// Application
public String Id = "80e1......709bab";
public String Pass = "d9fa......e06f7";
// Мастер-токен.
public String Token = "ig6......J";
// Код подтвеждения. Каждый раз разный. Получается если
// в response_type ставить code: response_type=code.
public String Code0 = "9435595";
// Из URL по API. Это результат получения кода по URL выше.
// Получается одинаково с кодом, но response_type=token. Одинаков для логина и приложения.
// user шмузер имеет такой токен:
public String AuthT0 = "AgAAAAA0Vx_wAAX.........5o";
}
Эти два файла компилируются:
javac -d . POST.java TestOAuthYaToken.java
Потом запускаются:
java test.TestOAuthYaToken <json сервер>
В качестве серверов можно тестирования взять http://jsonplaceholder.typicode.com/posts
Для Яндекс Директ нужна тестовая Песочница:
https://api-sandbox.direct.yandex.ru/json/v5/campaigns
При общении с http://jsonplaceholder.typicode.com/posts выдаёт (часть вывода вырезана):
api-sandbox.direct.yandex.ru/json/v5/campaigns
http://jsonplaceholder.typicode.com/posts
{null=[HTTP/1.1 201 Created], CF-RAY=[50a6fff80fb2d729-FRA], Server=[cloudflare], X-Content-Type-Options=[nosniff], Connection=[keep-alive], Pragma=[n...............44:45 GMT; path=/; domain=.typicode.com; HttpOnly], Expires=[-1], Content-Length=[134], Location=[http://jsonplaceholder.typicode.com/posts/101], X-Powered-By=[Express], Content-Type=[application/json; charset=utf-8]}
{"method":"get","params":{"SelectionCriteria":{},"FieldNames":["Id","Names"]},"id":101}
При общении с тестовой Песочницей выдаёт (часть вывода опять вырезана):
https://api-sandbox.direct.yandex.ru/json/v5/campaigns
{"method":"get","params":{"SelectionCriteria":{},"FieldNames":["Id","Name","DailyBudget","ClientInfo","Statistics"]}}
{Transfer-Encoding=[chunked], null=[HTTP/1.1 200 OK], RequestId=[.......................................................Content-Security-Policy=[default-src 'none'], X-XSS-Protection=[1; mode=block], Content-Type=[application/json; charset=utf-8]}
{"result":{"Campaigns":[{"Statistics":{"Clicks":12...2,"Impressions":14...0},"ClientInfo":"..........","DailyBudget":null,"Id":33...6,"Name":"Test API Sandbox campaign 1"},{"ClientInfo":"..........","Statistics":{"Clicks":0,"Impressions":0},"Id":3...97,"Name":"Test API Sandbox campaign 2","DailyBudget":null},{"Id":3...98,"Name":"Test API Sandbox campaign 3","DailyBudget":null,"ClientInfo":"......","Statistics":{"Impressions":0,"Clicks":0}}]}}
Для чего я написал эту заметку и привёл исходники для Java? Всё просто - я не нашёл в Инете ни одного более-менее подробного исходника для Java реализации API Yandex Direct!!! Ни одного!!!
Даже для начала, для понимания как всё это нужно строить, и механизм работы нигде не объяснён. Потому взял на себя труд дать пройти кому-то другому этот этап легче. Теперь есть JSON, есть Http/HttpsURLConnection и видно как со всем этим работать.
P.S. Если у меня ещё возникнут подробные проблемы - вынесу сюда их решение. Ругайте, критикуйте - я не боюсь. Только учитывайте, что всё это тестовые куски, рабочие, но тем не менее - тестовые программы. Для того чтобы понять КАК работает Яндекс Директ API.