Saturday, December 9, 2017

REStFul WS


1. What is a Web Service?


client-server model application, where the client is an s/w application 
build to have reusability of code worldwide
to make two different application implemented in entirely different technology stack interact with each other




RESTful web service WS-REST API

1.    Exposes set of resources through URIs

2.    Stateless

3.    Uniform interface[put|get|delete|post|]

      4. Json|xml|text|jpeg

Advantages

code reusability
interoperability
variety of solutions available

JAX-RS IS specification defining rules to implement rest web services  E.G


javax.ws.rs High-level interfaces and annotations used to create RESTful service resources.

javax.ws.rs.client The JAX-RS client API

javax.ws.rs.container Container-specific JAX-RS API.

javax.ws.rs.core Low-level interfaces and annotations used to create RESTful service resources.

javax.ws.rs.ext APIs that provide extensions to the types supported by the JAX-RS API.



Ways to test :

ü  REST

*    GET REQUEST CAN BE TESTED FROM BROWSER ITSELF

*    YOU CAN TRIGGER IT FROM FORM

*    CAN MAKE AJAX REQUEST

*    POSTMAN:You can set get|post|put|delete

ü  can add headers

ü  can add payload

*    you can write a java client 

ü  DEFAULT httpclient

ü  OK HTTP


Difference between SOA and Web Service

SOA is an architectural /design pattern which emphasis on providing a solution as services

Web services are one of the best implementations of it

Decision making 
data formats 
security
do you know the clients beforehand

Some JAX-RS annotations

@Path

@Produces

@PathParam

@Get

@Consumes

@QueryParam

@Post


@MatrixParam

@Delete

 

@HeaderParam

@Put

 

 

@Head

 

 


























JAX-RS is a Java-based standard API for REST Web Services over HTTP protocol. Representational state tra

Http protocol based web services
representational state transfer
stateless| asynchronous  client-server architecture


Spring 5 WebClient and WebTestClient Tutorial with Examples

https://www.callicoder.com/spring-5-reactive-webclient-webtestclient-examples/

Spring BOOT Rest Client
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-webclient.html



Can you send  boolean as responcetype in entity ? or  How to send boolean as responceEntity?



You can't return a boolean, however, consider using ResponseEntities and use the HTTP status code to communicate success.
public ResponseEntity<String> admin() {
    if (isAdmin()) {
        return new ResponseEntity<String>(HttpStatus.OK);
    } else {
        return new ResponseEntity<String>(HttpStatus.FORBIDDEN);            
    }
}

This method will return an empty document, but you can control the status code (FORBIDDEN is only an example, you can then chose the status code that is more appropriate, e.g. NOT FOUND ?)




ResponseEntity(HttpStatus.NOT_FOUND)
ResponseEntity(parcel, HttpStatus.OK)
https://www.programcreek.com/java-api-examples/?api=org.springframework.http.ResponseEntity

https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/http/ResponseEntity.html
There are 


1) @RestController : First of all, we are using Spring 4′s new @RestController annotation. This annotation eliminates the need of annotating each method with @ResponseBody. Under the hood, 

Spring shortcut Annotation
@GetMapping ,@PutMapping 
http://www.baeldung.com/spring-new-requestmapping-shortcuts
_____________________________________________________________________


______________________________________________________________________________
@QueryParam - Jersey
@QueryParam - ApacheCXF
@QueryParam - Spring REST
localhost:8080/myapp/booksearch/title/{title}/author/{author}?sortby=relevance&year=2013
Spring REST
@RequestMapping (...)
public String getBooks(@RequestParam(required = false, value = "sortby") String sortBy, @RequestParam(required = false, value = "year") String year) {...}





http://websystique.com/spring-boot/spring-boot-rest-api-example/
@RestController is itself annotated with @ResponseBody, and can be considered as combination of @Controller and @ResponseBody.
2) @RequestBody : If a method parameter is annotated with @RequestBody, Spring will bind the incoming HTTP request body(for the URL mentioned in @RequestMapping for that method) to that parameter. While doing that, Spring will [behind the scenes] use HTTP Message converters to convert the HTTP request body into domain object [deserialize request body to domain object], based on ACCEPT or Content-Type header present in request.
3) @ResponseBody : If a method is annotated with @ResponseBody, Spring will bind the return value to outgoing HTTP response body. While doing that, Spring will [behind the scenes] use HTTP Message converters to convert the return value to HTTP response body [serialize the object to response body], based on Content-Type present in request HTTP header. As already mentioned, in Spring 4, you may stop using this annotation.
4) @ResponseEntity is a real deal. It represents the entire HTTP response. Good thing about it is that you can control anything that goes into it. You can specify status code, headers, and body. It comes with several constructors to carry the information you want to sent in HTTP Response.
5) @PathVariable This annotation indicates that a method parameter should be bound to a URI template variable [the one in '{}'].
Basically, @RestController , @RequestBody, ResponseEntity & @PathVariable are all you need to know to implement a REST API in Spring. Additionally, spring provides several support classes to help you implement something customized.

