JAX-RS #Path annotation with regular expression doesn't match expected URL - java

I want the URL for my API to have following format for users resource:
http://hostname/webappname/something/something/serviceName/orgs/{id}/users
However, I get a 404 error as the URL mapping fails. But if I have following format, I get the response.
http://hostname/webappname/{id}/something/something/serviceName/orgs/{id}/users
This is my #Path annotation:
#Path("/orgs/{oid: [0-9]+}/users")
But if I have the following #Path annotation:
#Path("/orgs")
With URL's
http://hostname/webappname/{id}/something/something/serviceName/orgs
http://hostname/webappname/something/something/serviceName/orgs
I always get a response back. Can someone tell how should I provide #Path annotation for my resource. Or where am I going wrong?
Users Resource class looks like this:
#Path("/orgs/{oid: [0-9]+}/users")
#Component
public class UserResource extends Resource {
}
Orgs Resource class looks like this:
#Path("/orgs")
#Component
public class OrgResource extends Resource {
}

You should use JAX-RS subresources. In your case, UserResource is a subresource of OrgResource.

I figured out the problem. It had nothing to do with the my #Path annotation. While creating this project I added some old code dependencies. One of then was unsharding filter whose job was to strip of shard present in the URL before passing it to the next filter. It was removing the {oid} from the requested path and hence there was a URL mapping error. I faced this issue only when I changed the URL format for my project.

Related

Why doesn't my spring boot error controller work?

I have a simple spring boot web application (running under Tomcat) and I cannot figure out how to get an error handler to work. So far I've created the web application and controllers and they all work fine. That is, they display Thymeleaf templates. Upon error. I get the default white label error page.
I first disabled this white label error page by adding an EnableAutoConfiguration annotation, thus:
#SpringBootApplication(scanBasePackages="au.com.sample")
#EnableAutoConfiguration(exclude = {ErrorMvcAutoConfiguration.class})
public class SampleWebApp extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(SampleWebApp.class);
}
}
Now, if I browse to an unknown page I get a generic 404 HTML error page containing:
<html>...<body><h1>HTTP Status 404 – Not Found</h1></body></html>
So that step worked. Now when I try and add my own error controller it does not trigger when I browse to an end point that does not exist:
#Controller
#RequestMapping("/")
public class ErrorHandlingController implements ErrorController {
#Override
public String getErrorPath() {
return "/error";
}
#RequestMapping(/"error")
public ModelAndView handleError() {
ModelAndView mav = new ModelAndView();
mav.addObject("title", "Error");
mav.setViewName("layout");
return mav;
}
}
"layout" is my existing thymeleaf template that works fine for my other end points.
I've tried several variations of the error controller, eg. changing the endpoint to "error" (not "/error"), using #RestController instead of #Controller but with no luck. In every case, I get the same generic 404 HTML instead of my hand-crafted template.
Can anyone see what I'm doing wrong or have any tips on how to track down my problem.
To show a custom error page for a specific status is pretty easy in Spring Boot. Just add an <error-code>.html into the src/main/resources/templates/errors directory to have it resolved. Or add a generic error.html as a fallback. See the reference guide for this feature (see also this).
If you want to add your own error handling just add a bean of type ErrorController or if you want to only add attributes add an ErrorAttributes typed bean to your configuration. See this section for more information.
A late answer to the original question of why Spring doesn't call your error controller: I think you are excluding the magic that makes the custom ErrorController work. In a Spring-Boot v2.1.6 app I had to allow (NOT exclude) the ErrorMvcAutoConfiguration class, then it used a custom MyController-implements-ErrorController class on page-not-found situations etc. It seems to be an either-or situation. I think you should only exclude the ErrorMvc.. class if you are using Thymeleaf templates AND you have published template files in a templates/ directory, because in that case you don't really need a custom error controller. HTH.

Spring MVC 1 to 1 mapping of a URL to .HTML without exposing path

