Developers Club geek daily blog

1 year, 5 months ago
Google Cloud Endpoints — is the superstructure over Google App Engine (GAE) for creation of API for web and mobile applications doing development it is simpler also the atorization including "from box" protection against DoS-attacks, OAuth 2.0, the web interface for testing API, SSL, atomatichesky scalability (the site will not fall under habra-effect), and also possibility of use of services available in Google App Engine (sending and reception of e-mail and XMPP messages, data loading from the Internet (URL Fetch service), tasks of the schedule (Task Queues and Scheduled Tasks), etc.)

GAE is free within initial quotas which allow to try and test service, and also to provide free functioning of the website which does not have heavy loads. At exhaustion of quotas service to become paid.

Idea of service that it does all or most part of work of the system administrator, plus some part of work of the programmer. This service can be interesting to startups as allows by small forces and in kotrotky terms to start detail design.

The framework of Objectify provides convenient stredstvo for work with embedded database in GAE, and the angular-google-gapi module for connection of web application on AngularJS with authorization of users.

Under cat there are a lot of pictures and text, it is also supposed that the reader is familiar with Java Servlets.

Registration


For a start it is required to create the project on GAE. For this purpose the accounting entry of google will be required. It is possible to use existing or to create the new. The policy of Google allows creation of several accounting entries for one person. Google warns when application requests data of the user (OAuth consent screen), among data on application information on the developer, including its e-mail address, and it can be transferred not any e-mail address, namely the accounting entry @ the same Google Chrome allows to be switched by gmail.com under which work with GAE so perhaps it is worth getting separate accounting entries for different projects, the benefit conveniently between them.

We pass into the console of the Google developer: console.developers.google.com also we log in using the selected accounting entry of Google, we click "Create an empty project", we select name of the project and ID project. The project will be available to the address http:// of {ID project} of .appspot.com

For example: hello-habrahabr-api.appspot.com

ID has to be unique. Thus if {ID project} @ gmail.com is occupied, this ID will be already noted as occupied" for GAE. That is on it to turn out to create {ID project} of .appspot.com and {ID project} @ gmail.com at the same time.

Google Cloud Endpoints on Java: Manual. p.1

After the project is created, it will be available in the developer's console:

Google Cloud Endpoints on Java: Manual. p.1

For this manual we will create two projects in the console of the Google developer: hello-habrahabr-api.appspot.com where there will be API (actually Google Cloud Endpoints), and hello-habrahabr-webapp.appspot.com on which we will place the web client. I do it for descriptive reasons divisions of fronend and backend, it would be possible to create on one domain.

In the menu we select APIs &auth;> Credentials> Add Credentials> OAuth 2.0 Client ID> Configure consent screen from the console of the developer of our Cloud Endpoints of the hello-habrahabr-api.appspot.com project.

Google Cloud Endpoints on Java: Manual. p.1

In tinctures the e-mail address of the developer is automatically inserted, also we enter application name (Product name) (surely), and (optionally) the web page address, logo and other data.

We select APIs &auth;> Credentials> Add Credentials> OAuth 2.0 Client ID from the menu and we specify type of application for which we create client ID. In this manual we will consider creation of web application therefore we select Web application, and we click "Create".

We specify:

— web application name (any)

— the domain(s) from which Javascript can address to API (Authorized JavaScript origins) it can be any domain. It is possible the same at which Cloud Endpoints works. Thus it is necessary to consider that Cloud Endpoints does not work at the user's domain, only at .appspot.com yet. But the web application which addresses to API can work at any domain.

— Authorized redirect URIs — by default we specify the same doment + / by oauth2callback: hello-habrahabr-webapp.appspot.com/oauth2callback

We click "Create", we receive "client ID" and "client secret" — they will be necessary for us for web application.

Preparation and setup of work environment.


It is required to us:

1. Java 7 (Java 8 is not supported by GAE yet)

Java installation: www.java.com/en/download/manual_java7.jsp

For Ubuntu/Debian:

sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update &&sudo apt-get install oracle-java7-installer

2. Maven

Installation: maven.apache.org/install.html

In Linux (latest version 3.3.3):

sudo mkdir /usr/local/apache-maven/
cd /usr/local/apache-maven &&sudo wget http://www.eu.apache.org/dist/maven/maven-3/3.3.3/binaries/apache-maven-3.3.3-bin.tar.gz &&sudo tar xvf apache-maven-3.3.3-bin.tar.gz
echo 'export M2_HOME=/usr/local/apache-maven/apache-maven-3.3.3' >> ~/.bashrc
source ~/.bashrc

