SlideShare a Scribd company logo
Creating a Facebook Clone - Part XXV
The last piece of the server code is the WebService layer which is again relatively boilerplate driven. The WebServices mostly invoke the services layer without much
imagination. That's a good thing in engineering terms: boring is good. But it doesn't make a thrilling story. So please bare with me as this will pick up pace a bit when we
connect this to the client side!

We have 3 WebService classes: UserWebService, MediaWebService & PostWebService.
@Controller
@RequestMapping("/user")
@RestController
public class UserWebService {
@Autowired
private UserService users;
@Autowired
private NotificationService notifications;
@ExceptionHandler(LoginException.class)
@ResponseStatus(value=HttpStatus.FORBIDDEN)
public @ResponseBody
ErrorDAO handleLoginException(LoginException e) {
return new ErrorDAO(e.getMessage(), 0);
}
@ExceptionHandler(UnirestException.class)
@ResponseStatus(value=HttpStatus.NOT_FOUND)
public @ResponseBody
ErrorDAO handleEmailException(UnirestException e) {
return new ErrorDAO("Error while sending an email: " + e.
UserWebService
Just like the UserService this class is pretty big and also includes some additional capabilities from the NotificationService. Despite the fact that most of the class is
boilerplate there is still a lot of it so I'll try to keep it manageable by splitting it down a bit.

The WebService is located at the /user base which means any URL would be below that
@Controller
@RequestMapping("/user")
@RestController
public class UserWebService {
@Autowired
private UserService users;
@Autowired
private NotificationService notifications;
@ExceptionHandler(LoginException.class)
@ResponseStatus(value=HttpStatus.FORBIDDEN)
public @ResponseBody
ErrorDAO handleLoginException(LoginException e) {
return new ErrorDAO(e.getMessage(), 0);
}
@ExceptionHandler(UnirestException.class)
@ResponseStatus(value=HttpStatus.NOT_FOUND)
public @ResponseBody
ErrorDAO handleEmailException(UnirestException e) {
return new ErrorDAO("Error while sending an email: " + e.
UserWebService
We map 2 services in this single service. Since notifications has just one method it made sense bringing it here
@Autowired
private NotificationService notifications;
@ExceptionHandler(LoginException.class)
@ResponseStatus(value=HttpStatus.FORBIDDEN)
public @ResponseBody
ErrorDAO handleLoginException(LoginException e) {
return new ErrorDAO(e.getMessage(), 0);
}
@ExceptionHandler(UnirestException.class)
@ResponseStatus(value=HttpStatus.NOT_FOUND)
public @ResponseBody
ErrorDAO handleEmailException(UnirestException e) {
return new ErrorDAO("Error while sending an email: " + e.
getMessage(), 0);
}
@ExceptionHandler(SignupException.class)
@ResponseStatus(value=HttpStatus.FORBIDDEN)
public @ResponseBody
ErrorDAO handleSignupException(SignupException e) {
return new ErrorDAO(e.getMessage(), 0);
}
UserWebService
The exception handlers grab one of the 3 exception types that can be thrown in one of the methods in this class and convert them to an error response object.

Notice that the error response is the ErrorDAO which generates JSON with an error message. This is the ErrorDAO class.
public class ErrorDAO {
private String error;
private int code;
public ErrorDAO() {
}
public ErrorDAO(String error, int code) {
this.error = error;
this.code = code;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}
ErrorDAO
The Error DAO object is just a simple DAO that includes the error message and an optional error code.
@ExceptionHandler(SignupException.class)
@ResponseStatus(value=HttpStatus.FORBIDDEN)
public @ResponseBody
ErrorDAO handleSignupException(SignupException e) {
return new ErrorDAO(e.getMessage(), 0);
}
@RequestMapping(method=RequestMethod.POST, value="/login")
public @ResponseBody
UserDAO login(@RequestBody UserDAO u) throws LoginException {
return users.login(u.getEmail(), u.getPhone(), u.getPassword());
}
@RequestMapping(method=RequestMethod.GET, value="/refresh")
public UserDAO refreshUser(
@RequestHeader(name="auth", required=true) String auth) {
return users.refreshUser(auth);
}
@RequestMapping(method=RequestMethod.POST, value="/signup")
public @ResponseBody
UserDAO signup(@RequestBody UserDAO user)
throws SignupException, UnirestException {
return users.signup(user);
UserWebService
The user/login URL returns the response as JSON in the body and accept a JSON request in the body of the post. The exception will be converted to an ErrorDAO if it's
thrown
@ExceptionHandler(SignupException.class)
@ResponseStatus(value=HttpStatus.FORBIDDEN)
public @ResponseBody
ErrorDAO handleSignupException(SignupException e) {
return new ErrorDAO(e.getMessage(), 0);
}
@RequestMapping(method=RequestMethod.POST, value="/login")
public @ResponseBody
UserDAO login(@RequestBody UserDAO u) throws LoginException {
return users.login(u.getEmail(), u.getPhone(), u.getPassword());
}
@RequestMapping(method=RequestMethod.GET, value="/refresh")
public UserDAO refreshUser(
@RequestHeader(name="auth", required=true) String auth) {
return users.refreshUser(auth);
}
@RequestMapping(method=RequestMethod.POST, value="/signup")
public @ResponseBody
UserDAO signup(@RequestBody UserDAO user)
throws SignupException, UnirestException {
return users.signup(user);
UserWebService
The user/refresh method is a get operation notice that it doesn't accept an argument. It uses an HTTP header named auth for the authorization token
@RequestMapping(method=RequestMethod.GET, value="/refresh")
public UserDAO refreshUser(
@RequestHeader(name="auth", required=true) String auth) {
return users.refreshUser(auth);
}
@RequestMapping(method=RequestMethod.POST, value="/signup")
public @ResponseBody
UserDAO signup(@RequestBody UserDAO user)
throws SignupException, UnirestException {
return users.signup(user);
}
@RequestMapping(method=RequestMethod.GET, value="/verify")
public @ResponseBody
String verifyEmailOrPhone(
@RequestHeader(name="auth", required=true) String auth,
@RequestParam(name="code") String code,
@RequestParam(name="email") boolean email) {
if(users.verifyEmailOrPhone(auth, code, email)) {
return "OK";
}
return "ERROR";
}
UserWebService
Notice I use the auth header almost everywhere as it's a standard part of the request.

You will notice the code is very simple, it's declarative code that defines how the WebService will look before delegating to the underlying service class.
users.update(user);
return "OK";
}
@RequestMapping(value="/avatar/{id:.+}", method=RequestMethod.GET)
public ResponseEntity<byte[]> getAvatar(
@PathVariable("id") String id) {
byte[] av = users.getAvatar(id);
if(av != null) {
return ResponseEntity.ok().contentType(MediaType.IMAGE_JPEG).
body(av);
}
return ResponseEntity.notFound().build();
}
@RequestMapping(method=RequestMethod.GET, value="/set-avatar")
public String setAvatar(
@RequestHeader(name="auth", required=true) String auth,
@RequestParam(name="media", required=true) String mediaId) {
users.setAvatar(auth, mediaId);
return "OK";
}
@RequestMapping(method=RequestMethod.GET, value
="/send-friend-request")
UserWebService
Next we have the operations that map an avatar. This means the URL `user/avata/userId` would return the avatar JPEG directly and would work even with the browser so
we can share this URL.
users.update(user);
return "OK";
}
@RequestMapping(value="/avatar/{id:.+}", method=RequestMethod.GET)
public ResponseEntity<byte[]> getAvatar(
@PathVariable("id") String id) {
byte[] av = users.getAvatar(id);
if(av != null) {
return ResponseEntity.ok().contentType(MediaType.IMAGE_JPEG).
body(av);
}
return ResponseEntity.notFound().build();
}
@RequestMapping(method=RequestMethod.GET, value="/set-avatar")
public String setAvatar(
@RequestHeader(name="auth", required=true) String auth,
@RequestParam(name="media", required=true) String mediaId) {
users.setAvatar(auth, mediaId);
return "OK";
}
@RequestMapping(method=RequestMethod.GET, value
="/send-friend-request")
UserWebService
ResponseEntity lets us control the HTTP response including the mimetype and media content
users.update(user);
return "OK";
}
@RequestMapping(value="/avatar/{id:.+}", method=RequestMethod.GET)
public ResponseEntity<byte[]> getAvatar(
@PathVariable("id") String id) {
byte[] av = users.getAvatar(id);
if(av != null) {
return ResponseEntity.ok().contentType(MediaType.IMAGE_JPEG).
body(av);
}
return ResponseEntity.notFound().build();
}
@RequestMapping(method=RequestMethod.GET, value="/set-avatar")
public String setAvatar(
@RequestHeader(name="auth", required=true) String auth,
@RequestParam(name="media", required=true) String mediaId) {
users.setAvatar(auth, mediaId);
return "OK";
}
@RequestMapping(method=RequestMethod.GET, value
="/send-friend-request")
UserWebService
In this case we send a 404 response when the user doesn't have an avatar
byte[] av = users.getAvatar(id);
if(av != null) {
return ResponseEntity.ok().contentType(MediaType.IMAGE_JPEG).
body(av);
}
return ResponseEntity.notFound().build();
}
@RequestMapping(method=RequestMethod.GET, value="/set-avatar")
public String setAvatar(
@RequestHeader(name="auth", required=true) String auth,
@RequestParam(name="media", required=true) String mediaId) {
users.setAvatar(auth, mediaId);
return "OK";
}
@RequestMapping(method=RequestMethod.GET, value
="/send-friend-request")
public String sendFriendRequest(
@RequestHeader(name="auth", required=true) String auth,
@RequestParam(name="userId", required=true) String userId) {
users.sendFriendRequest(auth, userId);
return "OK";
}
@RequestMapping(method=RequestMethod.GET, value
UserWebService
I use the GET method to make a change in the set-avatar request. This simplifies the code as it already requires a separate operation to upload the Media object
@RequestHeader(name="auth", required=true) String auth,
@RequestParam(name="media", required=true) String mediaId) {
users.setAvatar(auth, mediaId);
return "OK";
}
@RequestMapping(method=RequestMethod.GET, value
="/send-friend-request")
public String sendFriendRequest(
@RequestHeader(name="auth", required=true) String auth,
@RequestParam(name="userId", required=true) String userId) {
users.sendFriendRequest(auth, userId);
return "OK";
}
@RequestMapping(method=RequestMethod.GET, value
="/accept-friend-request")
public String acceptFriendRequest(
@RequestHeader(name="auth", required=true) String auth,
@RequestParam(name="userId", required=true) String userId) {
users.acceptFriendRequest(auth, userId);
return "OK";
}
@RequestMapping(method=RequestMethod.GET, value="/notifications")
UserWebService
The friend request methods are trivial and nearly identical. They both accept the same arguments and pass them on to the underlying service calls.

I use a get operation because I don't want to add a DAO and get into the complexities of POST for this case.

Normally a get operation is a bad idea for an operation that triggers a change. I wouldn't do this on the web since it can lead to accidental resubmission of data.
However, since my client here is an app the use of a get operation simplifies some of the syntax.
@RequestMapping(method=RequestMethod.GET, value
="/accept-friend-request")
public String acceptFriendRequest(
@RequestHeader(name="auth", required=true) String auth,
@RequestParam(name="userId", required=true) String userId) {
users.acceptFriendRequest(auth, userId);
return "OK";
}
@RequestMapping(method=RequestMethod.GET, value="/notifications")
public List<NotificationDAO> listNotifications(
@RequestHeader(name="auth", required=true) String auth,
@RequestParam(name="page") int page,
@RequestParam(name="size") int size) {
return notifications.listNotifications(auth, page, size);
}
@RequestMapping(method=RequestMethod.POST, value="/contacts")
public String uploadContacts(
@RequestHeader(name="auth", required=true) String auth,
@RequestBody List<ShadowUserDAO> contacts) {
users.uploadContacts(auth, contacts);
return "OK";
}
}
UserWebService
Finally the last piece of the UserWebService code is the notification & contacts support. This method allows us to page through the list of notifications fetching a new
page dynamically as needed
@RequestMapping(method=RequestMethod.GET, value
="/accept-friend-request")
public String acceptFriendRequest(
@RequestHeader(name="auth", required=true) String auth,
@RequestParam(name="userId", required=true) String userId) {
users.acceptFriendRequest(auth, userId);
return "OK";
}
@RequestMapping(method=RequestMethod.GET, value="/notifications")
public List<NotificationDAO> listNotifications(
@RequestHeader(name="auth", required=true) String auth,
@RequestParam(name="page") int page,
@RequestParam(name="size") int size) {
return notifications.listNotifications(auth, page, size);
}
@RequestMapping(method=RequestMethod.POST, value="/contacts")
public String uploadContacts(
@RequestHeader(name="auth", required=true) String auth,
@RequestBody List<ShadowUserDAO> contacts) {
users.uploadContacts(auth, contacts);
return "OK";
}
}
UserWebService
We can submit a list of contacts for the shadow user through this API, notice that a List from JSON will implicitly translate to the given contact list. With that we finished
the relatively simple UserWebService.

More Related Content

PDF
Creating a Whatsapp Clone - Part XIV - Transcript.pdf
PDF
Creating a Facebook Clone - Part XXV.pdf
PDF
Creating a Facebook Clone - Part XXVI.pdf
PDF
Introduction to Spring MVC
PDF
Creating a Whatsapp Clone - Part XIV.pdf
PDF
L2 Web App Development Guest Lecture At University of Surrey 20/11/09
ODP
CDI @javaonehyderabad
PPTX
Asp.NET MVC
Creating a Whatsapp Clone - Part XIV - Transcript.pdf
Creating a Facebook Clone - Part XXV.pdf
Creating a Facebook Clone - Part XXVI.pdf
Introduction to Spring MVC
Creating a Whatsapp Clone - Part XIV.pdf
L2 Web App Development Guest Lecture At University of Surrey 20/11/09
CDI @javaonehyderabad
Asp.NET MVC

Similar to Creating a Facebook Clone - Part XXV - Transcript.pdf (20)

PDF
Jasigsakai12 columbia-customizes-cas
PDF
Spring 3: What's New
ODP
Codemotion appengine
PDF
Creating a Facebook Clone - Part XXVI - Transcript.pdf
PDF
Jug Guice Presentation
PDF
Testing in android
PPT
比XML更好用的Java Annotation
PPTX
PDF
softshake 2014 - Java EE
PDF
SOLID Principles
PDF
Creating a Whatsapp Clone - Part II - Transcript.pdf
PPTX
evoting ppt.pptx
PDF
jQuery and Rails: Best Friends Forever
ODP
Building a Cloud API Server using Play(SCALA) & Riak
PPT
Struts 2 + Spring
PPTX
React native by example by Vadim Ruban
PDF
ASPNET_MVC_Tutorial_06_CS
PDF
ASPNET_MVC_Tutorial_06_CS
PDF
JavaOne Brasil 2016: JavaEE e HTML5: da web/desktop ao mobile
PDF
May 2010 - RestEasy
Jasigsakai12 columbia-customizes-cas
Spring 3: What's New
Codemotion appengine
Creating a Facebook Clone - Part XXVI - Transcript.pdf
Jug Guice Presentation
Testing in android
比XML更好用的Java Annotation
softshake 2014 - Java EE
SOLID Principles
Creating a Whatsapp Clone - Part II - Transcript.pdf
evoting ppt.pptx
jQuery and Rails: Best Friends Forever
Building a Cloud API Server using Play(SCALA) & Riak
Struts 2 + Spring
React native by example by Vadim Ruban
ASPNET_MVC_Tutorial_06_CS
ASPNET_MVC_Tutorial_06_CS
JavaOne Brasil 2016: JavaEE e HTML5: da web/desktop ao mobile
May 2010 - RestEasy
Ad

More from ShaiAlmog1 (20)

PDF
The Duck Teaches Learn to debug from the masters. Local to production- kill ...
PDF
create-netflix-clone-06-client-ui.pdf
PDF
create-netflix-clone-01-introduction_transcript.pdf
PDF
create-netflix-clone-02-server_transcript.pdf
PDF
create-netflix-clone-04-server-continued_transcript.pdf
PDF
create-netflix-clone-01-introduction.pdf
PDF
create-netflix-clone-06-client-ui_transcript.pdf
PDF
create-netflix-clone-03-server.pdf
PDF
create-netflix-clone-04-server-continued.pdf
PDF
create-netflix-clone-05-client-model_transcript.pdf
PDF
create-netflix-clone-03-server_transcript.pdf
PDF
create-netflix-clone-02-server.pdf
PDF
create-netflix-clone-05-client-model.pdf
PDF
Creating a Whatsapp Clone - Part II.pdf
PDF
Creating a Whatsapp Clone - Part IX - Transcript.pdf
PDF
Creating a Whatsapp Clone - Part V - Transcript.pdf
PDF
Creating a Whatsapp Clone - Part IV - Transcript.pdf
PDF
Creating a Whatsapp Clone - Part IV.pdf
PDF
Creating a Whatsapp Clone - Part I - Transcript.pdf
PDF
Creating a Whatsapp Clone - Part IX.pdf
The Duck Teaches Learn to debug from the masters. Local to production- kill ...
create-netflix-clone-06-client-ui.pdf
create-netflix-clone-01-introduction_transcript.pdf
create-netflix-clone-02-server_transcript.pdf
create-netflix-clone-04-server-continued_transcript.pdf
create-netflix-clone-01-introduction.pdf
create-netflix-clone-06-client-ui_transcript.pdf
create-netflix-clone-03-server.pdf
create-netflix-clone-04-server-continued.pdf
create-netflix-clone-05-client-model_transcript.pdf
create-netflix-clone-03-server_transcript.pdf
create-netflix-clone-02-server.pdf
create-netflix-clone-05-client-model.pdf
Creating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part IX - Transcript.pdf
Creating a Whatsapp Clone - Part V - Transcript.pdf
Creating a Whatsapp Clone - Part IV - Transcript.pdf
Creating a Whatsapp Clone - Part IV.pdf
Creating a Whatsapp Clone - Part I - Transcript.pdf
Creating a Whatsapp Clone - Part IX.pdf
Ad

Recently uploaded (20)

PDF
Machine learning based COVID-19 study performance prediction
PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PPTX
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
KodekX | Application Modernization Development
PPTX
Spectroscopy.pptx food analysis technology
PPTX
Big Data Technologies - Introduction.pptx
PPTX
sap open course for s4hana steps from ECC to s4
PDF
Encapsulation theory and applications.pdf
PPTX
Cloud computing and distributed systems.
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PPT
Teaching material agriculture food technology
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PDF
Review of recent advances in non-invasive hemoglobin estimation
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PDF
cuic standard and advanced reporting.pdf
PDF
Network Security Unit 5.pdf for BCA BBA.
PPTX
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
Machine learning based COVID-19 study performance prediction
Reach Out and Touch Someone: Haptics and Empathic Computing
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
Dropbox Q2 2025 Financial Results & Investor Presentation
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
KodekX | Application Modernization Development
Spectroscopy.pptx food analysis technology
Big Data Technologies - Introduction.pptx
sap open course for s4hana steps from ECC to s4
Encapsulation theory and applications.pdf
Cloud computing and distributed systems.
Diabetes mellitus diagnosis method based random forest with bat algorithm
Teaching material agriculture food technology
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
Review of recent advances in non-invasive hemoglobin estimation
Mobile App Security Testing_ A Comprehensive Guide.pdf
cuic standard and advanced reporting.pdf
Network Security Unit 5.pdf for BCA BBA.
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
Digital-Transformation-Roadmap-for-Companies.pptx

Creating a Facebook Clone - Part XXV - Transcript.pdf

  • 1. Creating a Facebook Clone - Part XXV The last piece of the server code is the WebService layer which is again relatively boilerplate driven. The WebServices mostly invoke the services layer without much imagination. That's a good thing in engineering terms: boring is good. But it doesn't make a thrilling story. So please bare with me as this will pick up pace a bit when we connect this to the client side! We have 3 WebService classes: UserWebService, MediaWebService & PostWebService.
  • 2. @Controller @RequestMapping("/user") @RestController public class UserWebService { @Autowired private UserService users; @Autowired private NotificationService notifications; @ExceptionHandler(LoginException.class) @ResponseStatus(value=HttpStatus.FORBIDDEN) public @ResponseBody ErrorDAO handleLoginException(LoginException e) { return new ErrorDAO(e.getMessage(), 0); } @ExceptionHandler(UnirestException.class) @ResponseStatus(value=HttpStatus.NOT_FOUND) public @ResponseBody ErrorDAO handleEmailException(UnirestException e) { return new ErrorDAO("Error while sending an email: " + e. UserWebService Just like the UserService this class is pretty big and also includes some additional capabilities from the NotificationService. Despite the fact that most of the class is boilerplate there is still a lot of it so I'll try to keep it manageable by splitting it down a bit. The WebService is located at the /user base which means any URL would be below that
  • 3. @Controller @RequestMapping("/user") @RestController public class UserWebService { @Autowired private UserService users; @Autowired private NotificationService notifications; @ExceptionHandler(LoginException.class) @ResponseStatus(value=HttpStatus.FORBIDDEN) public @ResponseBody ErrorDAO handleLoginException(LoginException e) { return new ErrorDAO(e.getMessage(), 0); } @ExceptionHandler(UnirestException.class) @ResponseStatus(value=HttpStatus.NOT_FOUND) public @ResponseBody ErrorDAO handleEmailException(UnirestException e) { return new ErrorDAO("Error while sending an email: " + e. UserWebService We map 2 services in this single service. Since notifications has just one method it made sense bringing it here
  • 4. @Autowired private NotificationService notifications; @ExceptionHandler(LoginException.class) @ResponseStatus(value=HttpStatus.FORBIDDEN) public @ResponseBody ErrorDAO handleLoginException(LoginException e) { return new ErrorDAO(e.getMessage(), 0); } @ExceptionHandler(UnirestException.class) @ResponseStatus(value=HttpStatus.NOT_FOUND) public @ResponseBody ErrorDAO handleEmailException(UnirestException e) { return new ErrorDAO("Error while sending an email: " + e. getMessage(), 0); } @ExceptionHandler(SignupException.class) @ResponseStatus(value=HttpStatus.FORBIDDEN) public @ResponseBody ErrorDAO handleSignupException(SignupException e) { return new ErrorDAO(e.getMessage(), 0); } UserWebService The exception handlers grab one of the 3 exception types that can be thrown in one of the methods in this class and convert them to an error response object. Notice that the error response is the ErrorDAO which generates JSON with an error message. This is the ErrorDAO class.
  • 5. public class ErrorDAO { private String error; private int code; public ErrorDAO() { } public ErrorDAO(String error, int code) { this.error = error; this.code = code; } public String getError() { return error; } public void setError(String error) { this.error = error; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } } ErrorDAO The Error DAO object is just a simple DAO that includes the error message and an optional error code.
  • 6. @ExceptionHandler(SignupException.class) @ResponseStatus(value=HttpStatus.FORBIDDEN) public @ResponseBody ErrorDAO handleSignupException(SignupException e) { return new ErrorDAO(e.getMessage(), 0); } @RequestMapping(method=RequestMethod.POST, value="/login") public @ResponseBody UserDAO login(@RequestBody UserDAO u) throws LoginException { return users.login(u.getEmail(), u.getPhone(), u.getPassword()); } @RequestMapping(method=RequestMethod.GET, value="/refresh") public UserDAO refreshUser( @RequestHeader(name="auth", required=true) String auth) { return users.refreshUser(auth); } @RequestMapping(method=RequestMethod.POST, value="/signup") public @ResponseBody UserDAO signup(@RequestBody UserDAO user) throws SignupException, UnirestException { return users.signup(user); UserWebService The user/login URL returns the response as JSON in the body and accept a JSON request in the body of the post. The exception will be converted to an ErrorDAO if it's thrown
  • 7. @ExceptionHandler(SignupException.class) @ResponseStatus(value=HttpStatus.FORBIDDEN) public @ResponseBody ErrorDAO handleSignupException(SignupException e) { return new ErrorDAO(e.getMessage(), 0); } @RequestMapping(method=RequestMethod.POST, value="/login") public @ResponseBody UserDAO login(@RequestBody UserDAO u) throws LoginException { return users.login(u.getEmail(), u.getPhone(), u.getPassword()); } @RequestMapping(method=RequestMethod.GET, value="/refresh") public UserDAO refreshUser( @RequestHeader(name="auth", required=true) String auth) { return users.refreshUser(auth); } @RequestMapping(method=RequestMethod.POST, value="/signup") public @ResponseBody UserDAO signup(@RequestBody UserDAO user) throws SignupException, UnirestException { return users.signup(user); UserWebService The user/refresh method is a get operation notice that it doesn't accept an argument. It uses an HTTP header named auth for the authorization token
  • 8. @RequestMapping(method=RequestMethod.GET, value="/refresh") public UserDAO refreshUser( @RequestHeader(name="auth", required=true) String auth) { return users.refreshUser(auth); } @RequestMapping(method=RequestMethod.POST, value="/signup") public @ResponseBody UserDAO signup(@RequestBody UserDAO user) throws SignupException, UnirestException { return users.signup(user); } @RequestMapping(method=RequestMethod.GET, value="/verify") public @ResponseBody String verifyEmailOrPhone( @RequestHeader(name="auth", required=true) String auth, @RequestParam(name="code") String code, @RequestParam(name="email") boolean email) { if(users.verifyEmailOrPhone(auth, code, email)) { return "OK"; } return "ERROR"; } UserWebService Notice I use the auth header almost everywhere as it's a standard part of the request. You will notice the code is very simple, it's declarative code that defines how the WebService will look before delegating to the underlying service class.
  • 9. users.update(user); return "OK"; } @RequestMapping(value="/avatar/{id:.+}", method=RequestMethod.GET) public ResponseEntity<byte[]> getAvatar( @PathVariable("id") String id) { byte[] av = users.getAvatar(id); if(av != null) { return ResponseEntity.ok().contentType(MediaType.IMAGE_JPEG). body(av); } return ResponseEntity.notFound().build(); } @RequestMapping(method=RequestMethod.GET, value="/set-avatar") public String setAvatar( @RequestHeader(name="auth", required=true) String auth, @RequestParam(name="media", required=true) String mediaId) { users.setAvatar(auth, mediaId); return "OK"; } @RequestMapping(method=RequestMethod.GET, value ="/send-friend-request") UserWebService Next we have the operations that map an avatar. This means the URL `user/avata/userId` would return the avatar JPEG directly and would work even with the browser so we can share this URL.
  • 10. users.update(user); return "OK"; } @RequestMapping(value="/avatar/{id:.+}", method=RequestMethod.GET) public ResponseEntity<byte[]> getAvatar( @PathVariable("id") String id) { byte[] av = users.getAvatar(id); if(av != null) { return ResponseEntity.ok().contentType(MediaType.IMAGE_JPEG). body(av); } return ResponseEntity.notFound().build(); } @RequestMapping(method=RequestMethod.GET, value="/set-avatar") public String setAvatar( @RequestHeader(name="auth", required=true) String auth, @RequestParam(name="media", required=true) String mediaId) { users.setAvatar(auth, mediaId); return "OK"; } @RequestMapping(method=RequestMethod.GET, value ="/send-friend-request") UserWebService ResponseEntity lets us control the HTTP response including the mimetype and media content
  • 11. users.update(user); return "OK"; } @RequestMapping(value="/avatar/{id:.+}", method=RequestMethod.GET) public ResponseEntity<byte[]> getAvatar( @PathVariable("id") String id) { byte[] av = users.getAvatar(id); if(av != null) { return ResponseEntity.ok().contentType(MediaType.IMAGE_JPEG). body(av); } return ResponseEntity.notFound().build(); } @RequestMapping(method=RequestMethod.GET, value="/set-avatar") public String setAvatar( @RequestHeader(name="auth", required=true) String auth, @RequestParam(name="media", required=true) String mediaId) { users.setAvatar(auth, mediaId); return "OK"; } @RequestMapping(method=RequestMethod.GET, value ="/send-friend-request") UserWebService In this case we send a 404 response when the user doesn't have an avatar
  • 12. byte[] av = users.getAvatar(id); if(av != null) { return ResponseEntity.ok().contentType(MediaType.IMAGE_JPEG). body(av); } return ResponseEntity.notFound().build(); } @RequestMapping(method=RequestMethod.GET, value="/set-avatar") public String setAvatar( @RequestHeader(name="auth", required=true) String auth, @RequestParam(name="media", required=true) String mediaId) { users.setAvatar(auth, mediaId); return "OK"; } @RequestMapping(method=RequestMethod.GET, value ="/send-friend-request") public String sendFriendRequest( @RequestHeader(name="auth", required=true) String auth, @RequestParam(name="userId", required=true) String userId) { users.sendFriendRequest(auth, userId); return "OK"; } @RequestMapping(method=RequestMethod.GET, value UserWebService I use the GET method to make a change in the set-avatar request. This simplifies the code as it already requires a separate operation to upload the Media object
  • 13. @RequestHeader(name="auth", required=true) String auth, @RequestParam(name="media", required=true) String mediaId) { users.setAvatar(auth, mediaId); return "OK"; } @RequestMapping(method=RequestMethod.GET, value ="/send-friend-request") public String sendFriendRequest( @RequestHeader(name="auth", required=true) String auth, @RequestParam(name="userId", required=true) String userId) { users.sendFriendRequest(auth, userId); return "OK"; } @RequestMapping(method=RequestMethod.GET, value ="/accept-friend-request") public String acceptFriendRequest( @RequestHeader(name="auth", required=true) String auth, @RequestParam(name="userId", required=true) String userId) { users.acceptFriendRequest(auth, userId); return "OK"; } @RequestMapping(method=RequestMethod.GET, value="/notifications") UserWebService The friend request methods are trivial and nearly identical. They both accept the same arguments and pass them on to the underlying service calls. I use a get operation because I don't want to add a DAO and get into the complexities of POST for this case. Normally a get operation is a bad idea for an operation that triggers a change. I wouldn't do this on the web since it can lead to accidental resubmission of data. However, since my client here is an app the use of a get operation simplifies some of the syntax.
  • 14. @RequestMapping(method=RequestMethod.GET, value ="/accept-friend-request") public String acceptFriendRequest( @RequestHeader(name="auth", required=true) String auth, @RequestParam(name="userId", required=true) String userId) { users.acceptFriendRequest(auth, userId); return "OK"; } @RequestMapping(method=RequestMethod.GET, value="/notifications") public List<NotificationDAO> listNotifications( @RequestHeader(name="auth", required=true) String auth, @RequestParam(name="page") int page, @RequestParam(name="size") int size) { return notifications.listNotifications(auth, page, size); } @RequestMapping(method=RequestMethod.POST, value="/contacts") public String uploadContacts( @RequestHeader(name="auth", required=true) String auth, @RequestBody List<ShadowUserDAO> contacts) { users.uploadContacts(auth, contacts); return "OK"; } } UserWebService Finally the last piece of the UserWebService code is the notification & contacts support. This method allows us to page through the list of notifications fetching a new page dynamically as needed
  • 15. @RequestMapping(method=RequestMethod.GET, value ="/accept-friend-request") public String acceptFriendRequest( @RequestHeader(name="auth", required=true) String auth, @RequestParam(name="userId", required=true) String userId) { users.acceptFriendRequest(auth, userId); return "OK"; } @RequestMapping(method=RequestMethod.GET, value="/notifications") public List<NotificationDAO> listNotifications( @RequestHeader(name="auth", required=true) String auth, @RequestParam(name="page") int page, @RequestParam(name="size") int size) { return notifications.listNotifications(auth, page, size); } @RequestMapping(method=RequestMethod.POST, value="/contacts") public String uploadContacts( @RequestHeader(name="auth", required=true) String auth, @RequestBody List<ShadowUserDAO> contacts) { users.uploadContacts(auth, contacts); return "OK"; } } UserWebService We can submit a list of contacts for the shadow user through this API, notice that a List from JSON will implicitly translate to the given contact list. With that we finished the relatively simple UserWebService.