I am trying to create a custom component for page redirect. The use case is pageB should be accessible only via pageA. If pageB is accessed directly then user should be redirected to pageA.
In order to achieve this, I am setting a variable in pageA's request scope. In pageB i will be including a component(drag and droppable), which will look for pageA's request scope variable and if it is not available then we pageB will be redirected, using below snippet.
private void redirect(SlingHttpServletRequest request) {
String redirectPath = "/content/geometrixx/home.html";
if (StringUtils.isNotBlank(redirectPath)) {
getResponse().setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
getResponse().setHeader(redirectPath);
} else {
LOGGER.error("Redirect path is not set in component");
}
}
But the above code is failing. I believe it is due to "response already committed" error.
Can someone suggest me a way out for this problem?
I would suggest to you to not use a component on a page to do this. There is another mechanism called (Sling) Filter which is much more suited for something like this.
Put simply, a filter is a OSGi service that is called on every request. The filter gets the request and response passed and can use them to do different things, like for example check if a user visited a page before visiting another page.
There is already a good answer for something like this on StackOverflow: Filter request URL before any processing in CQ5.6
Important note:
Filters are only run if a users request gets to AEM. Typical AEM installations use a so called Dispatcher (Apache with a special "Dispatcher" Apache module) that cache responses. If a response is cached the Dispatcher will serve the cached HTML page instead of request AEM. So if a page is served from the Dispatcher cache your filter won't be run and therefore this check can not be done. There are a few ways to work around this but that's a topic for another question.
Sling filter is used when there is a need to handle more than one page following a certain pattern and you want to do some processing beforehand.
If you know the page URL already and simply want a redirect then you can also use sling:Mapping to redirect the page B request to page A.
You can use something like this.
Goto /etc/map/http and create a node below this path. For https requests, create a sling:folder for https as well.
/etc/map
+-- http
| +-- jcr:primaryType="sling:Folder"
+-- page-b
| +-- jcr:primaryType="sling:Mapping"
| +-- sling:redirect=/content/pageA.html -> Type String
| +-- sling:match=localhost.4502/content/pageB(.*)(/.*) -> Type String
+-- https
| +-- jcr:primaryType="sling:Folder"
+-- page-b
| +-- jcr:primaryType="sling:Mapping"
| +-- sling:redirect=/content/pageA.html -> Type String
| +-- sling:match=localhost.4502/content/pageB(.*)(/.*) -> Type String
Or if you simply want an internal handling of the resource and avoid page reload you can use:
jcr:primaryType="sling:Mapping"
sling:internalRedirect=/content/pageA.html -> Type String
sling:match=localhost.4502/content/pageB(.*)(/.*) -> Type String
To handle dispatcher redirect:
Include a rewrite rule in dispatcher for redirection from pageB to pageA.
Note: You can change the regex in sling:match to suit your needs.
Related
Is there a way to have a parameter at the end of the URL for a Rest request?
This is the URL:
http://localhost:8000/my_user/1000
I've tried to use a parameter for the 1000, like so:
http://localhost:8000/my_user/${#Project#id_test}
This doesn't render 1000 at the end.
Is there a way to do this?
There are REST resource parameter types.
In your case, style should be Template. That should resolve the issue.
To give an example:
While adding a new REST Resource from API use below url instead of plain text value.
In the request you would see as below:
Now, in your test cases, you can use the property expression (the one you were using in the question) i.e., ${#Project#id_test} for value field/column as underline in the above image.
You may also go thru documentation if needed.
What you're trying to achieve is possible, though I'm not sure about the value you have supplied.
When you set up a RESTful Test Project is SoapUI, you define the root/base url under test.
When you set up the service call in SoapUI, you define the GET, POST settings. In the same place, you can add parameters. Click on the Request tab and you should see an empty table with headings, Name, Value, Style, Level.
You can add your parameter here.
E.g.
Name | Value | Style | Level
id | Smith | QUERY | RESOURCE
When you run the service call and you not sure your request is correct, click on the Raw tab and that will show what SoapUI is actually sending to your service.
I'm getting this warning from Brakeman. As they say,redirects which rely on user-supplied values can be used to “spoof” websites or hide malicious links in otherwise harmless-looking URLs. They can also allow access to restricted areas of a site if the destination is not validated.
| Confidence | Class | Method | Warning Type | Message
| High | DocumentsController | download | Redirect | Possible unprotected redirect near line 46: redirect_to(+Document.find(params[:id]).f
In my controller I created a method download that takes the file URL (file stored on Amazon S3 and the URL in my database thanks to Paperclip) and creates a URL (ie. document_url) that will last 3 seconds (for the user to download) thanks to .expiring_url(3)
def download
#document = Document.find(params[:id])
document_url = #document.file.expiring_url(3)
if URI.parse(document_url).host.include? "domain.com"
redirect_to document_url, only_path: true
else
document_url = nil
end
end
I have been trying pass Brakeman's validation without success. As you can see above, I tried to check if the my domain is present in the URL but it did not change the report on Brakeman.
Any idea how to proceed?
I have the similar problem. I passed the warning by utilizing strong parameters like below, though I'm not sure why it works.
redirect_to referer_param
def referer_param
params.require(:referer)
end
I am trying to test an app with RESTful services via Postman (awaiting the front-end to be implemented).
The GET/POST and DELETE requests work as expected, but when it comes to PUT and PATCH I'm completely stuck.
I have a simple form with several inputs and files (pdf & image). With the POST request I simply use the "form-data" body to add all needed parameters. But when I try to test the PUT one, "form-data" detects nothing - with or without a "Content-Type" = "multipart/form-data" set in the header.
The PUT only works with the "x-www-form-urlencoded" option for the body, but then I cannot or don't know how to add both files, as I don't have the ability to choose from "Text/File" anymore via Postman's dropdown. Again, adding Content-Type doesn't help a bit.
I tried simulating POST with a "_method"="PUT" both as a URL parameter and as a form input, but it just creates new item instead of updating existing one (with correct route applied).
# Updated with the routes (standard Laravel routes for RESTful services)
GET | api/v1/Collection | api.v1.collection.index
POST | api/v1/collection | api.v1.collection.store
GET | api/v1/collection/create | api.v1.collection.create
GET | api/v1/collection/{item} | api.v1.collection.show
PUT | api/v1/collection/{item} | api.v1.collection.update
DELETE | api/v1/collection/{item} | api.v1.collection.destroy
GET | api/v1/collection/{item}/edit | api.v1.collection.edit
I realize I have to use a hidden input with _method/PUT as key-value, but I have no idea where to add it in Postman. I can only see two options: text input or file.
What am I missing?
I can provide screenshots upon request. Thanks.
Official Grails documentation says that
Version 2.0.x of the scaffolding plugin includes different scaffolding
templates that are aligned with the new REST APIs introcued in Grails
2.3 and above.
(taken from here http://grails.org/doc/latest/guide/scaffolding.html)
But I can't make (or I don't understand the concept) work RESTfulness together with scaffolding.
Let's start from scratch:
grails create-app myapp
cd myapp/
grails create-domain-class Book
grails create-scaffold-controller myapp.Book
Add a field to the domain class
class Book {
String text
static constraints = {
}
}
and run the app with grails run-app.
Surfing on the http://localhost:8080/myapp/ shows that scaffolding works great:
http://localhost:8080/myapp/book/index page shows books list
http://localhost:8080/myapp/book/show/1 page show details for the book with id = 1
http://localhost:8080/myapp/book/create page creates a book
and so force, good old scaffolding.
Let's see what about REST.
Official docs say I should use URLs like http://localhost:8080/myapp/books/... for the REST but any attempt to access the app, like this curl -i -H "Accept: application/json" localhost:8080/myapp/books/1 returns 404 with bunch of HTML.
Ok, let's read docs carefully:
The easiest way to create a RESTful API in Grails is to expose a
domain class as a REST resource. This can be done by adding the
grails.rest.Resource transformation to any domain class
No problem, now the Book class heading is
import grails.rest.*
#Resource(uri='/books') class Book {
Now surfing on the http://localhost:8080/myapp/ shows that scaffolding is broken:
http://localhost:8080/myapp/book/index page shows books list
http://localhost:8080/myapp/book/create page shows xml output <?xml version="1.0" encoding="UTF-8"?><book><text /></book>
and so force, bad new xml output.
I'd played with #Resource and "/books"(resources:"book") in URLMappings.groovy but hadn't found any working solution which makes possible scaffolding and RESTfulness work back-to-back. Indeed, I managed to make them work separately.
Update
I'd found the way how to achieve the desired goal. The way I found is:
Mark the Book class with #Resource(uri = "/books").
Remove scaffold controller BookController.
Create dedicated controller with scaffolding for the Book: class HumanBookController {static scaffold = Book}
Now scaffold GUI pages with URLs like http://localhost:8080/myapp/humanBook/index work pretty well. Either json requests are handled well with URLs like http://localhost:8080/myapp/books/1. But it's not elegant to have 2 controllers doing same things for common web and json.
You can do this:
import grails.rest.RestfulController
class BookController extends RestfulController {
static responseFormats = ['html', 'json']
BookController() {
super(Book)
}
}
And then in the UrlMappings.groovy:
"/books"(resources:"book")
"/$controller/$action?/$id?(.${format})?"{
constraints {
// apply constraints here
}
}
No need to add #Resource in the domain.
You can now have /books/1.json or /books/1.html to point to the right places. You might still need to do grails generate-view Book to have the view generated. But although you need to generate the views for html, you keep only single controller and path.
I had the same problems as yours.
That might be a trivial solution and not for every case, but try updating your Grails version.
As for me: Grails 2.3.4 -> Grails 2.3.6 worked.
Hope that help anyone.
I'm currently using Grails 2.4.0, the solution came by doing this:
Controller: BookController { static scaffold = true }
Domain: Book { .... } // WITHOUT #Resource
The result is that you can:
/book.json to get a list JSONized
/book/index to get an HTML standard scaffolding
/book/create html scaffold for a new item
/book/show/1 html scaffold edit item 1
/book/show/1.json JSON for item id: 1
I'ts wicked, i know. I found this and it get me going.
With Grails 2.4.4 I was able to get the scaffolding working with single controller using the following steps:
Added a URL to Resource mapping in UrlMappings.groovy, e.g. "/books"(resources:"book")
Inserted static scaffold = true into the generated controller
I did not verify if the following made a difference, but I also set grails.mime.disable.accept.header.userAgents = [] and grails.mime.use.accept.header = true in Config.groovy (the latter is presumably the new default value).
Both of the scaffolded REST and UI interfaces are working fine with the following tests:
GET /app//1 (passing Accept header)
GET /app//1.json (no Accept header)
POST /app/ (with payload as json or form encoded)
DELETE /app//1
PUT /app//1 (with json payload. form payload updated the object, but sent back 302 redirects)
EDIT
Removed the Resource annotation step and clarified the URL mapping setup
The URI assigned in the URL mapping is not the same as the default URI for the controller. For example, "books" instead of "book". After adding this mapping, the URI for the controller will default to the URI in UrlMapping, but the original URI will continue to work.
The generated controller is a Restful controller because it implements actions aware of requests like:
curl -i -X GET yourDomain:8080/yourApp/books.json
It returns a list of books in json format. (10 books, assuming that you created test data, did you?)
You can append a parameter like:
curl -i -X GET yourDomain:8080/yourApp/books.xml?40
By default you will get the html format. You need to append .json or .xml to get the correct data.
You can to use the Accept header too
curl -i -X GET -H "Accept: application/xml" yourDomain/books/1
returns details of book with id=1 in xml format. Finally
curl -i -X POST -H "Content-Type: application/json" -d "{name: 'Book'}" yourDomain/books
creates a new book and
curl -i -X PUT -H "Content-Type: application/json" -d "{name: 'Book'}" yourDomain/books/1
updates name of book with id=1
All resources need to be exposes through and url. The url is not generated for you, you should write it on UrlMappings file:
"/v1/books"(resources: "book")
Where the first string "/v1/books" is the uri and the second string "book" is the controller name following the grails convention. (The preceding v1 string is because I always put version number to my API URIs)
| GET | /v1/books | Action: index |
| GET | /v1/books/create | Action: create |
| POST | /v1/books | Action: save |
| GET | /v1/books/${id} | Action: show |
| GET | /v1/books/${id}/edit | Action: edit |
| PUT | /v1/books/${id} | Action: update |
| DELETE | /v1/books/${id} | Action: delete |
All that should be required is #Resource annotation with the uri on the Domain class. If you want specific formats (default format is first), also include the formats:
#Resource(uri='/books', formats=['json', 'xml'])
That should be it. If ypu are still having trouble finding your dynamic #Resource endpoint, try running:
grails url-mappings-report
That will give you a nice summary of all urls, including those backed by scaffolded controllers for #Resource domains. I have found that I tend to make silly mistakes when trying to "guess" the URL - using the report output ensures you and grails are in agreement.
I'm trying to build a REST service in a Sitecore root. My application start looks like this:
void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapHttpRoute(
name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = System.Web.Http.RouteParameter.Optional });
}
And my URL looks like this:
http://{mydomain}/api/books
I have the correct controller and all that.
But Sitecore keeps redirecting me to the 404 page. I've added the path to the IgnoreUrlPrefixes node in the web.config, but to no avail. If I had to guess, I'd think that Sitecore's handler is redirecting before my code gets the chance to execute, but I really don't know.
Does anybody have any idea what might be wrong?
Your assessment is correct. You need a processor in the httpRequestBegin pipeline to abort Sitecore's processing. See the SystemWebRoutingResolver in this answer:
Sitecore and ASP.net MVC
It's also described in this article:
http://www.sitecore.net/Community/Technical-Blogs/John-West-Sitecore-Blog/Posts/2010/10/Sitecore-MVC-Crash-Course.aspx
But I'll include the code here as well. :)
public class SystemWebRoutingResolver : Sitecore.Pipelines.HttpRequest.HttpRequestProcessor
{
public override void Process(Sitecore.Pipelines.HttpRequest.HttpRequestArgs args)
{
RouteData routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(args.Context));
if (routeData != null)
{
args.AbortPipeline();
}
}
}
Then in your httpRequestBegin configuration:
<processor type="My.SystemWebRoutingResolver, My.Classes" />
You might want to have a look at Sitecore Web Api
It's pretty much the same you are building.
Another option, which I've used to good effect, is to use the content tree, the "star" item, and a sublayout/layout combination dedicated to this purpose:
[siteroot]/API/*/*/*/*/*/*/*/*/*
The above path allows you to have anywhere between 1 and 9 segments - if you need more than that, you probably need to rethink your process, IMO. This also retains all of the Sitecore context. Sitecore, when unable to find an item in a folder, attempts to look for the catch-all star item and if present, it renders that item instead of returning a 404.
There are a few ways to go about doing the restful methods and the sublayout (or sublayouts if you want to segregate them by depth to simplify parsing).
You can choose to follow the general "standard" and use GET, PUT, and POST calls to interact with these items, but then you can't use Sitecore Caching without custom backend caching code). Alternately, you can split your API into three different trees:
[siteroot]/API/GET/*/*/*/*/*/*/*/*/*
[siteroot]/API/PUT/*/*/*/*/*/*/*/*/*
[siteroot]/API/POST/*/*/*/*/*/*/*/*/*
This allows caching the GET requests (since GET requests should only retrieve data, not update it). Be sure to use the proper caching scheme, essentially this should cache based on every permutation of the data, user, etc., if you intend to use this in any of those contexts.
If you are going to create multiple sublayouts, I recommend creating a base class that handles general methods for GET, PUT, and POST, and then use those classes as the base for your sublayouts.
In your sublayouts, you simply get the Request object, get the path (and query if you're using queries), split it, and perform your switch case logic just as you would with standard routing. For PUT, use Response.ReadBinary(). For POST use the Request.Form object to get all of the form elements and iterate through them to process the information provided (it may be easiest to put all of your form data into a single JSON object, encapsulated as a string (so .NET sees it as a string and therefore one single property) and then you only have one element in the post to deserialize depending on the POST path the user specified.
Complicated? Yes. Works? Yes. Recommended? Well... if you're in a shared environment (multiple sites) and you don't want this processing happening for EVERY site in the pipeline processor, then this solution works. If you have access to using MVC with Sitecore or have no issues altering the pipeline processor, then that is likely more efficient.
One benefit to the content based method is that the context lifecycle is exactly the same as a standard Sitecore page (logins, etc.), so you've got all the same controls as any other item would provide at that point in the lifecycle. The negative to this is that you have to deal with the entire page lifecycle load before it gets to your code... the pipeline processor can skip a lot of Sitecore's process and just get the data you need directly, making it faster.
you need to have a Pipeline initializer for Routing:
It will be like :
public class Initializer
{
public void Process(PipelineArgs args)
{
RouteCollection route = RouteTable.Routes;
route.MapHttpRoute("DefaultApi", "api/{controller}/{action}/{id}",
new { id = RouteParameter.Optional });
}
}
On config file you will have :
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<initialize>
<processor type="_YourNameSpace.Initializer,_YourAssembly" />
</initialize>
</pipelines>
</sitecore>
</configuration>
Happy coding