Html5 pushstate Urls on ServiceStack - angularjs

At the moment we're using a default.cshtml view in the root of ServiceStack to serve our AngularJS single-page app.
What I'd like to do is enable support for html5 pushstate (so no hash in the URL), but the only examples I've found so far involve a dependency on MVC with a wildcard route, and pushing the ServiceStack infrastructure to a /api subroute.
We can't take the MVC dependency, so I think we need for accept:text/html requests we need to accept any url and serve up our root application. I'd be happy to remove the default HtmlFormat extension or override it (we could still use it's JsonReport content type it we needed to).
How can I best approach this?

The Order of Operations wiki page shows the number of different hooks you can tap into to inject your own customized behavior as well as the order which they are run.
Hi-jacking requests with RawHttpHandlers
You can by-pass ServiceStack completely by adding a Config.RawHttpHandlers to return a IHttpHandler on requests you want to hi-jack, e.g this is how the built-in mini profiler hi-jacks all requests for files that start with ssr- and returns the physical file:
config.RawHttpHandlers.Add((IHttpRequest request) => {
var file = GetFileNameWithoutExtension(request.PathInfo);
return file != null && file.StartsWith("ssr-")
? new MiniProfilerHandler()
: null;
}
Providing a fallback handler for non-matching routes
If you want to provide a default handler for non-matching route you can register a CatchAllHandlers in AppHost.Configure() or in a plugin with:
appHost.CatchAllHandlers.Add((string method, string pathInfo, string filepath) =>
{
return ShouldProvideDefaultPage(pathInfo)
? new RazorHandler("/defaultpage.cshtml")
: null;
});
Using a wildcard to accept any url in a service
You could create a dummy service and simply return the same single view, e.g:
[Route("/app/{PathInfo*}")]
public class App {
public string PathInfo { get; set; }
}
public class MyService : Service
{
public object Any(App request)
{
return request;
}
}
With the wild card this service will return the view e.g. /View/App.cshtml on any route starting with /app, e.g:
/app
/app/hello
/app/hello/my/name/is?foo=bar
Partial page support
Since partial reloads is related to pushstate I'll also mention the built-in support ServiceStack has for partial reloads.
ServiceStack Docs is an example demo that uses pushstate on browsers that support it, otherwise it falls back to use full-page reloads with browsers that don't.
You can ask for a partial page with ?format=text.bare param, e.g.
Full page: http://www.servicestack.net/docs/markdown/about
Partial page: http://www.servicestack.net/docs/markdown/about?format=html.bare
Partial page markdown: http://www.servicestack.net/docs/markdown/about?format=text.bare
Although this uses Markdown Razor. In the latest ServiceStack.Razor support you can access a partial page with just: ?format=bare

Expanding on my comment. This is what I ended up with trying to host an application in /app while also supporting the virtual file system.
host.CatchAllHandlers.Add((string method, string pathInfo, string filepath) =>
{
if (!Regex.IsMatch(pathInfo, "^/app([/?]|$)"))
return null;
// Serve valid requests as is
var vFile = HostContext.ResolveVirtualFile(pathInfo, null);
if (vFile != null)
return null;
var vDir = HostContext.ResolveVirtualDirectory(pathInfo, null);
if (vDir != null && vDir.GetDefaultDocument() != null)
return null;
// Fallback to default document
var vDef = HostContext.ResolveVirtualDirectory("/app/", null).GetDefaultDocument();
return new CustomResponseHandler((req, res) =>
new HttpResult(vDef.OpenRead(), MimeTypes.GetMimeType(vDef.Name)));
});

Related

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

Hystrix Javanica : Call always returning result from fallback method.(java web app without spring)

I am trying to integrate Hystrix javanica into my existing java EJB web application and facing 2 issues with running it.
When I try to invoke following service it always returns response from fallback method and I see that the Throwable object in fallback method has "com.netflix.hystrix.exception.HystrixTimeoutException" exception.
Each time this service is triggered, HystrixCommad and fallback methods are called multiple times around 50 times.
Can anyone suggest me with any inputs? Am I missing any configuration?
I am including following libraries in my project.
project libraries
I have setup my aspect file as follows:
<aspectj>
<weaver options="-verbose -showWeaveInfo"></weaver>
<aspects>
<aspect name="com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect"/>
</aspects>
</aspectj>
Here is my config.properties file in META-INF/config.properties
hystrix.command.default.execution.timeout.enabled=false
Here is my rest service file
#Path("/hystrix")
public class HystrixService {
#GET
#Path("clusterName")
#Produces({ MediaType.APPLICATION_JSON })
public Response getClusterName(#QueryParam("id") int id) {
ClusterCmdBean clusterCmdBean = new ClusterCmdBean();
String result = clusterCmdBean.getClusterNameForId(id);
return Response.ok(result).build();
}
}
Here is my bean class
public class ClusterCmdBean {
#HystrixCommand(groupKey = "ClusterCmdBeanGroup", commandKey = "getClusterNameForId", fallbackMethod = "defaultClusterName")
public String getClusterNameForId(int id) {
if (id > 0) {
return "cluster"+id;
} else {
throw new RuntimeException("command failed");
}
}
public String defaultClusterName(int id, Throwable e) {
return "No cluster - returned from fallback:" + e.getMessage();
}
}
Thanks for the help.
If you want to ensure you are setting the property, you can do that explicitly in the circuit annotation itself:
#HystrixCommand(commandProperties = {
#HystrixProperty(name = "execution.timeout.enabled", value = "false")
})
I would only recommend this for debugging purposes though.
Something that jumps out to me is that Javanica uses AspectJ AOP, which I have never seen work with new MyBean() before. I've always have to use #Autowired with Spring or similar to allow proxying. This could well just be something that is new to me though.
If you set a breakpoint inside the getClusterNameForId can you see in the stack trace that its being called via reflection (which it should be AFAIK)?
Note you can remove commandKey as this will default to the method name. Personally I would also remove groupKey and let it default to the class name.

