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); CallmService= 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); CallmService= 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") CallSource code available at Githubnewsession(@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(); }
Comments
Post a Comment