For Ubuntu/Debian (in depositaries now version 3.0.5):

sudo apt-get install maven

We check the Maven and Java version:

maven -version

3. IDE or code editor — to taste. Eclipse and IntelliJ IDEA Ultimate have plug-ins for work with GAE, but testing offline for Cloud Endpoints all the same will not work at the machine (though in documentation it is specified that has to) therefore for division of code in development and the working system it is necessary or to use separate projects, or to use the work opportunity given to GAE with different versions of the project.

Instead of GAE plug-ins it is simpler and more convenient to Maven (imkho) to use in command line so IntelliJ IDEA Community Edtion quite will approach – there is built-in terminal, and it everything that is necessary. In Eclipse TM Terminal can be set.

4. Google App Engine SDK. If it is necessary — it is possible to download here.

mkdir ~/GAE-SDK &&cd ~/GAE-SDK
wget https://storage.googleapis.com/appengine-sdks/featured/appengine-java-sdk-1.9.27.zip &&unzip appengine-java-sdk-1.9.27.zip

But when using Maven, it will download Google App Engine SDK automatically.

We create backend basis


We pass into directory in which we will create project directory. Avoid directories with non-standard characters in the name and very long way, it can cause errors in Maven.

For creation of preparation of the project we use Maven and Endpoints Skeleton Archetype:

mvn archetype:generate -Dappengine-version=1.9.27 -Dfilter=com.google.appengine.archetypes:

We enter "2" (we select 2: remote -> com.google.appengine.archetypes:endpoints-skeleton-archetype (A skeleton project using Cloud Endpoints with Google App Engine Java)).

We select the version (by default — the last).

Define value for property 'groupId': — we enter groupId, it is unique identifier of our application, following Maven Guide to naming conventions on groupId, artifactId and version — as a rule is used domain name upside-down, but taking into account admissible characters of package name rules — that is in my case instead of com.appspot.hello-habrahabr-api it is necessary to enter com.appspot.hellohabrahabrapi or com.appspot.hello_habrahabr_api (I have selected the last).

Define value for property 'artifactId' — we enter short name of the project, for example hello-habrahabr-api (here the hyphen is allowed). .war (.jar) the file without version number will be called so (the file name will be hello-habrahabr-api-{version number} of .war).

Define value for property 'version' — we enter version number, for GAE number of versions has to be without points, for example not 1.0.3, but 1-0-3. It is connected with that on GAE versions of the project to the look address are available {version number}. {ID project} of .appspot.com.

Also in GAE the format {number instance} is used. { normer of the version }. {ID project} of .appspot.com — therefore it is recommended to begin name of the version with letter for example v-1-0 or ver-1-0 to distinguish from number of the started instance.

For this reason option the offered Maven by default — 1.0-SNAPSHOT — is better to replace the format suitable according to the GAE standards.

— usually we accept Define value for property 'package' offered by default (there will correspond groupId).

We confirm the selected settings: Y (yes, by default).

If everything is made correctly, we receive the message on successful assembly:

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 03:08 min
[INFO] Finished at: 2015-10-06T02:42:33+03:00
[INFO] Final Memory: 16M/108M
[INFO] ------------------------------------------------------------------------

Maven will create the folder which name there will correspond artifactId with files of the project. The folder has the following structure:

Google Cloud Endpoints on Java: Manual. p.1

pom.xml


For a start we should edit pom.xml which contains the Maven settings for our project. In code:

<properties>
    <app.id>your-app-id</app.id>
    <app.version>1</app.version>

</properties>

your-app-id it is necessary to replace on {ID project}, in my case it is hello-habrahabr-api (the note: if you do not see API in Services on... / _ ah/api/explorer — have perhaps forgotten to replace app.id) and ver-1-0 we bring into compliance with ver-1-0

In
<prerequisites>
    <maven>3.1.0</maven>
</prerequisites>

we register that Maven version which at us is set, in my case 3.1.0 I replace on 3.3.3.

src/main/webapp/WEB-INF/appengine-web.xml


In src/main/webapp/WEB-INF/appengine-web.xml:

<application>${app.id}</application>
<version>${app.version}</version>

{app.id} and {app.version} it is replaced respectively on {ID project} and the version (it is necessary to replace, automatically from pom.xml it will not take value).

We collect the project:

mvn clean install

If assembly was unsuccessful, we look for errors in settings. If — successful, we load the project on GAE:

mvn appengine:update

At the first start of Maven will download Google App Engine SDK, and will save in ~ / .m2/repository/com/google/appengine/appengine-java-sdk/ {version number} (on Linux).

