spring mvc catch all route but only unknown routes - angularjs

I've got a spring boot app with angular on the frontend.
I'm using ui-router with html5 mode and I would like spring to render the same index.html on all unknown routes.
// Works great, but it also overrides all the resources
#RequestMapping
public String index() {
return "index";
}
// Seems do be the same as above, but still overrides the resources
#RequestMapping("/**")
public String index() {
return "index";
}
// Works well but not for subdirectories. since it doesn't map to those
#RequestMapping("/*")
public String index() {
return "index";
}
So my question is how can i create a fallback mapping but that lets through the resources?

The simplest way I found was implementing a custom 404 page.
#Configuration
public class MvcConfig {
#Bean
public EmbeddedServletContainerCustomizer notFoundCustomizer(){
return new NotFoundIndexTemplate();
}
private static class NotFoundIndexTemplate implements EmbeddedServletContainerCustomizer {
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/"));
}
}
}
Neil McGuigan propopes a HandlerInterceptor, but I wasn't able to understand how that would be implemented. I't would be great to see how this would be implemented, as single page applications using html5 history push state will want this behaviour. And I have not really found any best practices to this problem.

Define an entry point for all the urls in your web.xml file like following:
<error-page>
<error-code>404</error-code>
<location>/Error_404</location>
</error-page>
This would catch all 404 i.e page not found errors and throw /Error_404 url, catch it in your controller and push to desired place.

You could handle all of non-matched requests in 404 handler. Take a look at this, there are several options
Another thing you could do is to override DefaultAnnotationHandlerMapping, and to add a sort of catch-all controller by setting the defaultHandler property.
public void setDefaultHandler(Object defaultHandler)
Set the default handler for this handler mapping. This handler will be returned if no specific mapping was found.
Default is null, indicating no default handler.

try to use #ExceptionHandler in your controller, change Exception.class by the class of the exception who you want to handle.
#ExceptionHandler(value = {Exception.class})
public String notFoundErrorHandler() {
return "index";
}

Related

Explicitly define Camel Routes in Quarkus

