I am attempting to migrate an application from JSF 1.2 to JSF 2.1. The code below worked in the 1.2.
I am using PrettyFaces 3.3.3, MyFaces 2.1.
in pretty-config.xml:
<url-mapping id="seSite">
<pattern value="/sites/#{seViewChooserBean.urlSiteType}/#{seViewChooserBean.siteId}"/>
<view-id value="#{seViewChooserBean.getSiteViewId}"/>
</url-mapping>
<url-mapping id="seSiteProps">
<pattern value="/sites/#{sePropsBean.urlSiteType}/#{sePropsBean.siteId}/properties"/>
<view-id value="/pages/se/site/props.xhtml"/>
<action>#{sePropsBean.init}</action>
</url-mapping>
I have a request with URL: http://example.com/myapp/sites/object/309847
This request successfully matches the url mapping id "seSite" and getSiteViewId is invoked on seViewChooserBean and returns the result "pretty:seSiteProps". I have debugged and confirmed this. For your reference this is the bean code for ViewChooserBean.java:
public String getSiteViewId() {
if (siteType == SiteType.TYPE) {
// redirect to tag list view
initSiteBean("seTagListBean", TagListBean.class);
return "pretty:seTagList";
}
else {
// redirect to site properties view
initSiteBean("sePropsBean", PropertiesBean.class);
return "pretty:seSiteProps";
}
}
After that prettyfaces then attempts to forward to the new view id seSiteProps but the new generated URL is not processed by pretty faces because (from the logs): "Request is not mapped using PrettyFaces. Continue."
So I get 404 response for URL http:://example.com/myapp/sites/object/309847/properties.
Note that this url match to view id seSiteProps.
I have debugged this up into the pretty faces filter and discovered the following:
After the initial request for http://example.com/myapp/sites/object/309847, the DynaviewEngine.processDynaView is invoked and generates the correct target url http:://example.com/sites/object/309847/properties and forwards via faces request.
Then, with a breakpoint in PrettyFilter.doFilter() I observed the following:
In PrettyFilter.doFilter() method: isUrlMappingForward(req) returns false, therefore request is not processed by prettyfaces. Why??
// isUrlMappingForward returns false. The request has url http:://example.com/myapp/sites/object/309847/properties on it.
if (!isUrlMappingForward(req))
{
mapping = getConfig().getMappingForUrl(url);
}
Also, note that if I put the request http:://example.com/myapp/sites/object/309847/properties directly in the browser the page IS processed by prettyfaces and isUrlMappingForward(req) returns true and it loads correctly in the browser.
I was thinking I've missed something obvious here as the problem hasn't been reported elsewhere as far as I can tell. Any help is greatly appreciated. Thanks.
Brett
Actually I'm very surprised that returning PrettyFaces navigation strings from dynaview methods ever worked. This isn't documented anywhere and I doubt that this has been tested in detail. So basically you are using the dynaview feature in an very weird way.
So I recommend to return plain JSF view IDs instead which should work fine. See the documentation for details:
http://ocpsoft.org/docs/prettyfaces/3.3.3/en-US/html/Configuration.html#config.dynaview
Related
So I followed the Kontent doc from the github which allows to retrieve content from a link (https://github.com/Kentico/kontent-delivery-sdk-net/wiki/Resolving-links-to-content-items)
First I implement a resolver to redirect when we click on the link like this :
public class CustomContentLinkUrlResolver : IContentLinkUrlResolver
{
public string ResolveBrokenLinkUrl()
{
return "/404";
}
public string ResolveLinkUrl(ContentLink link)
{
switch(link.ContentTypeCodename)
{
case "author":
return $"/author/{link.UrlSlug}";
default:
return $"/not_found";
}
}
}
Then I register my resolver within a IDeliveryClient
client = DeliveryClientBuilder
.WithProjectId(myid)
.WithContentLinkUrlResolver(new CustomContentLinkUrlResolver())
.Build();
At this moment if i click on the link it will redirect to /author/linkName with an error on the page what I think is normal
I don't get the last part of the doc (how just by doing a getString on the contentItem the link will work ?) so I would like to know how to display the content on the redirect page
I don't know if i was clear enough and sorry for my english
Here is the error thrown on the redirect page
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
The last part of the wiki article refers to something that you already have:
At this moment if i click on the link
If you have a link that you can click on, then you have done what that part of the article describes.
What you need is to resolve the request. If you are getting a 404 that you expect, then you know that you need to add a route to your application to handle the request. In the handler (a controller, a component, etc.) extract the urlSlug from the route and use it with a IDeliveryClient to retrieve the item and then render the content. You will need to filter the GetItems call with something like new EqualsFilter("elements.urlSlug", urlSlug).
I am using Primefaces DialogFramework with
Primefaces 5.0
Mojarra 2.1.27
Glassfish 3.1.2.2 Build 5
My problem is, that if the user knows the location of my dialog, he is able to access it directly via the URL. I do not want that to be possible, so I thought it would be able to put the dialog in WEB-INF folder of my web-app, but now, if I want to open the dialog, I get a FileNotFound-Exception.
If my dialog is located in some regular folder, it works fine
RequestContext.getCurrentInstance().openDialog("/myfolder/mydialog");
// this works as expected
but if it is located in WEB-INF, it does not work any longer
RequestContext.getCurrentInstance().openDialog("/WEB-INF/mydialog",options,null);
// this is causing a fileNotFoundException
I also tried to set up a navigation rule for this in faces-config but again with no success
<navigation-case>
<from-outcome>mydialog</from-outcome>
<to-view-id>/WEB-INF/mydialog.xhtml</to-view-id>
<redirect />
</navigation-case>
How may I open dialogs located in WEB-INF folder, or is it not possible at all?
Thanks in advance
Unfortunately, putting PrimeFaces Dialog Framework dialogs in /WEB-INF in order to prevent direct access is indeed not going to work. The dialogs are loaded entirely client side. On the POST request which opens the dialog, JSF/PrimeFaces returns an oncomplete script with the (public!) URL of the dialog to JavaScript/jQuery, which in turn shows a basic dialog template with an <iframe> whose URL is set to the dialog URL, which in turn loads the content. In effects, 2 requests are being sent, the first to get the dialog's URL and the second to get the dialog's content based on that URL in the <iframe>.
There's no way to keep the dialog in /WEB-INF without falling back to the "traditional" dialog approach via <p:dialog> and conditional display via JS/CSS. There's also no way in the server side to verify based on some headers if the request is coming from an <iframe>, so that all others could simply be blocked. Your closest bet is the referer header, but this can be spoofed.
One way to minimize abuse is checking the presence of pfdlgcid request parameter (identified by Constants.DIALOG_FRAMEWORK.CONVERSATION_PARAM) when a dialog is being requested. PrimeFaces namely appends this request parameter representing "conversation ID" to the dialog URL. Presuming that all dialogs are stored in a folder /dialogs, then you could do the job with a simple servlet filter. Here's a kickoff example which sends a HTTP 400 error when /dialogs/* is being requested without the pfdlgcid request parameter.
#WebFilter("/dialogs/*")
public class DialogFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String id = request.getParameter(Constants.DIALOG_FRAMEWORK.CONVERSATION_PARAM);
if (id != null) {
chain.doFilter(req, res); // Okay, just continue request.
}
else {
response.sendError(HttpServletResponse.SC_BAD_REQUEST); // 400 error.
}
}
// ...
}
However, the abuser might not be that stupid and discover the pfdlgcid request parameter during the normal flow and still be able to open the dialog individually when supplying that parameter, even with a random value. I thought of comparing the actual pfdlgcid value to the known ones. I checked the PrimeFaces DialogNavigationHandler source code, but unfortunately, PrimeFaces doesn't store this value anywhere in the session. You'd need to provide a custom DialogNavigationHandler implementation wherein you store the pfdlgcid value in the session map which in turn is also compared in the servlet filter.
First add the following method to the DialogFilter:
public static Set<String> getIds(HttpServletRequest request) {
HttpSession session = request.getSession();
Set<String> ids = (Set<String>) session.getAttribute(getClass().getName());
if (ids == null) {
ids = new HashSet<>();
session.setAttribute(getClass().getName(), ids);
}
return ids;
}
Then copypaste the PrimeFaces DialogNavigationHandler source code into your own package and add the following line after line 62:
DialogFilter.getIds((HttpServletRequest) context.getExternalContext().getRequest()).add(pfdlgcid);
Replace the <navigation-handler> in faces-config.xml with the customized one.
Finally, alter the if condition in the DialogFilter#doFilter() method as follows:
if (getIds(request).contains(id)) {
// ...
}
Now, this prevents the abuser from attempting to open the dialog with a random ID. This however doesn't prevent the abuser from attempting to open the dialog by copypasting the exact <iframe> URL immediately after opening it. Given the way how the PrimeFaces dialog framework works, there's no way to prevent that. You could at most remove the pfdlgcid value from the session when the dialog is about to returns to the parent. However, when the dialog is closed by pure JS means, then this is also bypassed.
All in all, if you really, really, want to avoid the enduser being able to open the dialog individually, then you can't go around the "traditional" <p:dialog> approach.
Ok. I have a url setup to log a user out. On the server, there is no html. The session on the server simply gets destroyed, and then the user is redirected to an address.
This works fine with plain html, but with Angular i am having issues. I've been routing all main routes using $routeProvider.when('/foo', {templateUrl: '/foo.html', controller: 'Ctrl'}) and that works fine for normal templated routes.. however, if there is no template it will not work.
So, how do i support the route /logout in the same fashion as above, when there is no html template?
A workaround is to use template instead of templateUrl. From the Angular docs:
template – {string=} – html template as a string that should be used
by ngView or ngInclude directives. this property takes precedence over
templateUrl.
This can be used as follows:
$routeProvider.when("/foo", {template: " ", controller: "Ctrl"});
Note: You must use " " instead of an empty string "" because Angular uses an if (template) check before firing the controller, and an empty string evaluates to false.
-- EDIT --
A better way to do it is to use the resolve map. See the Angular Docs:
resolve - {Object.=} - An optional map of
dependencies which should be injected into the controller.
This can be used like this:
$routeProvider.when('/foo', {resolve: {redirect: 'RedirectService'}});
Note: I've changed it from "Ctrl" to "RedirectService", because what you're describing in the question isn't really a "controller" in the Angular sense. It doesn't set up scope for a view. Instead, it's more like a service, which ends up redirecting.
I am writing the solution based on the already accepted answer and the github issue mentioned in it's comments.
The approach I am using is a resolve parameter in the $routeProvider. In my case I was trying to create a nice solution to logout in my application, when user goes to /logout.
Example code of $routeProvider:
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider.
...
when('/logout', {
resolve: {
logout: ['logoutService', function (logoutService) {
logoutService();
}]
},
}).
...
}]);
In the resolve part you specify a service (factory) by name and later on you have to call it. Still it is the nicest solution around.
To make the example complete I present my logoutService:
angular.module('xxx').factory('logoutService', function ($location, Auth) {
return function () {
Auth.setUser(undefined);
$location.path('/');
}
});
Works great!
Since Grails 1.1.x, they have supported XML and JSON unmarshalling for REST requests. I can't seem to get this working in version 2.1.0. Here is the relevant files from the example project I am using to test the functionality:
UrlMappings.groovy
static mappings = {
"/$action/$id?"(controller:'verification',parseRequest:true)
"/"(view:"/index")
"500"(view:'/error')
}
Tester.groovy
class Tester {
String name
String vendor
String toString() {
return "$name $vendor"
}
}
VerificationController.groovy
class VerificationController {
def save() {
Tester tester = new Tester(params.tester)
log.error "Tester = ${tester}"
log.error "Request XML = ${request.XML}"
}
}
XML send with REST call
<?xml version="1.0" encoding="utf-8"?>
<tester>
<name>Windows</name>
<vendor>Microsoft</vendor>
</tester>
When I pass the XML in, the controller log statements output this:
Tester = null null
Request XML = WindowsMicrosoft
My bewilderment would be in why it recognizes it as XML (by putting it in the XML field of the request) but won't parse it and put it in params as defined here
After trying for a few hours with the same problem I found a solution for me.
My problem was that I didn't had a content-type defined in my REST call.
So if you add content-type: application/xml to your rest call it should work.
I had the same problem, with almost exactly the same setup. I believe your problem could lie within the UrlMapping configuration. Initially I had the following.
"/rest/airport/$iata?"(controller: "airport", action: "restHandler", parseRequest: "true")
When I PUT/POST XML to that URI, it would show up correctly in request.XML, but it would never show up in the params object. I then realized I had the parseRequest boolean in quotes. Removing that fixed the issue.
"/rest/airport/$iata?"(controller: "airport", action: "restHandler", parseRequest: true)
Now, if your code is truly the same as what you have above, your problem may not be exactly the same. However, what might help is making some kind of change to your UrlMappings to see if you could get it working a different way.
At first I thought it might have been the single quotes you had, but that seemed to work fine either way I tried it. So, I would suggest just changing your URI mapping to something else and be very picky about the formatting. In the end it was just a minor formatting issue in the UrlMappings.groovy file that fixed my problem.
For a POST method, the W3 specs say:
If a resource has been created on the origin server, the response
SHOULD be 201 (Created) and contain an entity which describes the
status of the request and refers to the new resource, and a Location
header (see Section 10.4).
http://www.ietf.org/internet-drafts/draft-ietf-httpbis-p2-semantics-05.txt (section 8.5)
The standard response actually seems to be to send a Redirect to the newly created resource.
I'm building my site with ASP.NET MVC, and tried to follow the spec, so created a ResourceCreatedResult class:
public class ResourceCreatedResult : ActionResult
{
public string Location { get; set; }
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.Clear();
context.HttpContext.Response.StatusCode = 201;
context.HttpContext.Response.ClearHeaders();
context.HttpContext.Response.AddHeader("Location", Location);
}
}
And my action looks something like this:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult CreateNew(string entityStuff)
{
Entity newEntity = new Entity(entityStuff);
IEntityRepository entityRepository = ObjectFactory.GetInstance<IEntityRepository>();
entityRepository.Add(newEntity);
ActionResult result = new ResourceCreatedResult()
{ Location = Url.Action("Show", new { id = newEntity.Id }) };
return result;
}
However, IE, Firefox and Chrome all fail to redirect to the new resource. Have I messed up generating the correct response, or do web browsers not expect this type of response, instead relying on servers to send a Redirect response?
To be explicit, browsers (including modern browsers like Firefox 3 and IE8) do not "take the hint" and follow up an HTTP 201: Created response with a GET request to the URI supplied in the Location header.
If you want browsers to go to the URI supplied in the Location header, you should send an HTTP 303: See Other status instead.
Redirect after post or post/redirect/get is something your application must do to be user friendly.
Edit. This is above and beyond the HTTP specifications. If we simply return a 201 after a POST, the browser back button behaves badly.
Note that Web Services requests (which do NOT respond to a browser) follow the standard completely and do NOT redirect after post.
It works like this.
The browser POSTS the data.
Your application validates the data. If it's invalid, you respond with the form so they can fix it and POST.
Your application responds with a redirect.
The browser gets the redirect and does a GET.
Your application sees the GET and responds.
Now -- hey presto! -- the back button works.
My solution is to respond with a '201 Created' containing a simple page with a link to the new resource, and a javascript redirect using location.replace().
This lets the same code work for API and browser requests, plays nicely with Back and Refresh buttons, and degrades gracefully in old browsers.
As stated in the spec the response SHOULD be a HTTP 201 with redirect. So it isn't mandatory for a browser vendor to implement the correct answer...
You should try to change to a 30x code to see if it is correctly redirected. If so, it's a browser problem, else it may come from your code (I don't know anything in ASP.NET so I can't "validate" your code)
Shouldn't that only count for when something is "Created" and therefore a simple redirect to action should be genuinely sufficient?