Also at the first start from the console it will be necessary to log in in GAE, in window of the browser the dialog will open:

Google Cloud Endpoints on Java: Manual. p.1

When clicking the button, in new window to appear the code which needs to be copied and entered into consoles.

Tokens of user authentication are stored in ~ / .appcfg_oauth2_tokens_java. If in attempt of input you receive the message of 404 Not Found This application does not exist (app_id=u of' {your-app-ID}') — it is perhaps connected with that you have been logged in under other ekkaunt. Rename (or delete) this file and try again.

If the mvn appengine:update command is executed successfully ("BUILD SUCCESS") means initial settings are made successfully.

The bug when loading in version 1.9.21 which could be corrected as follows was observed: in pom.xml in all locally we replace appengine.version with appengine.sdk.version (see code .google.com/p/googleappengine/issues/detail?id=12009) In the current version (1.9.27) the bug is not shown, but the nobility just in case will not prevent.

In version 1.9.27 when loading it is possible to receive the message:

********************************************************
The API version in this SDK is no longer supported on the server!
-----------
Latest SDK:
Release: 1.9.27
Timestamp: Tue Aug 18 21:28:24 IDT 2015
API versions: [1]

-----------
Your SDK:
Release: 1.9.27
Timestamp: Wed Sep 02 23:29:33 IDT 2015
API versions: [1.0]

-----------
Please visit https://developers.google.com/appengine/downloads for the latest SDK.
********************************************************

Despite this warning, loading has to take place successfully.

We configure Git on GAE


On GAE there is opportunity to place free private Git repository for the code. The repository is only storage, the code from it does not deploitsya, on the server .war the file which has been built and loaded by means of Maven is executed.

At first we initialize git repository in project directory:

git init
git add src/ pom.xml README.md
git commit -a

We pass into the developer's console: console.developers.google.com/project, also we select our project.

We select from the menu: Source code> Browse. The site will suggest to create Git repository. We press "Get started"

Google Cloud Endpoints on Java: Manual. p.1

It is possible to copy code from the existing repository on Github or Bitbucket, to load (push) code from the kopyyuter, or to clone (clone) code of the project with GAE on the computer. Let's select "push". We receive the instruction:

1. To set Google Cloud SDK

Installation instructions. For Linux/Mac OS X:

curl https://sdk.cloud.google.com | bash # на вопросы можно принимать варианты предлагаемые по умолчанию
exec -l $SHELL
gcloud init # откроется окно браузера в котором надо залогиниться в учетную запись Google, потом в командной строке вводим имя нашего проекта

Google Cloud Endpoints on Java: Manual. p.1

In the future of setup it is possible to change team: gcloud config.

To show settings:
gcloud config list

To edit settings:
gcloud config set

To erase the Google Cloud SDK settings:
gcloud config unset

For example:

gcloud config unset account — to erase the accounting entry in settings

gcloud config unset project — to erase project name in settings

2. We make authentication

On Linux/Mac OS X:
gcloud auth login # откроется окно браузера в котором надо залогиниться в учетную запись Google
git config credential.helper gcloud.sh

3. We add GAE repository deleted with git

git remote add google source.developers.google.com/p { ID of the project } /

git remote add google https://source.developers.google.com/p/hello-habrahabr-api/

4. We unload in remote repository

git push --all google

To clone repository from GAE cloud on the local machine, it will be required to us:

To set Google Cloud SDK as it is stated above.

To make authentication:

gcloud auth login

To clone repository on the local machine (the instruction on the site offering for this gcloud init { ID of the project } — has become outdated):

gcloud source repos clone default {ID проекта}

We pass into the created directory, we write also kommity code in local repository, and

git push -u origin master

For atomatization of work it is possible to use the following script (something like commit.push.build.and.upload.sh):

mvn clean
git add * # предполагает наличие .gitignore
git commit -a
git push -u google
#
mvn install
mvn appengine:update

If it is necessary to switch between projects, in directory of the project it is worth placing script for fast and convenient change of settings (set.account.sh):

gcloud config set account "{учетная запись @mgmail.com}"
gcloud config set project "{проект ID}"
gcloud config list
rm ~/.appcfg_oauth2_tokens_java

We start writing of API


We start the favourite editor or IDE.

For IntelliJ IDEA:

For work with GAE we need version 3.1.0 Maven above, in IntelliJ IDEA now by default 3.0.5. If necessary we change, specifying directory in which we have set the latest version of Maven, for example of/usr/local/apache-maven/apache-maven-3.3.3 (it is possible to check team: $M2_HOME echo).

