Building NodeJS REST service and consuming it from within Android application
Introduction
In this tutorial I will walk you through the process of building REST service in NodeJS. That service will enable Android users to authenticate to the back-end and set their profile photo.Using the code
The application consist of two parts:- Backend: RESTful service written in NodeJS
- Mobile: Android app written in Java
Part 1. - NodeJS back-end
Back-end part will enable users to:- Login using email/password combination
- Upload their profile picture
app.js
var express = require('express');
var http = require('http');
var path = require('path');
var app = express();
var util = require('util');
var multiparty = require('multiparty');
var multer = require('multer');
var upload = multer({ dest: './uploads/' });
app.use(multer({ dest: './uploads/' }));
var fs = require('fs');
var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// all environments
app.set('port', process.env.PORT || 3000);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.json());
app.use(express.urlencoded());
app.use(express.methodOverride());
app.use(app.router);
app.use(require('stylus').middleware(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'public')));
app.use('/uploads', express.static(__dirname + '/uploads'));
app.use('/uploads', express.directory(__dirname + '/uploadsimages'));
// development only test
if ('development' == app.get('env')) {
app.use(express.errorHandler());
}
var testEmail = "test@email.com";
var testPassword = "123456";
var testuserid = "my_userid";
app.get('/', function (req,res) {
res.send("BCG REST service v2 by Jalle");
});
var type = upload.single('image');
//working
app.post('/sessions/new', function (req, res) {
var email = req.body.email;
var password = req.body.password;
if (email == testEmail && password == testPassword) {
res.json({ userid: "my_userid", token: "token" });
} else {
res.json({ userid: "", token: "" });
}
});
//working
app.get('/users/:userid', function (req, res) {
var userid = req.params.userid;
if (userid == testuserid ) {
res.json({ email: testEmail, avatar_url: "uploads/pic.jpg" });
} else {
res.json({ email: "", avatar_url: "" });
}
});
//upload avatar image
app.post('/users/:userid/avatar/:avatar', type,
function(req, res) {
var userid = req.params.userid;
if (userid == testuserid || true) {
var tmp_path = req.file.path;
/** The original name of the uploaded file stored in the variable "originalname". **/
var target_path = 'uploads/' + req.file.originalname;
/** A better way to copy the uploaded file. **/
var src = fs.createReadStream(tmp_path);
var dest = fs.createWriteStream(target_path);
src.pipe(dest);
src.on('end', function () { res.end('file uploaded'); });
src.on('error', function (err) { res.end('error'); });
res.json({ avatar_url: target_path });
} else {
res.json({ avatar_url: "" });
}
});
//File upload test
app.get('/upload', function (req, res) {
res.render('upload', {
title: 'Upload Images'
});
});
app.post('/upload', type, function (req, res, next) {
/** When using the "single" data come in "req.file" regardless of the attribute "name". **/
var tmp_path = req.file.path;
/** The original name of the uploaded file stored in the variable "originalname". **/
var target_path = 'uploads/' + req.file.originalname;
/** A better way to copy the uploaded file. **/
var src = fs.createReadStream(tmp_path);
var dest = fs.createWriteStream(target_path);
src.pipe(dest);
src.on('end', function () { res.end('file uploaded'); });
src.on('error', function (err) { res.end('error'); });
});
http.createServer(app).listen(app.get('port'), function () {
console.log('Express server listening on port ' + app.get('port'));
});
Now we can test the backend by using Postman. Use test account (credentials are in the code) to login and submit profile picture.
Source code available at Github
Part 2. - Android application
Mobile app will contain these classes
- LoginActivity
- MainActivity
- APIInterface
LoginActivity
package com.bcg.loginexample;
/**
* A login screen that offers login via email/password.
*/
public class LoginActivity extends AppCompatActivity {
public Settings settings;
public static final String BASE_URL = ApiInterface.ENDPOINT;
// UI references.
private EditText mEmailView;
private EditText mPasswordView;
private TextView failedLoginMessage;
View focusView = null;
private String email;
private String password;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
settings =new Settings(getApplicationContext());
settings = settings.getSettings();
// settings.saveSettings("","","","");
// Set up the login form.
mEmailView = (EditText) findViewById(R.id.email);
failedLoginMessage = (TextView)findViewById(R.id.failed_login);
mPasswordView = (EditText) findViewById(R.id.password);
mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
if (id == R.id.login || id == EditorInfo.IME_NULL) {
attemptLogin();
return true;
}
return false;
}
});
Button mEmailSignInButton = (Button) findViewById(R.id.email_sign_in_button);
mEmailSignInButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
attemptLogin();
}
});
if(settings.token!="")
startMain();
// uploadAvatar("my_userid")
//getuser("my_userid");
}
private void startMain() {
Intent main = new Intent(LoginActivity.this, MainActivity.class);
startActivity(main);
}
private ApiInterface getInterfaceService() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
final ApiInterface mInterfaceService = retrofit.create(ApiInterface.class);
return mInterfaceService;
}
private void loginProcessWithRetrofit(final String email, String password){
ApiInterface mApiService = ApiInterface.retrofit.create(ApiInterface.class);
Call mService= mApiService.newsession(email,password);
mService.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
Session mSession = response.body();
if (mSession.token!="")
{
settings =new Settings(getApplicationContext(), email, mSession.userid,mSession.token,null);
startMain();
}else
{
// show error
failedLoginMessage.setText("Wrong username or password. Try again");
mEmailView.requestFocus();
}
}
@Override
public void onFailure(Call call, Throwable t) {
call.cancel();
Toast.makeText(LoginActivity.this, "Please check your network connection", Toast.LENGTH_LONG).show();
}
});
}
private void attemptLogin(){
email=mEmailView.getText().toString();
password=mPasswordView.getText().toString();
loginProcessWithRetrofit(email,password);
}
}
MainActivity
package com.bcg.loginexample;
public class MainActivity extends AppCompatActivity {
private ImageButton imgAvatar;
private de.hdodenhof.circleimageview.CircleImageView profile_image;
private Button btnLogout, btnUseAvatar, btnTakePhoto;
public Settings settings;
MarshMallowPermission marshMallowPermission = new MarshMallowPermission(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
settings =new Settings(getApplicationContext()).getSettings();
String email =settings.getEmail();
TextView loginInformation = (TextView)findViewById(R.id.login_email);
if(email != null || !email.equals("") ){
loginInformation.setText("Welcome!!! You have logged in as " + email);
}else {
loginInformation.setText("Your login email is missing");
}
profile_image =(de.hdodenhof.circleimageview.CircleImageView) findViewById(R.id.profile_image);
imgAvatar = (ImageButton) findViewById(R.id.imgAvatar);
imgAvatar.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
getImage();
}
});
btnLogout = (Button) findViewById(R.id.btnLogout);
btnLogout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
settings.saveSettings(null,null,"",null);
Toast.makeText(MainActivity.this, "Signing out! Token deleted.", Toast.LENGTH_LONG).show();
Intent i = new Intent(MainActivity.this, LoginActivity.class);
startActivity(i);
}
});
btnUseAvatar = (Button) findViewById(R.id.btnUseAvatar);
btnUseAvatar.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String hash = MD5Util.md5Hex(settings.getEmail());
String gravatarUrl = "http://www.gravatar.com/avatar/" + hash + "?s=600&d=600";
settings.setAvatar_url(gravatarUrl);
LoadImage();
}
});
btnTakePhoto = (Button) findViewById(R.id.btnTakePhoto);
btnTakePhoto.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
getImage();
}
});
getuser(settings.getUserid());
//LoadImage( );
}
private void getImage() {
if (!marshMallowPermission.checkPermissionForCamera()) {
marshMallowPermission.requestPermissionForCamera();
} else {
if (!marshMallowPermission.checkPermissionForExternalStorage()) {
marshMallowPermission.requestPermissionForExternalStorage();
} else {
Intent chooseImageIntent = ImagePicker.getPickImageIntent(getApplicationContext());
startActivityForResult(chooseImageIntent, PICK_IMAGE_ID);
}}
}
private void getuser(final String userId){
ApiInterface mApiService = ApiInterface.retrofit.create(ApiInterface.class);
Call mService= mApiService.getuser(userId);
mService.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
User userObject = response.body();
settings.setAvatar_url(ApiInterface.ENDPOINT +userObject.avatar_url.replaceFirst("^/", "") );
LoadImage( );
Toast.makeText(MainActivity.this, "Returned " + userObject, Toast.LENGTH_LONG).show();
}
@Override
public void onFailure(Call call, Throwable t) {
call.cancel();
Toast.makeText(MainActivity.this, "Please check your network connection", Toast.LENGTH_LONG).show();
}
});
}
private static final int PICK_IMAGE_ID = 234; // the number doesn't matter
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch(requestCode) {
case PICK_IMAGE_ID:
Bitmap bitmap = ImagePicker.getImageFromResult(this, resultCode, data);
File file = null;
try {
file = savebitmap(bitmap, "pic.jpeg");
} catch (IOException e) {
e.printStackTrace();
}
uploadAvatar("my_userid", file );
break;
default:
super.onActivityResult(requestCode, resultCode, data);
break;
}
}
public static File savebitmap(Bitmap bmp, String fName) throws IOException {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.JPEG, 60, bytes);
File f = new File(Environment.getExternalStorageDirectory()
+ File.separator + fName);
f.createNewFile();
FileOutputStream fo = new FileOutputStream(f);
fo.write(bytes.toByteArray());
fo.close();
return f;
}
private void uploadAvatar(final String userid,File file){
// File file = new File("/storage/emulated/0/Android/data/com.bcg.loginexample/files/pic.jpg");
// File file = new File("/storage/emulated/0/DCIM/camera/pic.jpg");
// File file = new File(filePath);
RequestBody body = RequestBody.create(MediaType.parse("image/*"), file);
RequestBody description = RequestBody.create(MediaType.parse("text/plain"), "image");
ApiInterface mApiService = ApiInterface.retrofit.create(ApiInterface.class);
Call mService= mApiService.postAvatar(body,description );
mService.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
Avatar avatar = response.body();
settings.setAvatar_url(ApiInterface.ENDPOINT + avatar.avatar_url);
LoadImage();
}
@Override
public void onFailure(Call call, Throwable t) {
call.cancel();
Toast.makeText(getApplicationContext(), "Error: " + t.getMessage(), Toast.LENGTH_LONG).show();
}
});
}
public void LoadImage() {
String url=settings.getAvatar_url();
//load image from server if exists
if (url!=""){
Picasso.with(getApplicationContext())
.load(url)
.networkPolicy(NetworkPolicy.NO_CACHE)
.memoryPolicy(MemoryPolicy.NO_CACHE)
.placeholder(R.drawable.empty_avatar)
.noFade()
.into(profile_image);
}
}
}
ApiInterface
package com.bcg.loginexample.Rest;
public interface ApiInterface {
String ENDPOINT = "URL-TO-YOUR-REST-SERVICE/";
@FormUrlEncoded
@POST("sessions/new")
Call newsession(@Field("email") String email, @Field("password") String password);
@GET("users/{userid}")
Call getuser(@Path("userid") String userid);
@Multipart
@POST ("users/{userid}/avatar/{avatar}")
Call postAvatar (@Part("image\"; filename=\"pic.jpg\" ") RequestBody file , @Part("FirstName") RequestBody description);
public static final Retrofit retrofit = new Retrofit.Builder()
.baseUrl(ENDPOINT)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
Source code available at Github
Points of Interest
Every modern application today consist of some back-end (REST) service and devices that are using it. NodeJS + Android Java is good example of how to build one.
Comments
Post a Comment