The rest application in build using spring and jersy.
I need a rest application that can read the value of #path annotation from the properties file.
PFB the code sample:-
#Controller
#Path("report")
public class CommonService extends BaseService
{
#Path("specificGet")
#GET
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response getReport ()
{
-----------
-----------
}
I want the value that is now "specificGet" to be readable from a .properties file.
For example if i have the below entry in properties file:-
path.getwithoutparam=thispathforGet
I want to refer to this property for #path annotation
Please comments/suggestions will be very helpful.
I might get some downvotes :)) but I'm pretty sure there is no out-of-the-box support for property placeholders for #Path annotations.
I guess you would have to build your own annotation processor in this case, that plugs into Spring property handling system.
Related
I've one spring boot rest controller method which is mapped to multiple mappings. Please find the example code below.
#RestController
public class HomeController {
#RequestMapping( {"/", "/home"} )
public String home() {
return "Hello, World!";
}
}
I want to hide /home mapping from swagger documentation.
Can someone please help me to achieve this.
I also searched for a way to hide certain URLs from multi mapping methods. Unfortunately, I don't think it's possible when multi mapping it's defined like this #RequestMapping( {url1, url2} )
There are 2 alternative ways to do it:
Split your method into 2 methods that call the same function and annotate the one you want to hide with #Operation(hidden=true)
Define exceptions in your swagger config (for swagger 3 which uses open API):
#Configuration
public class SwaggerConfig {
#Bean
public GroupedOpenApi myApi()
{
return GroupedOpenApi.builder()
.pathsToMatch("/**")
.pathsToExclude("/home")
.build();
}
}
Is it possible to define the value of a #RequestMapping annotation in Spring by defining it in a properties file?
Actually, I do something like:
#Controller
#RequestMapping("/xxx")
public class MyController {
...
}
But I would like to store the path /xxx in a properties file. Why? For instance, it is less likely that I do mystakes in my templates if I rename the path in the controller.
In other framework this is allowed (see Symfony, for instance).
It should be possible to use placeholders in #RequestMapping, like for example #RequestMapping("${foo.bar}"). Take a look at the documentation for more details:
Patterns in #RequestMapping annotations support ${…} placeholders against local properties and/or system properties and environment variables. This may be useful in cases where the path a controller is mapped to may need to be customized through configuration. For more information on placeholders, see the javadocs of the PropertyPlaceholderConfigurer class.
Thx for the help. It is my contribution...
No dependencies are necessary because maven do everything by itself.
In the property file - use maven interpolation, such as below:
vs= v1
us= users
me= messages
url.user=${vs}/${us}
url.mess=${vs}/${me}
In your destiny file, for example controller/resource (in mycase):
#RestController
//#RequestMapping("v1/users") <<<<<<instead this
#RequestMapping("${url.user}")<<<<<<use this
#Api(value = "API RESTFUL)
public class UserResource {
//
As bohuslav burghardt has mentioned this is totally possible.
So if you have a common domain stored in your application.properties file you can use placeholders to call it in your controller/s and even chain placeholders & text together.
For Example...
In your .properties file
app.domain = mydomain/v1
In the controller
#RestController
#RequestMapping("${app.domain}/my-controller")
public class MyController {
I have a class like:
public class TestService {
#Path("/v1/test1/list")
public Response getTest1() {
}
#Path("/v1/test2/list")
public Response getTest2() {
}
}
If I do not give #Path annotation at Class level, then this class is not recognized as a REST resource, but I cannot give "/v1" Path for this class since there is already another class with #Path("/v1").
What are possible workaround, to make this class to be recognized as a Rest Resource
Resource classes
A #Path annotation is required to define a resource class. Quoting the Jersey documentation:
Root resource classes are POJOs (Plain Old Java Objects) that are annotated with #Path, have at least one method annotated with #Path or a resource method designator annotation such as #GET, #PUT, #POST, #DELETE.
One possible solution
As already mentioned by Justas, one possible solution is to add the #Path("") annotation to the TestService class. However, it doesn't smell good:
#Path("")
public class TestService {
#GET
#Path("/v1/test1/list")
public Response getTest1() {
...
}
#GET
#Path("/v1/test2/list")
public Response getTest2() {
...
}
}
A better solution
I don't know what your project looks like, but instead of having a single class, I would have two classes, designed as following:
#Path("/v1/test1")
public class TestService1 {
#GET
#Path("/list")
public Response getTest1() {
...
}
}
#Path("/v1/test2")
public class TestService2 {
#GET
#Path("/list")
public Response getTest2() {
...
}
}
You can add empty path #Path("") or #Path("/"). However, this problem may show that you should design your code differently.
The #Path annotation is used to specify the URI through which a resource and an API can be accessed. Resource in this case is the REST Web service itself. Thus this annotation is present at the class level as well as the method level. It is mandatory to annotate a REST Web resource class with the #Path annotation. Thus if a user wants to access the 'Countries' through the resource 'HelloWorld' context, then:
Resource at the class level would have #Path("/"). This is the default annotation at the class level.
Resource at the API level would have #Path("Countries") annotation.
As a best practice, the class-level path annotation should be a noun that qualifies the Web service, and the method-level annotation can be a noun or an action (for example, user and add-user, respectively).
https://www.juniper.net/documentation/en_US/junos-space-sdk/13.1/apiref/com.juniper.junos_space.sdk.help/html/reference/spaceannoREST.html
With the new Version 2.4/2.5 of the Play Framework they moved further towards injecting everything and removing the servers state. play.Play.application() is now deprecated. However, I need the application in my template (e.g. to get all supported languages displayed on all pages with play.i18n.Lang.availables(play.Play.application())).
I'm aware I could:
Pass play.Application explicitly to all of my templates.
Add an implicit parameter to my template like #()(implicit app: play.Application). However, in my Java-Project it's not really implicit, I have to pass it every time I render the template.
Create a Scala object providing the application implicitly. However, this also needs the deprecated play.api.Play.current.
How can I inject play.Application in my templates?
---- Update: ----
What I've tried so far, I created the following setup:
index.scala.html:
#(title: String)
#template(title) { //here is the play.Application obviously missing, however I don't want to pass it in every template - even worse in every Controller <-- this is the core of my question
Welcome to my page!
}
template.scala.html:
#(title: String)(content: Html)(implicit app: play.Application)
<html>
<head>
<title>#title</title>
</head>
<body>
<p>Is live? #app.isProd</p>
#content
</body>
</html>
Controller function:
public Result index() {
return ok(views.html.index.render("home"));
}
You can get an application with Play.current() or inject an application in controller like this is explained in this question. The template should get the argument of type play.Application.
It should be something like this.
The template, let's say is injectappexample.scala.html:
#(app: play.Application)
.... use the app object
The controller:
public class SomeController extends Controller {
Provider<Application> applicationProvider;
#Inject
public SomeController(Provider<Application> applicationProvider) {
this.applicationProvider = applicationProvider;
}
public Result injectAppExample() {
return ok(injectappexample.render(applicationProvider.get());
}
}
It worth to reconsider sending the application object to the template. If you should send a particular configuration property value, inject Configuration in the controller, get the value from configuration object and send it to the template. In this case injecting of application is not needed at all.
The template:
#(value: String)
.... use the value
The controller:
public class SomeController extends Controller {
Configuration configuration;
#Inject
public SomeController(Configuration configuration) {
this.configuration = configuration;
}
public Result injectAppExample() {
return ok(injectappexample.render(configuration.getString("SOME_PROPERTY"));
}
}
I just had to look into this for Play framework 2.6.x. It is possible to inject an object into a template according to the documentation: https://www.playframework.com/documentation/2.6.x/ScalaTemplatesDependencyInjection.
I implemented a simple sample (a bit contrived) and I used scala:
test.scala.html:
#this(configuration: play.api.Configuration)
#(key: String)
config = #configuration.get[Seq[String]](key).mkString(", ")
HomeController.scala
package controllers
import javax.inject._
import play.api._
import play.api.i18n._
import play.api.mvc._
/**
* This controller creates an `Action` to handle HTTP requests to the
* application's home page.
*/
#Singleton
class HomeController #Inject()(cc: ControllerComponents, testView: views.html.test) (implicit configuration: Configuration) extends AbstractController(cc) with I18nSupport{
def test() = Action { implicit request =>
Ok(testView("play.i18n.langs"))
}
}
routes:
GET /test controllers.HomeController.test()
The configuration object gets injected into views.html.test template and the view itself is injected into the controller. Note the #this(configuration: play.api.Configuration) statement in the template. Play will generate a class behind the template with a constructor that is injected the Configuration object.
Please note that the injected configuration into the controller doesn't have any role in this particular code. I experimented with other permutations before I found this solution ... Let's say that if you have an inner template used by an outer template called from the controller, and the inner template needs the configuration object you need to feed the configuration from the controller top down, and add an implicit configuration: play.api.Configuration parameter in all the templates in the hierarchy path right to the template that needs it, something like this: #(message: String)(implicit messagesProvider: MessagesProvider, configuration: play.api.Configuration)
. Then the controller injected configuration is fed to the top template all the way to the template that needs it.
It is generally discouraged to inject the Application itself, because that makes your code very cumbersome to test. Instead, think about what you actually need and inject that directly.
If you have a number of things you need in pretty much every template, my suggestion would be to create some sort of a context class that you can inject in your controller and then pass it on to the template.
First of all, though you are asking how to inject application what you really need is configuration.
You can do something like this in play 2.5. I'll show constructor injection you may use field injection as your requirement.
import com.google.inject.Inject;
import play.Configuration;
public class MyClass{
private Configuration conf;
#Inject
public MyClass(Configuration conf){
this.conf = conf;
}
}
Now you have your configuration class. Then specifically for your requirement posted in your comment, you can do this.
List<Object> langList = conf.getList("play.i18n.langs");
//conf.getList("") returns an object array.
//You can cast to Strings (assuming you are using Java8).
List<String> languages = new ArrayList<>(langList.size());
for (Object object : langList) {
languages.add(Objects.toString(object, null));
}
Now you can have your languages list in languages.
I have some ServerResources in my application which are identified by the #Service(name) annotation. The methods are annotated with the #Get and #Post restlet annotations.
Everything worked fine until recently I wanted to add another ServerResource which has to serve a URL pattern with different parameters and request methods, thus I tried to use #RequestMapping annotation on the methods like:
#Service
public class MyResource extends ServerResource {
#RequestMapping(value="/pathToMyResource/{parameter1}", method=RequestMethod.GET)
public Representation getResponseForGetRequest(Representation entity) {
...
}
//and for the other method:
#RequestMapping(value="/pathToMyResource/{parameter1}/{parameter2}", method=RequestMethod.POST)
public Representation getResponseForPostRequest(Representation entity) {
...
}
...
}
However my resource is not properly registered with org.restlet.ext.spring.SpringBeanRouter as it is not found when this URL is requested.
The only way I figured out multiple paths working for one resource is via XML configuration:
<bean name="/pathToMyResource/{parameter1}"
id="myGetResource"
class="com.mycompany.resource.MyResource"
autowire="byName" scope="prototype">
</bean>
<bean name="/pathToMyResource/{parameter1}/{parameter2}"
id="myPostResource"
class="com.mycompany.resource.MyResource"
autowire="byName" scope="prototype">
</bean>
and using Restlet annotation for methods:
public class MyResource extends ServerResource {
#Get
public Representation getResponseForGetRequest(Representation entity) {
...
}
//and for the other method:
#Post
public Representation getResponseForPostRequest(Representation entity) {
...
}
...
}
Do you know how the #RequestMapping annotation works with Restlet? I would like to avoid XML configuration completely and trying to find a way to get it working with annotations...
As I mentioned I have no problem with resources mapped only to one path like:
#Service("/pathToMyResource/{parameter1}")
public class MyResource extends ServerResource {
...
}
This is working fine... only multiple paths mapping causes problems.
Thanks for any help!
from what I see, we don't support annotations taken from Spring framework.
If you want to remove any xml configuration, you can follow two ways:
use classic Restlet code: define an Application, implement the createInboundRoot methods in order to define the routing aspects, use annotated ServerResource. (see this page for a simple example http://restlet.com/learn/guide/2.2/editions/jse/, or this one for a complete code http://restlet.com/learn/guide/2.2/introduction/first-steps/first-application,
use the JaxRs extension in order to rely on JaxRs annotations (see http://restlet.com/learn/guide/2.2/extensions/jaxrs)