inport project-> we specify project directory-> Import project from external model-> Maven-> it is possible to leave default settings-> Import Project-> we specify SDK (Java 1.7)-> Finish.

First of all we will consider as looks WEB INF/web.xml. In our case:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<web-app
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.5"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

    <servlet>
        <servlet-name>SystemServiceServlet</servlet-name>
        <servlet-class>com.google.api.server.spi.SystemServiceServlet</servlet-class>
        <init-param>
            <param-name>services</param-name>
            <param-value>com.appspot.hello_habrahabr_api.YourFirstAPI</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>SystemServiceServlet</servlet-name>
        <url-pattern>/_ah/spi/*</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>

</web-app>

Let's pay attention that all requests to API are sent to the address: / _ah/spi / * are also processed by servlet of com .google.api.server.spi.SystemServiceServlet (SystemServiceServlet).

One of main "counters" of Cloud Endpoints — the web interface for testing of API (API Explorer) dostutpen to the address {ID project} of .appspot.com / _ ah/api/explorer.

For data modeling of the accepted and given out API JavaBean, i.e. the classes meeting the requirements are used:

* public (public) the designer without parameters. In this case the designer has to be specified in an explicit form though in examples on the site Google it is missed, but without designer at practice does not work.

* all properties of class private, access through get/set (for boolean getter also has to begin with get, but not as will generate IDE).

* the class has to be serializable (in an explicit form it is possible not to specify).

Let's create two classes:

UserForm.java:

package com.appspot.hello_habrahabr_api;

public class UserForm {

    private String name;
    private int age;
    private boolean ishuman;

    public UserForm() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public boolean getIshuman() {
        return ishuman;
    }
    // not just ishuman() for boolean
    // as will be created using getters and setters generation
    // in IntelliJ IDEA and Eclipse

    public void setIshuman(boolean ishuman) {
        this.ishuman = ishuman;
    }
}

And MessageToUser.java:

package com.appspot.hello_habrahabr_api;

public class MessageToUser {

    private String name;
    private String message;
    private int usernumber;
    private boolean isregistered;

    public MessageToUser() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public int getUsernumber() {
        return usernumber;
    }

    public void setUsernumber(int usernumber) {
        this.usernumber = usernumber;
    }

    public boolean getIsregistered() {
        return isregistered;
    }
    // not "isregistered()" as suggested by IDE's getter/setter generator

    public void setIsregistered(boolean isregistered) {
        this.isregistered = isregistered;
    }
}


Now we write class for the first API method. We edit YourFirstApi.java, and we will insert the following code there:

package com.appspot.hello_habrahabr_api;

import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import com.google.api.server.spi.config.ApiMethod.HttpMethod;

@Api(
        name = "myApi",
        version = "v1",
        scopes = {Constants.EMAIL_SCOPE},
        description = "first API for this application."
)

public class YourFirstAPI {

    @ApiMethod(
            name = "register",
            path = "register",
            httpMethod = HttpMethod.POST
    )

    @SuppressWarnings("unused")

    public MessageToUser userInfo(
            final UserForm userForm
    ) {

        MessageToUser messageToUser = new MessageToUser();

        messageToUser.setMessage("Hi, " + userForm.getName() + ", you are registered on our site");

        messageToUser.setUsernumber(1);

        messageToUser.setIsregistered(true);

        return messageToUser;
    }
}

The web interface to API (APIs Explorer)



Now deploy (mvn clean install &&mvn; appengine:update) also we open in the web browser the address https://{ the ID project } .appspot.com / _ ah/api/explorer, in my case of hello-habrahabr-api.appspot.com / _ ah/api/explorer.

Google Cloud Endpoints on Java: Manual. p.1

We click on the name of our API (if we create some classes in the summary of Api — from will be a little) and we see the methods containing in this API (class methods with @ApiMethod anntotation):

Google Cloud Endpoints on Java: Manual. p.1

Having clicked across the field of "Request body" we can fill in data of the request received by api-method:

Google Cloud Endpoints on Java: Manual. p.1

Further we can select "Autorize and execute" — and then we will need to pass authorization ipolzuya the accounting entry of Google (@ gmail.com) or to select "Execute without OAuth" as our API so far does not use authorization in any way, we will see identical results with authorization and without:

Google Cloud Endpoints on Java: Manual. p.1

"-Show headers -" in Response is clickable.

Logging


Log are available to the address: console.developers.google.com/project/of {ID project}/logs.

Settings in file/src/main/webapp/WEB-INF/logging.properties and / src/main/webapp/WEB-INF/appengine-web.xml.

In order that our class gave reports in log, it is necessary the class API:

1.

import java.util.logging.Logger;

2.

@SuppressWarnings("unused")
private static final Logger LOG = Logger.getLogger({имя класса}.class.getName());


3. We will add to method:

LOG.info("сообщение");

Google Cloud Endpoints on Java: Manual. p.1

Authorization of OAuth 2.0 using the accounting entry of Google (@ gmail.com)


We will add to Constants.java:

import com.google.api.server.spi.Constant

and

public static final String API_EXPLORER_CLIENT_ID = Constant.API_EXPLORER_CLIENT_ID

It is necessary for testing of the OAuth-protected API methods in APIs Explorer.

Our Constants.java will look as follows:

package com.appspot.hello_habrahabr_api;

import com.google.api.server.spi.Constant;

/**
 * Contains the client IDs and scopes for allowed clients consuming your API.
 */