I am using Spring 4.1.5 with Boot 1.2 on a webservice that does not serve up any JSPs. I don't want to add a JSP servlet but I want it to serve up a single canary page that shows in a prettier html type format the information that would be provided at the /manage/health endpoint.
I have a file in webapp/canary/canary.html I want to serve this up from the url: www.mywebservice.com:9343/canary, exactly like that, NOT canary.html
I tried doing this:
#Configuration
public class CanaryConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/canary")
.addResourceLocations("/canary/canary.html");
}
}
That doesn't work however.
It is expecting the handler to provide a file name. So in otherwords the location should be something like: /canary/
and the handler would something like: /canary/**
With that, the URL www.mywebservice.com:9343/canary/canary.html would work like a charm.
HOWEVER, I want the URL to resolve www.mywebservice.com:9343/canary to webapp/canary/canary.html without me having to type the html.
This is really easy in a jsp servlet because you can set the suffix ect...
I looked at ResourceResolver but it didn't make sense to me how I would link that into my current configuration.
It looks like what I want:
Provides mechanisms for resolving an incoming request to an actual Resource and for obtaining the public URL path that clients should use when requesting the resource.
See: ResourceResolver Documentation
Any help would be very beneficial.
Also I am very aware that I can put html in the resources/static and several other places that are automatically configured. That always requires the .html to be typed, which is not what I want in this case so that won't work. Thanks!
You can use view controllers to do it. Here is a sample of it. Hope this helps.
public class AppConfig extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/canary").setViewName("/canary/canary.html");
}
}
Note: if you are using tomcat, you might have to configure jsp servlet to server html files.
Related post here.
For information sake, the selected answer is the same as the following:
#Controller
public class CanaryController {
#RequestMapping(value="/canary", method=RequestMethod.GET)
public String getCanary() {
return "/canary/canary.html";
}
}
The above code will work as long as canary(or whatever file/folder) is in your webapp folder.
When I tried this I was trying to set the suffix to .html in my YAML (.yml) file and it wasn't working to I thought that it needed to return to a servlet if it is not a RestController. I was mistaken.

Verifying the path of a web service resource

I am writing some unit tests for a web service written years ago. The root class has a path like:
#Path("v1/path/")
public class RootResource {
...
}
The methods inside the class have their respective path. One working path is:
#GET
#Path("orders/{order_num}.xml")
#Produces(MediaType.APPLICATION_XML)
public Response getXML() {
...
}
This is working fine at root_path/v1/path/orders/123123.xml.
However, there is another method:
#POST
#Path("orders/{order_numer}/status.xml")
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
public Response getStatusXML() {
Logger.info(CALLER, "orderStatusXML", "XML Request received");
...
}
When I try to access this at root_path/v1/path/orders/123423/status.xml I get 404 in return. Even the first line with the logger is missing from the logs.
I am on Weblogic 12 and we used Jersey REST API for these web services.
I have tried a number of things to make sure the path listed in the test case is the correct one. Any hints/ideas on how to check for the correct path?
There is no reason for the root_path/v1/path/orders/123423/status.xml path to give a 404 unless something else is acting on that URL.
Things to try:
look into your web.xml file and see what URL pattern Jersey handles. That particular URL might be handled by some other servlet;
again look into the web.xml and see if you have any filters declared. What URLs do does filters intercept and what do the filters do to the request once intercepted?
this might not be the case but I'll add it anyway... is it a "404 - Not found" that you get back or is it actually a "405 - Method not allowed" that is returned? If you try to access the root_path/v1/path/orders/123423/status.xml URL with a GET, like from the browser, you get 405 because your method is annotated with #POST. Are you using a POST?
My bet is on a filter!

Overriding RolesAllowedDynamicFeature in Jersey 2.5.1

Short and sweet:
I want to be able to filter incoming requests to authenticate the user then take the roles defined in my database and use them in the Jersey 2.5.1 Service classes.
e.g.
#RolesAllowed("Custom1", "Custom2")
#Post
.....
Currently I have the following, which seems to work with the basic #PermitAll and #DenyAll annotations, I am just not sure how to overload/what to overload to get some custom code working with Jersey 2.5.1. I've seen examples for Jersey1. Should I just create a request Filter and do it in there and set the securityContext? Any help would be appreciated.
public class TestApi extends ResourceConfig {
public TestApi() {
super(AuthenticateResource.class);
register(RolesAllowedDynamicFeature.class);
}
}
Figured out my problem. Injected Resource Info then pulled out the annotation. This works if it's not pre-matching
#Context
private ResourceInfo resourceInfo;
Annotation[] annotations = resourceInfo.getResourceMethod().getDeclaredAnnotations();
SecurityContext is either set by the underlying container or it's set manually in your application (usually in ContainerRequestFilter). If your container is sophisticated enough to set the correct security context for you (with correct principal) you can go this way. Otherwise create a custom ContainerRequestFilter similar to the one in Jersey example ContainerAuthFilter.

Spring MVC URL for the AbstractController

I have a controller that inherits from the org.springframework.web.servlet.mvc.AbstractController class
I have configurated it in this way:
<bean name="/gameServiceController.json" class="xx.xxx.GameController"/>
so can accepts url of this form
http://<hostname>:<port>/<context-path>/gameServiceController.json
but the customer has provided to me the requirement to write URL in this way
http://<hostname>:<port>/<context-path>/createNewGame?parameter=<value>
but I think that is not possible to map this type of URL with my controller. Anyone know the type of configuration that can be used in order to configure this type of URL mapping ?
Otherwise, is legal to ask to change the format of the URL in this way
http://<hostname>:<port>/<context-path>/gameServiceController.json?command=createNewGame&phoneNumber=<phoneNumber>
so I can manage the command parameter in the "handleRequestInternal" method of my custom controller that inherits from the org.springframework.web.servlet.mvc.AbstractController class ??
Don't use the legacy Controller framework, use annotated controllers. There, you can easily use URL templates, something like this:
#Controller
public class GameController{
#RequestMapping(value="/createNewGame?parameter={param}",
method=RequestMethod.GET)
public String createNewGame(#PathVariable String param, Model model) {
// do stuff here
return "viewName";
}
}

Categories

Resources