Configuring Webpack build for file:// use in CEF

I have to develop a webapp for a CEF-Browser environment. There is no HTTP server available, everything will be served over file:// protocol.
When developing a Webapp nowadays one doesn't get round working with a framework like react/vue for frontend. The standard webpack build scripts of those build a bundle which only works served over HTTP.
Is it possible to configure webpacks build bundle to work on file:// or is there another way to use react or vue via file://?
I'm suggest read CEF wiki more carefully. You are especially interested in https://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage.md#markdown-header-request-handling
In short:
You can register custom scheme handler to serve resources over http+custom fake domain.
You can pack resources in zip for example if you like, or leave them at file system as is (but in that case you can expect that some funny users can edit your files, and then report back unexisting errors back to you).
Important helpers already done (but you can write own when need.)
You can... many other things.
Main thing that "file" scheme are more restricted, and for example you can't do XHR requests. But for custom handler you can. Even if dynamic loader for some reason use XHR instead DOM-based loading it will work again same as on http without touching network.
cefclient itself also has usage of custom schemes. Check URL of Tests->Other... in menu. :)
PS: Sorry that my answer doesnt have direct answer for your question. But, custom resource handling in CEF is so common, that i'm just should say about.
fddima is right - you don't need to configure your webpack (although it would be theoretically possible). Instead you can use custom scheme handler in CEF. I made it work with angular at work.
I wrote blog post on how to serve web application via 'file' protocol in CEF.
What you want to add is your scheme handler and its factory:
using System;
using System.IO;
using CefSharp;
namespace MyProject.CustomProtocol
{
public class CustomProtocolSchemeHandler : ResourceHandler
{
// Specifies where you bundled app resides.
// Basically path to your index.html
private string frontendFolderPath;
public CustomProtocolSchemeHandler()
{
frontendFolderPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "./bundle/");
}
// Process request and craft response.
public override bool ProcessRequestAsync(IRequest request, ICallback callback)
{
var uri = new Uri(request.Url);
var fileName = uri.AbsolutePath;
var requestedFilePath = frontendFolderPath + fileName;
if (File.Exists(requestedFilePath))
{
byte[] bytes = File.ReadAllBytes(requestedFilePath);
Stream = new MemoryStream(bytes);
var fileExtension = Path.GetExtension(fileName);
MimeType = GetMimeType(fileExtension);
callback.Continue();
return true;
}
callback.Dispose();
return false;
}
}
public class CustomProtocolSchemeHandlerFactory : ISchemeHandlerFactory
{
public const string SchemeName = "customFileProtocol";
public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request)
{
return new CustomProtocolSchemeHandler();
}
}
}
And then register it before calling Cef.Initialize:
var settings = new CefSettings
{
BrowserSubprocessPath = GetCefExecutablePath()
};
settings.RegisterScheme(new CefCustomScheme
{
SchemeName = CustomProtocolSchemeHandlerFactory.SchemeName,
SchemeHandlerFactory = new CustomProtocolSchemeHandlerFactory()
});

MSF4J: Serving static content

Can MSF4J application serve static content without using the Mustache template engine. I have developed a REST service which will be consumed by an already developed angular web app. Now I need to package the same angular app with the micro service so it will render in the browser and will consume the service via ajax calls.
MSF4J does not directly support serving static content. From your question what I understood is that you want to point the MSF4J server to a directory and serve resources in that directory by their relative path or something similar. In this case what you can do is to write an MSF4J service method with a wildcard path and serve the static content that matches the path of the request.
#Path("/")
public class FileServer {
private static final String BASE_PATH = "/your/www/dir";
#GET
#Path("/**")
public Response serveFiles(#Context Request request) {
String uri = request.getUri();
System.out.println("Requested: " + uri);
File file = Paths.get(BASE_PATH, uri).toFile();
if (file.exists()) {
return Response.ok().entity(file).build();
} else {
return Response.status(404).entity("<h1>Not Found</h1>").build();
}
}
}

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