public class Constants {
    public static final String WEB_CLIENT_ID = "replace this with your web client ID";
    public static final String ANDROID_CLIENT_ID = "replace this with your Android client ID";
    public static final String IOS_CLIENT_ID = "replace this with your iOS client ID";
    public static final String ANDROID_AUDIENCE = WEB_CLIENT_ID;
    public static final String EMAIL_SCOPE = "https://www.googleapis.com/auth/userinfo.email";

    public static final String API_EXPLORER_CLIENT_ID = Constant.API_EXPLORER_CLIENT_ID;
}

Now we will create new class:


package com.appspot.hello_habrahabr_api;

import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import com.google.api.server.spi.config.ApiMethod.HttpMethod;
import com.google.api.server.spi.response.UnauthorizedException;
import com.google.appengine.api.users.User;
// https://cloud.google.com/appengine/docs/java/javadoc/index?com/google/appengine/api/users/User.html

import java.util.logging.Logger;

@Api(
        name = "oAuth2Api", // The api name must match '[a-z]+[A-Za-z0-9]*'
        version = "v1",
        description = "API using OAuth2"
)
public class OAuth2Api {


    @SuppressWarnings("unused")
    private static final Logger LOG = Logger.getLogger(OAuth2Api.class.getName());

    @ApiMethod(
            name = "getUserInfo",
            path = "getuserinfo",
            httpMethod = HttpMethod.POST
    )
    @SuppressWarnings("unused")
    public User getUserInfo(User user) throws UnauthorizedException {

        if (user == null) {
            LOG.warning("[warning] User not logged in");
            throw new UnauthorizedException("Authorization required");
        }

        return user;
    }
}

Also we will register it in SystemServiceServlet servlet init-param in web.xml:

    <servlet>
        <servlet-name>SystemServiceServlet</servlet-name>
        <servlet-class>com.google.api.server.spi.SystemServiceServlet</servlet-class>
        <init-param>
            <param-name>services</param-name>
            <param-value>
                com.appspot.hello_habrahabr_api.YourFirstAPI,
                com.appspot.hello_habrahabr_api.OAuth2Api
            </param-value>
        </init-param>
    </servlet>

Deploim the project, also we watch API Explorer:

Google Cloud Endpoints on Java: Manual. p.1

We see new API in the list, having clicked it we see the list of its methods.

We click on the name of method:

Google Cloud Endpoints on Java: Manual. p.1

Now, if we click "Execute without OAuth" we will receive Exception:

Google Cloud Endpoints on Java: Manual. p.1

If we click "Autorize and execute" — it is necessary to log in using the accounting entry of Google. In Response respectively we will receive email, nickname and userId (unique number of the user of Google).

The object of the class com.google.appengine.api.users.User is provided to GAE and contains information on the current user if the user is not authorized, according to null. Thus we can carry out authorization using login password of the accounting entry of Google.

How it was already mentioned on Habré (Sometimes better less — why only Google-authorization?, the Usability of forms of authorization) the project in general can do without own processing of logins and passwords.

In my opinion, it is the correct approach, first of all from the point of view of safety. Naturally, we can do "registration" on the site after input of additional information, payment, etc.

We will consider creation of frontend on AngularJS in the following article.

This article is a translation of the original post at habrahabr.ru/post/268863/
If you have any questions regarding the material covered in the article above, please, contact the original author of the post.
If you have any complaints about this article or you want this article to be deleted, please, drop an email here: sysmagazine.com@gmail.com.

We believe that the knowledge, which is available at the most popular Russian IT blog habrahabr.ru, should be accessed by everyone, even though it is poorly translated.
Shared knowledge makes the world better.
Best wishes.

comments powered by Disqus