Spring Requestmapping with condition based on server-side parameter - java

I have a controller (Spring Controller) that will serve json to a mobile app.
Various servers run our software. Not all will be configured to serve mobile requests.
I've added a bean of class Integer with the id of an object containing various system parameters necessary to handle requests.
This bean is defined in xml and autowired into the controller.
The autowiring uses (required = false) so we can run without a value defined in xml.
I have checked and found that the autowired Integer is indeed null if not defined in xml.
What I would like to do now is add to my requestmappings in a way that will match one method if that Integer is null and the regular method when the Integer is not null (basically, we'll reply with a standard json error object).
This seems like it'd be pretty straightforward with some sort of AOP, but I've little experience aside from using Spring. Most conditional info in the Requestmapping annotation seems to be based on request parameters, not server-side variables.
Any ideas?

I think that is better use a property-placeholder to load a properties file from the classpath. In this way you can deploy the same war file in different servers and use a different property file for each server (putting it in the AS classpath).
Once you did it you can use a variable in your controller (or in an interceptor if you want to leave the controller's code clean) and do something like this:
#Controller
public class MyController{
#Value("${mobile.enabled}")
private boolean mobileEnabled;
#RequestMapping("/mobile")
public Object json(){
if (!mobileEnabled)
throw new IllegalStateException("This server can't do it!");
}
//create the json
return result;
}
And a properties file like:
mobile.enabled=true
when you want to enable it, or false when don't.

Related

Is there a way to define queryparams for all endpoints in javax.ws.rs?

I am trying to document an already existing application using javax.ws.rs annotations to define what headers (#HeaderParam) and parameters (#QueryParam) a specific endpoint needs. This information would them be used to generate a swagger page for the application.
public Response SampleFunction(#RequestBody(...),
#QueryParam(...),
#HeaderParam(...),
#HeaderParam(...),
#HeaderParam(...),
etc etc etc){
return doStuff()
}
I have identified a set of "#HeaderParam" which are required for all endpoints.
I need to know if there is any way for me to define the #HeaderParam only once and use that definition for all endpoints and, since this is an already existing application, I need to do this change without any major code refactorization.
We believe to have found a solution for this matter.
By declaring the #HeaderParam globally they appear for all endpoints without having to repeat the declaration for each endpoint.
Something like this:
#Path("/")
public class myClass{
#HeaderParam("Parameter_one")
#Parameter(example = "example_one)
Type parameter_one
#HeaderParam("Parameter_two")
#Parameter(example = "example_two)
Type parameter_two
public Response SampleFunction(#RequestBody(...),
etc etc etc){
return doStuff()
}
}
In this particular case, Parameter_one and Parameter_two will become available on the Swagger page for all endpoints.

Map spring rest controller to a path without the servlet context

I have a spring boot application with the following context path:
server.servlet.context-path:/api
I need to write a rest controller that's mapped to
http://localhost:8080/logout
instead of http://localhost:8080/api/logout
Is there a way to achieve this? changing the "server.servelt.context-path" value is not an option.
this is what I tried and didn't work:
#GetMapping(value="../signout"){
public void logout(){
}
Nero, you say you can't change the "server.servlet.context-path" value. I bet you say this because you don't want to break the API, but I think you can manage to change this without breaking the API. Set the context-path to blank, which is permitted. Then in your application change the "api" mapping, which I assume is currently "/", to "api".
Change server.servlet.context-path:/api to server.servlet.context-path:/ or maybe server.servlet.context-path: (no slash). (Supposedly this is the default so you might just remove this entry altogether.)
Somewhere in your application change #RequestMapping("/") to #RequestMapping("/api").
Now you can also have #GetMapping(value="/signout") and you will have resources at http://localhost:8080/logout and http://localhost:8080/api.
I don't know what mapping annotations you happen to be using, but hopefully this is clear enough.
It may not be possible within that application to go outside its context root. Maybe you can create a separate Rest service app for that particular url and take it from there.

How to use Basic Authentication with Spring's Resource abstraction

I have a file on a server available via Https I want to access using Spring's Resource abstraction. I want Spring to resolve the resource and inject it into the constructor of my Bean like this:
public class MyClass {
public MyClass(
#Value("https://myserver.com/myfile") Resource resource) {
// do something using the resource
}
}
The issue is that I cannot figure out how to include the username and password for basic authentication into this pattern. I tried the "common" style
#Value("https://username:password#myserver.com/myfile")
but it looks like this is not understood correctly. The server is responding with HTTP status 401 - Unauthorized. I copied the string and perfomed the same query using wget and it worked. So there is no issue with my credentials but most likely with the syntax used to define the resource.
Is there a valid syntax for this in Spring or must I fetch the config in an alternative way setting the Authentication header by hand?
This feels wrong, and I'd prefer it if you didn't do it this way...but you can rely on #Value to inject the property value. Note the use of #Autowired here.
#Component
public class MyClass {
private String resourceUrl;
#Autowired
public MyClass(#Value(${external.resource.url}) String resourceUrl) {
this.resourceUrl = resourceUrl;
}
// The rest of your code
}
Then you could place into the property external.resource.url whichever value you liked...including your full URL with username and password.
I want to call attention that this is probably not a desirable thing to do, since you want to be able to inject the URL, username and password as separate things into your application. This gives you an idea of how you can accomplish it with one component, and while I strongly encourage you to split this up instead (and whatever you do, do not check the properties file in with those values into your source control), I leave the mechanical part of splitting this into more values as an exercise for the reader.

Is it possible to map a #RestController only if it isn't already mapped?

So I have a controller mapped to /App/Health and making some changes to the functionality, moving it into its own project to allow for just including it as a dependency rather than having to set it up manually.
However, a few services already have it set up and if possible, I'd like the new controller to only create itself only if the path isn't mapped by the old one..
Existing:
#RestController
#RequestMapping("/App")
public class ApplicationController {
#RequestMapping(value="/Health", method=RequestMethod.GET)
public String health() {
return "Ok!";
}
}
New:
#RestController
public class HealthCheck {
#RequestMapping("/App/Health")
public String health() {
return "Ok!";
}
}
So, given both of these existing in two different packages, is it possible to make "New" optionally instantiate if the "Existing" doesn't?
If you distribute your code as a lib, using package-scan for it (and I guess that's the problem) is a weird. The app should control what beans it creates.
So you can setup beans manually via XML or Java config and if there is a controller that does the job, just don't create a bean for the one from the lib.
As it turns out, the above code just works.
Because the first defines RequestMethod.GET while the new one doesn't, which results in the new one mapping to all methods Except anything already bound specifically.
I added another endpoint that maps to everything BUT the RequestMethod.GET and returns an error, leading to the new one, which maps to "Everything available" either maps to the GET if the existing one doesn't, or to Nothing if the existing one does.

Can you specify preferred default media type for a single path in Spring MVC?

I have a Jersey application which has been converted to Spring MVC. One piece of functionality that I don't see a way to port directly over is the ability, per path, to specify the preferred media type if none is specified. In Jersey, I could specify the "qs" property on the media type, and it would use that to determine which response type to send if none were specified (or if multiple options were specified in the Accept header, I believe this value was multiplied by the quality scores specified).
#Produces("application/json")
#GET
#Path("/some/path")
public Response preferredResponseType() {
//Implementation goes here
}
#Produces({"application/schema+json;qs=0.9"})
#GET
#Path("/some/path")
public Response otherResponseType() {
//Implementation goes here
}
In this example, if I do a GET request against /some/path with no Accept header, it will return the application/json response.
I don't see any easy way to do this in Spring MVC, particularly not if I want to restrict the default to applying to just that one endpoint (there are other endpoints in the app that should have a different preferred default). I do see that there is a way to globally set a default content type (per the "defaultContentType" and "defaultContentTypeStrategy" methods in ContentNegotiationConfigurer), but that does not easily address the per-path use case.
Is there an easy way to achieve per-path media type defaults different from the application global default?
Spring issue 19050 requests this functionality. Per the conversation there, it looks like there is no simple way to declaratively specify the default content type to use. Furthermore, the Spring team has closed the issue with a decision not to implement this functionality.
The "defaultContentTypeStrategy" allows to provide your own ContentNegotiationStrategy to use. It has access to the full request so you can make path based decisions possibly with the an AntPathMatcher to support patterns easily.

Categories

Resources