6)MediaType : Although we didn’t, with @RequestMapping annotation, you can additionally, specify the MediaType to be produced or consumed (using produces or consumes attributes) by that particular controller method, to further narrow down the mapping























REST TEMPLATE

This example is not original work ,It has been saved purely for learning..

exchange() : Executes the HTTP method for the given URI. It returns ResponseEntity. It can communicate using any HTTP method.



--> getForObject() : It retrieves an entity using HTTP GET method on the given URL. 

getForEntity() : It retrieves an entity by using HTTP GET method for the given URL. It returns ResponseEntity.


 
headForHeaders() : Retrieves all headers. It uses HTTP HEAD method. 
 
delete() : Deletes the resources at the given URL. It uses HTTP DELETE method. 
put(): It creates new resource or update for the given URL using HTTP PUT method. 



postForObject(): It creates new resource using HTTP POST method and returns an entity. 
postForLocation(): It creates new resource using HTTP POST method and returns the location of created new resource. 
postForEntity(): It creates news resource using HTTP POST method to the given URI template. It returnsResponseEntity





SPRING HATEOS[HYPERMEDIA AS ENGINE OF APPLICATION STATE]
____________________________________________________________________

Dependency : spring-hateoas 

ControllerLinkBuilder
Builder to ease building Link instances pointing to Spring MVC controllers.
linkTo(Class controller)
Creates a new ControllerLinkBuilder with a base of the mapping annotated to the given controller class.

Its not easy to remember and maintain the complete list of services and state transition by the clients.
  • Model classes for link, resource representation models
  • Link builder API to create links pointing to Spring MVC controller methods
  • Support for hypermedia formats like HAL
Resources
As pretty much every representation of a resource will contain some links (at least the self one) we provide a base class to actually inherit from when designing representation classes.
https://www.dineshonjava.com/spring-hateoas-hypermedia-driven-restful-web-service/
add(Iterable<Link> links)Adds all given Links to the resource.getLink(String rel)Returns the link with the given rel.removeLinks()Removes all Links added to the resource so far.hasLinks()Returns whether the resource contains Links at all.removeLinks()Removes all Links added to the resource so far
Person person = new Person(1L, "Dave", "Matthews");
//                 /person                 /     1
Link link = linkTo(PersonController.class).slash(person.getId()).withSelfRel();
assertThat(link.getRel(), is(Link.REL_SELF));
assertThat(link.getHref(), endsWith("/people/1"));

Entry point: The entry point to our API is :
http://localhost:1111/accounts