Current behaviour: When I'm running a Quarkus App with Camel it automatically starts all the RouteBuilder Extensions as Routes.
What I want to achieve: On startup only the Routes that I configured are started.
What I tried:
With the following snippet it's possible to explicitly start the CamelMainApplication but I dont know how to get control over e.g. the CamelContext at this point where I would be able to configure my routes.
#QuarkusMain
public class Main {
public static void main(String[] args) throws Exception {
Quarkus.run(CamelMainApplication.class, args);
}
}
On the Route I can use .noAutoStartup() to disable the route on startup. But this means that it's not the default for all routes to be disabled at first and second I don't know where to activate them as I don't know where in a Quarkus App I can get a hand on the Camel Context to activate the route.
With the following in my application.yml I can disable the automatic route discovery but then the remaining question is how I can manually start the route, e.g. in my QuarkusMain class.
quarkus:
camel:
routes-discovery:
enabled: false
I think it is best way. Quarkus has properties for include and exclude route as pattern. this properties is List You can add one to N
quarkus.camel.routes-discovery.exclude-patterns=tes.Comp,tes.package.MyRoute
quarkus.camel.routes-discovery.include-patterns=test.mypackage.XX
I had this same problem I ended up doing something like the following:
#QuarkusMain
public class Main implements QuarkusApplication {
#Inject
OrderService orderService;
#Override
public int run(String... args) throws Exception {
CamelRuntime runtime = Arc.container().instance(CamelRuntime.class).get();
runtime.start(new String[]{});
orderService.handleOrders(args[0]); //this would inject the camelContext and start the route.
return 0;
}

Quarkus extension - How to have an interceptor working on beans provided by the extension itself

I'm working on a Quarkus extension that provides an interceptor (and its annotation) to add some retry logic around business methods this extension offers. Nothing new in there, and this is working when i annotate a public method of a bean in an application that uses this extension.
But the extension also provides some #ApplicationScoped beans that are also annotated, but the interceptor is not intercepting any of these.
Seems like an interceptor does not check / apply on the extension itself.
I would like to know if this is an intended behavior, or an issue in my extension setup, and if so how to fix it. Could not find anything about this in the documentation, but there is so much dos that i may have missed something.
Any idea about this ?
I finally found a way to make this work.
I was using a producer bean pattern to produce my beam as an #ApplicationScoped bean inside the extension.
#ApplicationScoped
public class ProxyProducer {
#Produces
#ApplicationScoped
public BeanA setUpBean(ExtensionConfig config)
{
return new BeamsClientProxy(new InternalBean(config.prop1, config.prop2));
}
}
with the following BeanA class (just an example)
public class BeanA {
private final InternalBean innerBean;
public BeanA(final InternalBean innerBean) {
this.innerBean = innerBean;
}
#MyInterceptedAnnotation
public void doSomething() {
}
}
Due to this setup, the bean is not considered by the interceptor (i guess because it's produced only the first time it's used / injected somewhere else)
Removing the producer pattern and annotating directly the BeanA fixed the issue.
Example:
#ApplicationScoped
public class BeanA {
private final InternalBean innerBean;
public BeanA(final ExtensionConfig config) {
this.innerBean = new InternalBean(config.prop1, config.prop2);
}
#MyInterceptedAnnotation
public void doSomething() {
}
}
with of course adding the following lines to register the bean directly on the extension processor:
#BuildStep
AdditionalBeanBuildItem proxyProducer() {
return AdditionalBeanBuildItem.unremovableOf(BeanA.class);
}
As a conclusion:
Changing the bean implementation to avoid the producer-based bean use case solved my issue (please refers to Ladicek comment below)
Edit:
As Ladicek explained, Quarkus doesn't support interception on producer-based beans.

Load Freemarker Template from External Url

I have implemeted spring boot app where we need to send email using freemarker.
App is going to deployed on google app engine, where file structure is not available to store the templates. So, I saved templates on google storage with public access. But not able to load in freemarker template engine.
freeMarkerConfiguration.setDirectoryForTemplateLoading(new File("/home/dnilesh/Downloads/helloworld-springboot/src/main/resources/"));
content.append(FreeMarkerTemplateUtils.processTemplateIntoString(
freeMarkerConfiguration.getTemplate("Email.html"),model));
This above configuration will work on development env. But on Google app engine I dont have directory to store template.
I tried this :
freeMarkerConfiguration.setDirectoryForTemplateLoading(new File("https://storage.googleapis.com/nixon-medical/"));
content.append(FreeMarkerTemplateUtils.processTemplateIntoString(
freeMarkerConfiguration.getTemplate("Email.html"),model));
But freemarker not loading Template from External URL. How can I load this?
For external URL,you should use URLTemplateLoader:
If your template source accesses the templates through an URL, you needn't implement a TemplateLoader from scratch; you can choose to subclass freemarker.cache.URLTemplateLoader instead and just implement the URL getURL(String templateName) method.
See code sample
Though there is an accepted answer, I did not find the integration with spring boot. So I have done this
I was trying to read Freemarker template from google cloud storage with spring boot application.
So, I have done the following and it worked for me.
Implement URLTemplateLoader and only override getURL method On
FreeMarkerConfigurer bean, set pretemplate as custom template
CloudTemplateLoader - my custom loader
public class CloudTemplateLoader extends URLTemplateLoader {
private URL root;
public CloudTemplateLoader(URL root) {
super();
this.root = root;
}
#Override
protected URL getURL(String template) {
try {
return new URL(root, "/" + template);
} catch (MalformedURLException e) {
e.printStackTrace();
}
return null;
}
}
FreeMarkerConfigurer Bean to set my custom loader
#Bean
public FreeMarkerConfigurer freeMarkerConfigurer() throws MalformedURLException {
FreeMarkerConfigurer freeMarkerConfigurer = new FreeMarkerConfigurer();
Properties properties = new Properties();
properties.setProperty("localized_lookup", "false");
freeMarkerConfigurer.setFreemarkerSettings(properties);
freeMarkerConfigurer.setPreTemplateLoaders(new CloudTemplateLoader(new URL("https://storage.googleapis.com")));
freeMarkerConfigurer.setDefaultEncoding("UTF-8");
return freeMarkerConfigurer;
}
And my controller is following
#GetMapping
public String index() {
return "<bucket-name>/index.ftl";
}
Don't forget to upload the template on the google cloud or s3. For test purpose, I added public access on my index.ftl file.
You can use a Thymeleaf resolver to load the external files.
https://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html

Spring Controller sometimes has null #Autowired objects and seems not to be managed by Spring Context

I have a Spring controller defined like this with 2 request mappings, one using localDAO and the other using dependencyDAO. LocalDAO classes exist in my project and DependencyDAO classes are imported via maven dependency:
#RestController
#PreAuthorize("hasRole('USER')")
public class MyController
#Autowired
private localDAO LocalDAO; // dao classes exist in my project
#Autowired
private DependencyDAO dependencyDAO; // dao classes imported via maven dependency
...
#RequestMapping("/getUsingLocalDAO")
private String getUsingLocalDAO(
#JsonProperty("param") String param) {
localDAO.get(param) ... // <== this never null
}
#RequestMapping("/getUsingDependencyDAO")
private String getUsingDependencyDAO(
#JsonProperty("param") String param) {
dependencyDAO.get(param) ... // <== this always null
}
...
My dao beans are defined in another class:
#Configuration
public class DaoBeans {
#Bean
public LocalDAO localDAO() throws Exception {
return new LocalDAOImpl();
}
#Bean
public DependencyDAO dependencyDAO () throws Exception {
return new DependencyDAOImpl();
}
...
I am doing an $http.post from Angular like this:
$http.post('getUsingLocalDAO', $.param($scope.parameters), {
headers : {
"content-type" : "application/x-www-form-urlencoded"
}
}).success(function(data) {
...
}).error(function(data) {
...
$http.post('getUsingDependencyDAO', $.param($scope.parameters), {
headers : {
"content-type" : "application/x-www-form-urlencoded"
}
}).success(function(data) {
...
}).error(function(data) {
...
Both posts are identical except for the method they execute.
When stepping through the debugger I can see all the dao beans being created.
When I call getUsingLocalDAO everything works as expected.
But, when I call getUsingDependencyDAO every #Autowired object is null.
I believe I am getting different instances of MyController. One managed by Spring and one not; or at least not instantiated properly.
I make these calls in succession. It doesn't matter what order they are in.
I tried injecting the servlet context via #Autowired to get the bean manually but it is always null in getUsingDependencyDAO as well.
I tried using application context aware and although I see the context setter being set in the debugger the context is always null in getUsingDependencyDAO.
If I wrap the two calls in a third request mapping like so everything works well (no null objects).
#RequestMapping("/getUsingBothDAO")
private String getUsingBothDAO(
#JsonProperty("param") String param) {
getLocalDAO(param);
getDependencyDAO(param);
...
}
I am using Spring-Boot 4.1.5. My project is 100% annotation driven and has no .xml configurations. The only difference between the two request mappings is that one uses a bean from a dependency and one does not.
I have been searching for an answer to this problem for 3 days and have not found anything close to what I am experiencing.
Can anyone shed some light as to what I am doing wrong? Any help would be greatly appreciated.
Thanks.
Ok, I solved the problem. My example code above is not entirely accurate. The request method that was giving me nulls was defined as a private method while the one that worked was defined as public as its supposed to be. Originally the private method was not a request method and that modifier remained after the change. I changed it to public and everything is working.
It was just coincidence that the private method was from an imported project. It's curious that Spring did not throw an error that the request mapping didn't exist on the private method or something to that effect.
Thanks to anyone who looked at this and was trying to figure it out.

How can I get AngularJS working with the ServiceStack FallbackRoute attribute to support HTML5 pushstate Urls?

I am building a client/server solution, using an AngularJS Single Page App as the client component and a Self-Host ServiceStack RESTful API as the server component. A single Visual Studio Console Application Project holds HTML and JavaScript files for the AngularJS component, along with C# classes for bootstrapping the ServiceStack AppHost (I have devolved Interface and Service responsibilities to separate Visual Studio Projects).
I have set all HTML and JavaScript files to have a 'Build Action' of 'None' and a 'Copy to Output Directory' of 'Copy if newer'.
Everything is working very well as long as I am prepared to put up with having a '#' in my site URLs. I would like to eliminate this by using HTML5 pushstate URLs.
Effectively this means I need to persuade ServiceStack to serve up my default Single Page App HTML shell page whenever a non-existent route is requested. There is now a FallbackRoute attribute available in ServiceStack which appears to have been added exactly for this purpose.
However, I am unsure how to use it. I have found people asking similar questions here, here and here. But the answers given were all before the new FallbackRoute attribute arrived.
Essentially, I am looking for a simple, yet complete example of how to use the FallbackRoute attribute to ensure any requests to non-existent routes are redirected to a single static HTML page.
The RazorRockstars.Web has an implementation. I'll modify it to use a wildcard path and a default view:
[FallbackRoute("/{Path*}")]
public class Fallback
{
public string Path { get; set; }
public string PathInfo { get; set; }
}
public class RockstarsService : Service
{
[DefaultView("Index")]
public object Any(Fallback request)
{
request.PathInfo = base.Request.PathInfo;
return request;
}
// ...
}
Since this is a service it requires a View page (details here) rather than a content page.
In the RockStars example, I can't determine what view would be rendered for the FallBackResponse, but setting the view explicitly should be all you need.
The [DefaultView("Index")] attribute I added to the Any method maps the response to a Views/Index.cshtml file. The Index.cshtml file can be empty but for a template declaration, and the complete markup for your single page app can be in your template file (i.e. _Layout.cshtml)
Without Razor
Read the html into a string and return it, while setting the content type to "text/html" with an attribute, see wiki docs on service return types
public class RockstarsService : Service
{
static string readContents;
[AddHeader(ContentType = "text/html")]
public string Any(Fallback request)
{
// check timestamp for changes for production use
if (readContents == '') {
using (StreamReader streamReader = new StreamReader(pathFromConfigFile, Encoding.UTF8))
{
readContents = streamReader.ReadToEnd();
}
}
return readContents;
}
// ...
}
It turns out it is all very simple with the FallbackRoute functionality, once you work out how to use it properly:
[FallbackRoute("/{Path*}")]
public class Fallback
{
public string Path { get; set; }
}
public class FallBackService : Service
{
public object Any(Fallback request)
{
return new HttpResult(new FileInfo("index.html")) {ContentType = "text/html"};
}
}
Once this is in place, I find 'index.html' is indeed getting served up whenever I try to hit a non-existent route.
Any static files, such as JavaScript and CSS resources, get served up as normal (as long as they have a 'Copy to Output Directory' setting of 'Copy if newer', of course).
This works like a charm with the HTML5 Push-state functionality in AngularJS.

Resources