I have developed few RESTful methods and exposed them via Apache Cxf
I'm developing the client side application using Spring MVC and I'm looking for a simple example to demonstrate how to call/consume these REST methods using Spring MVC
I know how to do it using Apache http client but prefer to use Spring MVC in case such this has already been implemented there.
Spring provides simple wrapper to consume RESTful services called RestTemplate. It performs path variable resolution, marshalling and unmarshalling:
Map<String, Integer> vars = new HashMap<String, Integer>();
vars.put("hotelId", 42);
vars.put("roomId", 13);
Room room = restTemplate.getForObject(
"http://example.com/hotels/{hotelId}/rooms/{roomId}",
Room.class, vars);
Assuming Room is a JAXB object which can be understood by The RestTemplate.
Note that this class has nothing to do with Spring MVC. You can use it in MVC application, but also in a standalone app. It is a client library.
See also
REST in Spring 3: RestTemplate
Use path variables to consume REST data. For example:
https://localhost/products/{12345}
This pattern should give you the detail of the product having product id 12345.
#RequestMapping(value="/products/{productId}")
#ResponseBody
public SomeModel doProductProcessing(#PathVariable("productId") String productId){
//do prpcessing with productid
return someModel;
}
If you want to consume Rest Service from another service then have a look at:
http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/client/RestTemplate.html
and
http://www.informit.com/guides/content.aspx?g=java&seqNum=546
Related
I am currently working on a Spring Boot project and I am fairly new to template engines. This will also be my first own private project with Spring Boot.
I would like to know whether it is necessary to include a template engine, such as Thymeleaf, while developing a web application with Spring Boot. I'm using PostegreSQL for the database.
I read under another post that a template engine is not needed, if the backend framework uses JSON for data exchange, because template engines are for rendering retrieved data to HTML for the client. I retrieve JSON objects from the database, can I leave template engines out of my project then?
If any more details are needed, leave a comment below.
No they aren't necessary, in fact most new projects that require web-pages are using single page applications now like Angular, React, Vue, ... over thymeleaf or jsp.
Aside from that a Spring project doesn't always need web pages, for instance when you are just creating a REST API for other applications to call on, or when you are automating things like: a mail service / printing / ... You name it.
However, when you DO want a simple solution with some pages that aren't all that dynamic or complex, pivotal / VMware does recommend to use thymeleaf (over jsp and other solutions) because it integrates easily.
I read under another post that a template engine is not needed, if the backend framework uses JSON for data exchange, because template engines are for rendering retrieved data to HTML for the client. I retrieve JSON objects from the database, can I leave template engines out of my project then?
This is partly true. Yes, Thymeleaf and alike are mostly intended to render data to HTML. They can render any text data, including JSON, but there are tools better suited for the job. On other hand it does not matter how you store the data in your database or what database you are using. You can't just skip rendering (serializing) the response so it does not matter how you store it. What matters is what you want to return as response. For HTML Thymeleaf or even JSP are suitable, but for JSON you may want to use Jackson or Gson instead.
You didn't mentioned the technology you are going to use, so for my examples I'll assume you intend to use Spring Web MVC. Lets take a look at "traditional" controller:
#Controller
public class GreetingController {
#GetMapping("/greeting")
public ModelAndView greeting(#RequestParam(value = "name", defaultValue = "World") String name) {
return new ModelAndView("greeting", "greeting", new Greeting(name));
}
}
When you make GET request to "/greeting", Spring will call greeting and get the object it returns. In this case it contains the model (the data we want to render) and the view (the template file to use). Then it will try to find a view (something like greeting.html or greeting.jsp) and use template engine like Thymeleaf (or whatever else is configured) to render it (typically to HTML).
What if we want to return JSON instead? In this case we need to:
modify greeting to return Greeting instance instead of ModelAndView
Use RestController instead of Controller. This will tell Spring MVC that we want to directly serialize the object returned to JSON (or similar format) instead of using template to do that.
Here is the modified example:
#RestController
public class GreetingController {
#GetMapping("/greeting")
public Greeting greeting(#RequestParam(value = "name", defaultValue = "World") String name) {
return new Greeting(name);
}
}
Spring MVC still needs some help to serialize the Greeting instance to JSON. But if you use Spring Boot and the web starter, you don't have to worry about it. It will include Jackson for you.
This is in no way exhaustive answer. As a matter of fact it skips a lot of details, but I hope nevertheless it is useful one. If you want to create REST API using JSON, this Building a RESTful Web Service guide is a good place to start. If you follow the Starting with Spring Initializr steps you'll get a project setup with only what is needed (well maybe with a bit extra but I would not worry too much about it).
I have two Java processes - which get spawned from the same Jar using different run configurations
Process A - Client UI component , Developed Using Spring bean xml based approach. No Spring Boot is there.
Process B - A new Springboot Based component , hosts REST End points.
Now from Process A , on various button click how can I call the REST end points on Process B using Feign Client.
Note - Since Process A is Spring XML based , right at the moment we can not convert that to Spring boot. Hence #EnableFeignClients can not be used to initialise the Feign Clients
So Two questions
1) If the above is possible how to do it ?
2) Till Process A is moved to Spring boot - is Feign still an easier option than spring REST template ?
Feign is a Java to HTTP client binder inspired by Retrofit, JAXRS-2.0, and WebSockets and you can easily use feign without spring boot. And Yes, feign still better option to use because Feign Simplify the HTTP API Clients using declarative way as Spring REST does.
1) Define http methods and endpoints in interface.
#Headers({"Content-Type: application/json"})
public interface NotificationClient {
#RequestLine("POST")
String notify(URI uri, #HeaderMap Map<String, Object> headers, NotificationBody body);
}
2) Create Feign client using Feign.builder() method.
Feign.builder()
.encoder(new JacksonEncoder())
.decoder(customDecoder())
.target(Target.EmptyTarget.create(NotificationClient.class));
There are various decoders available in feign to simplify your tasks.
You are able to just initialise Feign in any code (without spring) just like in the readme example:
public static void main(String... args) {
GitHub github = Feign.builder()
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com");
...
}
Please take a look at the getting started guide: feign on github
In a spring boot application using Netflix's Feign to make HTTP requests to a service, is there an easy way to cache and return these cached values automatically? The cache should be based on the parameters passed to the request (similar to memoizing a function for X minutes).
I'm looking for something like the #Cache annotation below:
#Component
#FeignClient(value = "facebook", url = "${auth.facebook.url}")
public interface FacebookClient {
#Cache(600) // Something like this.
#RequestMapping(method = RequestMethod.GET, value = "/debug_token?input_token={input_token}&access_token={access_token}")
Map debugToken(#PathVariable("input_token") String inputToken, #PathVariable("access_token") String appToken);
}
Of course I could cache it myself using a decorator around the FacebookClient, I was wondering if there was a quicker/less code way.
Springs #Cacheable does what you need.
Check: Caching Data with Spring
Feign does not support caching. I would prefer the JCache (JSR-107) and maybe use it via the spring-boot-starter-cache described in the spring guide
JCache is an abstraction for the proprietary implementation of EhCache, Hazelcast, ... so it is possible to change the implementation with very less impact on the application. At first I would prefer EhCache 3.
Feign client doesn't support caching.
Another better way would be to create a Service class which calls FeignClient and put up cache on methods of this new Service class.
We use Spring to implement REST controller, for example:
#Controller
#RequestMapping("/myservice")
public class MyController {
#RequestMapping(value = "foo", method = RequestMethod.GET)
public #ResponseBody string foo() {...}
}
I can call this service using spring RestTemplate, and it works fine, but I would prefer to invoke it using a proxy, instead of typeless invocation using string url:
// client code:
MyController proxy = getProxy("baseUrl", MyController.class);
String results = proxy.foo();
So the input to proxy generation is java interface with annotations describing REST details.
I read this article and it looks like all types of remote calls do have proxies, and all I need for REST is something like RestProxyFactoryBean, that would take my REST java interface and return type-safe proxy that uses RestTemplate as implementation.
The closest solution I found is JBoss RESTEasy.
But it seems to use different set of annotations, so I am not sure it will work with annotations I already have: #Controller, #RequestMapping.
Are there other options, or RESTEasy is the only one?
Note, I am spring newbie so some obvious spring things are pretty new to me.
Thank you.
Dima
You can try Feign by Netflix, a lightweight proxy-based REST client. It works declaratively through annotations, and it's used by Spring Cloud projects to interact with Netflix Eureka.
One of the reasons the REST paradigm was invented was because expirience with other remoting technologies (RMI, CORBA, SOAP) shows us that often, the proxy-based approach creates more problems than it solves.
Theoretically, a proxy makes the fact that a function call is remote transparent to its users, so they can use the function exactly the same way as if it were a local function call.
In practice however this promise cannot be fulfilled, because remote function calls simply have other properties than local calls. Network outages, congestion, timeouts, load problems to name just a few. If you choose to ignore all these things that can go wrong with remote calls, your code probably won't be very stable.
TL;DR: You probably shouldn't work with a proxy, it's not state of the art any more. Just use RestTemplate.
Here is a project trying to generate runtime proxies from the controller annotations (using RestTemplate in the background to handle proxy calls): spring-rest-proxy-client Very early in implementation though.
This seems to do it: https://swagger.io/swagger-codegen/, and swagger has many other nice things for REST API.
Have a look at https://github.com/ggeorgovassilis/spring-rest-invoker.
All you need is to register FactoryBean:
#Configuration
public class MyConfiguration {
#Bean
SpringRestInvokerProxyFactoryBean BankService() {
SpringRestInvokerProxyFactoryBean proxyFactory = new SpringRestInvokerProxyFactoryBean();
proxyFactory.setBaseUrl("http://localhost/bankservice");
proxyFactory.setRemoteServiceInterfaceClass(BankService.class);
return proxyFactory;
}
and after that you can autowire the interface class:
#Autowired
BookService bookService;
I also ended up making my own library for this. I wanted something that is as small as possible, adds only itself to classpath and no transitive dependencies.
A client is created like:
final StoreApi storeApi = SpringRestTemplateClientBuilder
.create(StoreApi.class)
.setRestTemplate(restTemplate)
.setUrl(this.getMockUrl())
.build();
And rest-requests will be performed when invoking the methods:
storeApi.deleteOrder(1234L);
The is supports both method signatures:
ResponseEntity<X> deleteOrder(Long)
X deleteOrder(Long)
I've got spring web application with jersey rest services. However rest is secured via spring security and login process is very hard to perform from unit test code. I'd like to test rest services with whole spring security disabled. Is it even possible?
One of the advantages of annotation based web services is that you can unit-test them easily.
class WebServiceEndpoint {
#Path("/foo/{fooId}")
#POST
#Produces({ MediaType.APPLICATION_XML })
public Response doFoo(#PathParam("fooId") Integer fooId) {
/// ... web service endpoint implementation
}
}
If you're using Spring's servlet filter for security, then there shouldn't be any security-related code in the doFoo method, so you can just create a new WebServiceEndpoint class and call the method. So that's one way of 'disabling' security.
When you say the login process is 'hard', what do you mean? If you've succeeded in logging in once, then you can just reuse the same code in your other unit tests (e.g. in a #Before method).
Just test it as a pojo. Pass in whatever, return whatever, don't load an app context at all - that would be an integration test.
The ability to easily test functionality without the framework loaded is one of the key advantages of spring.
You don't say what's "hard," so I'm assuming that you've got something in your REST service, i.e. in the java method that you want to test, which requires authentication results. Spring has utilities for mocking the authentication results. For example, you can do the following in a #Before setup method:
Object principal = null; // fix this
Object credentials = null; // fix this
Authentication auth = new org.springframework.security.authentication.TestingAuthenticationToken(principal, credentials);
SecurityContextHolder.getContext().setAuthentication(auth);
But again, you haven't said what problem you're actually trying to solve...