http://localhost:1111/accounts/{id}
{
accountId: 1000,
accountHolder: {
userId: 5115,
name: "Arnav",
address: "Noida"
},
amount: 1039.13,
ifscCode: "AA992QA",
_links: {
accounts: {
href: "http://localhost:1111/accounts"
},
accountHolder: {
href: "http://localhost:1111/accounts/1000/5115"
}
}
}
http://localhost:1111/accounts/{id}/{accountHolderId}
{
userId: 5115,
name: "Arnav",
address: "Noida",
_links: {
account: {
href: "http://localhost:1111/accounts/1000"
}
}
}
This account holder detail api has link for associated account detail for this user
 @RequestMapping(value= "/accounts/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
 public Resource byId(@PathVariable("id") Long id) {
  logger.info("accounts byId() invoked: " + id);
  Account account = accountRepository.getAccount(id.toString());
  Resource resource = new Resource(account);
  resource.add(linkTo(methodOn(AccountController.class).all()).withRel("accounts"));
  resource.add(linkTo(methodOn(AccountController.class).findAccountHolderById(account.getAccountId(), account.getAccountHolder().getUserId())).withRel("accountHolder"));
  logger.info("accounts byId() found: " + account);
  return resource;
 }
 
 @RequestMapping(value= "/accounts/{id}/{userId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
 public Resource findAccountHolderById(@PathVariable("id") Long id, @PathVariable("userId") Long userId) {
  logger.info("accounts findAccountHolderById() invoked: " + id);
  Account account = accountRepository.getAccount(id.toString());
  AccountHolder accountHolder = account.getAccountHolder();
  Resource resource = new Resource(accountHolder);
  resource.add(linkTo(methodOn(AccountController.class).byId(account.getAccountId())).withRel("account"));
  logger.info("accounts findAccountHolderById() found: " + account);
  return resource;
 }
 
 private Resource getAccountResource(Account account) {
  Resource resource = new Resource(account);
  resource.add(linkTo(methodOn(AccountController.class).byId(account.getAccountId())).withSelfRel());
  resource.add(linkTo(methodOn(AccountController.class).findAccountHolderById(account.getAccountId(), account.getAccountHolder().getUserId())).withRel("accountHolder"));
  return resource;
 }
References https://docs.spring.io/spring-hateoas/docs/current/reference/html/ https://www.dineshonjava.com/spring-hateoas-hypermedia-driven-restful-web-service/

CONFIGURATION



Preview:

package com.websystique.springmvc.configuration;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.websystique.springmvc")
public class HelloWorldConfiguration {
 

}

ADDING FILTER



package com.websystique.springmvc.configuration;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;


public class CORSFilter implements Filter {

 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
  System.out.println("Filtering on...........................................................");
  HttpServletResponse response = (HttpServletResponse) res;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Credentials", "true");
  response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
  response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Authorization, Origin, Accept, Access-Control-Request-Method, Access-Control-Request-Headers");

  chain.doFilter(req, res);
 }

 public void init(FilterConfig filterConfig) {}

 public void destroy() {}

}

INITIALIZER



package com.websystique.springmvc.configuration;

import javax.servlet.Filter;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class HelloWorldInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
 
    @Override
    protected Class[] getRootConfigClasses() {
        return new Class[] { HelloWorldConfiguration.class };
    }
  
    @Override
    protected Class[] getServletConfigClasses() {
        return null;
    }
  
    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }
    
    @Override
    protected Filter[] getServletFilters() {
     Filter [] singleton = { new CORSFilter()};
     return singleton;
    }
 
}



RESTCONTROLLER




package com.websystique.springmvc.controller;
 
import java.util.List;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponentsBuilder;
 
import com.websystique.springmvc.model.User;
import com.websystique.springmvc.service.UserService;
 
@RestController
public class HelloWorldRestController {
 
    @Autowired
    UserService userService;  //Service which will do all data retrieval/manipulation work
 
     
    //-------------------Retrieve All Users--------------------------------------------------------
     
    @RequestMapping(value = "/user/", method = RequestMethod.GET)
    public ResponseEntity<List> listAllUsers() {
        List users = userService.findAllUsers();
        if(users.isEmpty()){
            return new ResponseEntity>(HttpStatus.NO_CONTENT);//You many decide to return HttpStatus.NOT_FOUND
        }
        return new ResponseEntity>(users, HttpStatus.OK);
    }
 
 
    //-------------------Retrieve Single User--------------------------------------------------------
     
