Sometimes I will want to pass in an identifier to my website through the url, but I don't want to display this to the user. Is there a simple way to take in a request param but not display it to the user when loading the page?
This is a general idea of how my code is set up currently
#GetMapping("/somePage")
public ModelAndView get(#RequestHeader HttpHeaders headers,
#RequestParam(value = "someId", required = false) String someId) {
I know I could theoretically do this on the javascript side, but that seems to require the page to reload or mess with the history.
Generally this is bad practice - if it's passed in the URL, it'll be visible in the user's browser history. POST is probably the best practice here.
But to answer your actual question:
Put your custom value into a header and redirect?
Something along these lines (untested)
headers.set("X-Custom-Header1", someId);
headers.set("Location", "/newEndpoint");
return new ResponseEntity<>(headers, HttpStatus.FOUND);
Related
I have a Java-written Web API wherein I have web controllers handling HTTP requests. I'm trying to implement a RESTful architecture with HATEOAS, using Spring Boot. When adding HATEOAS links in methods I can easily add links for GET/DELETE requests, but I'm having trouble with POST/PUT/PATCH requests, mostly because those require me to supply a body of the thing I want to post, usually in JSON format. I've been googling for a while and I can't find out how to do it.
Here's how I'm adding links to GET / DELETE operations.
/**
* Shows all the Rooms present in the database.
*
* #return OK status and a list of Room Minimal DTO.
*/
#GetMapping(path = "/", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> getRooms() {
List<RoomDTOMinimal> roomDTOList = roomRepository.getAllDTOWebInformation();
for (RoomDTOMinimal r : roomDTOList) {
if (userService.getUsernameFromToken().equals("ADMIN")) {
Link roomSensors = linkTo(methodOn(RoomsWebController.class).getSensors(r.getName())).withRel("Get Room" +
"Sensors");
Link deleteRoom = linkTo(methodOn(RoomsWebController.class).deleteRoom(r)).withRel("Delete this Room");
r.add(roomSensors);
r.add(deleteRoom);
} else if (userService.getUsernameFromToken().equals("REGULAR_USER")) {
Link roomTemp = linkTo(methodOn(RoomsWebController.class).getCurrentRoomTemperature(r.getName())).
withRel("Get Room Temperature");
r.add(roomTemp);
}
}
return new ResponseEntity<>(roomDTOList, HttpStatus.OK);
}
I want to add a Link to an "editRoom" request, something like:
Link editRoom = linkTo(methodOn(RoomsWebController.class).configureRoom(r.getName(), WHAT GOES HERE???).withSelfRel();
But configureRoom takes in the roomName and a roomDTO in its signature. RoomDTO is a #RequestBody, so I can't give it to the Link. How should I add the link to the objects in a way that then allows me to call on that method?
I'd like to have something like:
ROOM | Delete | Edit
On the client side, where if I click DELETE the room is deleted, and if I click Edit the client side expands, with text boxes, allowing me to insert the required parameters to edit the room. I have the client-side code implemented for the Edit function, with appropriate front-end; but I can't link to it on the server-side without already providing data that should come later, from the user input. What's the best way to do this?
I've since solved it after talking with a team lead. Apparently it's acceptable to either pass null or an empty DTO object as a parameter in the scenario above; the HATEOAS implementation cares specifically about those parameters that are of the path, and roughly speaking ignores the others. Those can then be replaced as needed on the client-side upon a user performing an action / inserting input.
I would like to get the page content as a stream/or a string from a Page.class directly.
At the moment: I have to go through the route:
String uri = linkSource.createPageRenderLink(AnotherPage.class).toAbsoluteURI();
IOUtils.toString(uri, "UTF-8")
The problem with this approach is the call to toAbsoluteURI() makes the framework feel like the request is made from an external source; and it asks the user to login again; which should not be the case, as its one tapestry page accessing the other within the same application.
Note: I am not trying to "redirect" to AnotherPage.class. I would simply like to get another page's content as String without having to go via toAbsoluteURI() etc.
Alternatively, getting another page's content as Stream works too.
I am using Apache Tapestry 5.4.1
Take a look at the tapestry-offline module. It lets you obtain the HTML from a Tapeatry-generated page quite easily.
https://github.com/uklance/tapestry-offline
Have the method onActivate return an implementation of a StreamResponse, e.g.
public StreamResponse onActivate(Long productId) {
return new TextStreamResponse("text/csv", convertProductToCsv(productId));
}
By default a page returns a template, but in this way you override that behaviour.
Check out this page: https://wiki.apache.org/tapestry/Tapestry5HowToStreamAnExistingBinaryFile.
I'm trying to redirect my users to an AngularJS style URL:
http://somedomain.com/#/oauth/authorize
And I have some parameters I'm adding to the URL via my model. So far, so standard. Right up until Spring MVC takes a dark and sinister turn inside of RedirectView at line 400. If you click that link you'll see the comment:
// Append anchor fragment, if any, to end of URL.
No! This is not good for AngularJS! That would make my normally beautiful redirect:
http://somedomain.com/#/oauth/authorize?query=params
look like:
http://somedomain.com/?query=params#/oauth/authorize
This is bad juju!
So I have two questions:
What purpose does this arrangement of anchor and query params serve.
How do I circumvent this devilish machinery?
Thanks for any help!
I feel like I need to take a shower after doing this, but this is a possible solution.
Place this in your main script (i.e. app.js) immediately before creating the module in which you configure your $routeProvider.
var urlLoaded = window.location.href;
var qIndex = urlLoaded.indexOf('?');
var hashIndex = urlLoaded.indexOf('#');
if (qIndex !== -1 && hashIndex !== -1 && qIndex < hashIndex) {
var correctedPath = urlLoaded.substring(hashIndex);
var correctedParams = urlLoaded.substring(qIndex, hashIndex);
var base = urlLoaded.substring(0, qIndex);
var correctedUrl = base + correctedPath + correctedParams;
window.location.href = correctedUrl;
}
If you are in the early stages, I would look into using HTML5 pushState. This way you can use anchors like they were meant to be used, and you url's will look cleaner. There are browser limitations to this, and other considerations, but getting it in correctly might save you time down the line from running into this again.
You can extend this class, override the method, and fix it to be smarter than it currently is. Register it with spring (it might take a couple of overrides, look at ViewControllerRegistry), and if you're feeling nice, submit a Pull Request and see if they want it.
Beat that URL back into submission in angular as #hartz1989 has suggested
Instead of doing a redirect, you could respond back with a query parameter that has the redirected url, and add a handler in angular that understands this and does the "redirect". I don't really like this idea, but it is another idea.
I have an URL which shows me a coupon form based on id:
GET /coupon/:couponId
All the coupon forms are different and submit different POST params to:
POST /saveCoupon/:id
I want to have a convenient way of debugging my coupons and be able to have a way of viewing actual POST params submitted.
I've made a controller on URL POST /outputPOST/saveCoupon/:id which saves nothing, but prints to browser POST params received.
Now I want to have an URL like GET /changeActionUrl/coupon/:couponId which calls GET /coupon/:couponId and then substitutes form's action URL POST /saveCoupon/:id with POST /outputPOST/saveCoupon/:id .
In other words I want to do something like:
Result.getHtml().replace("/saveCoupon/","/outputPOST/saveCoupon/");
With this I can easily debug my coupons just by adding "/outputPOST" in the browser.
You could just use a bookmarklet and javascript to replace all of the forms' action attributes. That way your developer can do it with one click instead of changing urls.
Something like this will prefix all form actions on the page with "/outputPOST".
javascript:(function(){var forms=document.getElementsByTagName('FORM');for(i=0;i<forms.length;++i){forms[i].setAttribute('action','/outputPOST'+forms[i].getAttribute('action'));}})();
I don't understand, at least not everything ;)
In general you can debug every piece of Play app using debugger (check for your favorite IDE tips how to do that) - this will be always better, faster, etc etc, than modifying code only for checking incoming values.
I.e. Idea 13+ with Play support allows for debbuging like a dream!
I developed a shopsystem. there is a product page, which lists the available items filtered by some select menus. there is also one item detail page to view some content about each product. the content of that page will be loaded out of an xml property file. if one would click the link in the listview of an item, to view some details, an item specific GET parameter is set. with the parameters value, i can dynamically load the content for that specific item from my properties, by altering the loaded keys name.
so far so good, but not really good. so much to the backgroud. lets get to some details.
most of all, this is some SEO motivated stuff. so far there is also a problem with the pageinstance Id in the url for statefull pages, not only because of the nonstable url, also because wicket is doing 302 redirects to manipulate the url. maybe I will remove the statefull components of the item detailpage to solve that problem.
so now there are some QR-code on the products being sold, that contain a link to my detail page. these links are not designed by myself and as you can imagine, they look a whole lot of different like the actual url. lets say the QR-code url path would be "/shop/item1" where item1 would be the product name. my page class would be ItemDetailPage .
I wrote an IRequestMapper that I am mounting in my WebApplication#init() that is resolving the incoming requests URL and checks wether it needs to be resolved by this IRequestMapper. If so, I build my page with PageProvider and return a requesthandler for it.
public IRequestHandler mapRequest(Request request) {
if(compatibilityScore>0) {
PageProvider provider = new PageProvider(ItemDetailPage.class, new ItemIDUrlParam(request.getUrl().getPath().split("/")[1]));
provider.setPageSource(Application.get().getMapperContext());
return new RenderPageRequestHandler(provider);
}
return null;
}
So as you can see, I build up a parameter that my detailpage can handle. But the resulting URL is not very nice. I'd like to keep the original url by mapping the bookmarkable content to it, without any redirect.
My first thought was to implement an URLCodingStrategy to rebuild the URL with its parameters in the form of a path. I think the HybridUrlCodingStrategy is doing something like that.
After resolving the URL path "/shop/item1/" with the IRequestMapper it would look like "/shop/item?1?id=item1" where the first parameter off course is the wicket pageinstance Id, which will most likely be removed as I will rebuild the detail page to be stateless :(
after applying an HybridURLCodingStrategy it might look like "/shop/item/1/id/item1" or "/shop/item/id/item1" without pageinstance Id. another Idea would be to remove the second path part and the parameter name and only use the parameters value so the url would look like "/shop/item1" which is then the same url as it was in the request.
Do you guys have any experience with that or any smart ideas?
The rewuirements are
Having one fix URL for each product the SE bot can index
no parameters
stateless and bookmarkable
no 302 redirects in any way.
the identity of the requested item must be available for the detailpage
with kind regards from germany
Marcel
As Bert stated, your use case should be covered with normal page mounting, see also the MountedMapper wiki page, for your case a concrete example:
mountPage("/shop/${id}", ShopDetailPage.class);
Given that "item1" is the ID of the item (which is not very clear to me), you can retrieve it now as the named page parameter id in Wicket. Another example often seen in SEO links, containing both the unique ID and the (non-unique, changing) title:
mountPage("/shop/${id}/${title}", ShopDetailPage.class);
Regarding the page instance ID, there are some ways to get rid of it, perhaps the best way is to make the page stateless as you said, another easy way is to configure IRequestCycleSettings.RenderStrategy.ONE_PASS_RENDER as the render strategy (see API doc for consequences).