    @RequestMapping(value = "/user/{id}", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE,MediaType.APPLICATION_XML_VALUE})
    public ResponseEntity getUser(@PathVariable("id") long id) {
        System.out.println("Fetching User with id " + id);
        User user = userService.findById(id);
        if (user == null) {
            System.out.println("User with id " + id + " not found");
            return new ResponseEntity(HttpStatus.NOT_FOUND);
        }
        return new ResponseEntity(user, HttpStatus.OK);
    }
 
     
     
    //-------------------Create a User--------------------------------------------------------
     
    @RequestMapping(value = "/user/", method = RequestMethod.POST)
    public ResponseEntity<Void> createUser(@RequestBody User user, UriComponentsBuilder ucBuilder) {
        System.out.println("Creating User " + user.getName());
 
        if (userService.isUserExist(user)) {
            System.out.println("A User with name " + user.getName() + " already exist");
            return new ResponseEntity(HttpStatus.CONFLICT);
        }
 
        userService.saveUser(user);
 
        HttpHeaders headers = new HttpHeaders();
        headers.setLocation(ucBuilder.path("/user/{id}").buildAndExpand(user.getId()).toUri());
        return new ResponseEntity(headers, HttpStatus.CREATED);
    }
 
     
    //------------------- Update a User --------------------------------------------------------
     
    @RequestMapping(value = "/user/{id}", method = RequestMethod.PUT)
    public ResponseEntity updateUser(@PathVariable("id") long id, @RequestBody User user) {
        System.out.println("Updating User " + id);
         
        User currentUser = userService.findById(id);
         
        if (currentUser==null) {
            System.out.println("User with id " + id + " not found");
            return new ResponseEntity(HttpStatus.NOT_FOUND);
        }
 
        currentUser.setName(user.getName());
        currentUser.setAge(user.getAge());
        currentUser.setSalary(user.getSalary());
         
        userService.updateUser(currentUser);
        return new ResponseEntity(currentUser, HttpStatus.OK);
    }
 
    //------------------- Delete a User --------------------------------------------------------
     
    @RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE)
    public ResponseEntity deleteUser(@PathVariable("id") long id) {
        System.out.println("Fetching & Deleting User with id " + id);
 
        User user = userService.findById(id);
        if (user == null) {
            System.out.println("Unable to delete. User with id " + id + " not found");
            return new ResponseEntity(HttpStatus.NOT_FOUND);
        }
 
        userService.deleteUserById(id);
        return new ResponseEntity(HttpStatus.NO_CONTENT);
    }
 
     
    //------------------- Delete All Users --------------------------------------------------------
     
    @RequestMapping(value = "/user/", method = RequestMethod.DELETE)
    public ResponseEntity deleteAllUsers() {
        System.out.println("Deleting All Users");
 
        userService.deleteAllUsers();
        return new ResponseEntity(HttpStatus.NO_CONTENT);
    }
 
}


package com.websystique.springmvc.model;

public class AuthTokenInfo {

 private String access_token;
 private String token_type;
 private String refresh_token;
 private int expires_in;
 private String scope;
 public String getAccess_token() {
  return access_token;
 }
 public void setAccess_token(String access_token) {
  this.access_token = access_token;
 }
 public String getToken_type() {
  return token_type;
 }
 public void setToken_type(String token_type) {
  this.token_type = token_type;
 }
 public String getRefresh_token() {
  return refresh_token;
 }
 public void setRefresh_token(String refresh_token) {
  this.refresh_token = refresh_token;
 }
 public int getExpires_in() {
  return expires_in;
 }
 public void setExpires_in(int expires_in) {
  this.expires_in = expires_in;
 }
 public String getScope() {
  return scope;
 }
 public void setScope(String scope) {
  this.scope = scope;
 }
 @Override
 public String toString() {
  return "AuthTokenInfo [access_token=" + access_token + ", token_type=" + token_type + ", refresh_token="
    + refresh_token + ", expires_in=" + expires_in + ", scope=" + scope + "]";
 }
 
 
}

package com.websystique.springmvc.model;

public class User {

 private long id;
 
 private String name;
 
 private int age;
 
 private double salary;

 public User(){
  id=0;
 }
 
 public User(long id, String name, int age, double salary){
  this.id = id;
  this.name = name;
  this.age = age;
  this.salary = salary;
 }
 
 public long getId() {
  return id;
 }

 public void setId(long id) {
  this.id = id;
 }

 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 double getSalary() {
  return salary;
 }

 public void setSalary(double salary) {
  this.salary = salary;
 }

 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + (int) (id ^ (id >>> 32));
  return result;
 }

 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  User other = (User) obj;
  if (id != other.id)
   return false;
  return true;
 }

 @Override
 public String toString() {
  return "User [id=" + id + ", name=" + name + ", age=" + age
    + ", salary=" + salary + "]";
 }


}


Preview:
package com.websystique.springmvc.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
import org.springframework.security.oauth2.provider.token.TokenStore;

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

 private static String REALM="MY_OAUTH_REALM";
 
 @Autowired
 private TokenStore tokenStore;

 @Autowired
 private UserApprovalHandler userApprovalHandler;

 @Autowired
 @Qualifier("authenticationManagerBean")
 private AuthenticationManager authenticationManager;

 @Override
 public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

  clients.inMemory()
         .withClient("my-trusted-client")
            .authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
            .authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
            .scopes("read", "write", "trust")
            .secret("secret")
            .accessTokenValiditySeconds(120).//Access token is only valid for 2 minutes.
            refreshTokenValiditySeconds(600);//Refresh token is only valid for 10 minutes.
 }

 @Override
 public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
  endpoints.tokenStore(tokenStore).userApprovalHandler(userApprovalHandler)
    .authenticationManager(authenticationManager);
 }

 @Override
 public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
  oauthServer.realm(REALM+"/client");
 }

}
-->

No comments:

Post a Comment