I have a REST service in Groovy on Grails; basic service that takes data and transforms it. It works fine except when the data being passed in has forward or back slashes. In those cases the browser tries to navigate to a directory based on the data:
localhost/traverse/map/321 64 fourth <<< this works fine
localhost/traverse/map/321/64/fourth <<< tries to find localhost/traverse/map/321/64/fourth and throws an http status 404
My urlmapping:
"map/$id" (controller: "map", action: "transform", formats=['text/plain'], method: "GET")
My controller. aside from the class declaration and class import nothing else going on:
def transform = {
//println params.id
if (param.id) {
DataMap dm = new DataMap();
render dm.hostNodeLookup(params.id)
}
}
Most of the data that will be passed to the REST service will have slashes and the number of slashes per "data being passed in" will vary from 1-N but I haven't been able to figure out how to escape/parse/other wise get around that issue. I've read up on this site but I didn't find it too helpful for this problem.
I do not have access to the web server to adjust encoding or how browsers render URL mappings and strings. The data doesn't get to the controller so I haven't been able to parse out the strings there. Anyone have ideas?
After reading this post I tried it and it worked like a charm.
In the urlmapping file I added this ** to the id variable:
"map/$id**" (controller: "map", action: "transform", formats=['text/plain'], method: "GET")
Related
Grails 2.5. I need to log the request and the response to log file.
This is to log REST/JSON api calls. I need to log both the request JSON (which is easy with a filter), and the response JSON (Which is looking impossible)
an api call looks like this:
class myController {
def myMethod() {
def json = request.JSON
:
render(status:200, contentType:'application/json' {
['something': "something",'sometingElse' : 42]
}
Tried creating a filter, e.g:
class LoggingFilters {
def filters = {
after = { Map model ->
log.debug "${response.???"
}
The question is, how do you get a handle on the output? Tried respose.outputStream.toString() etc, but it says something has already got the output stream.
I saw one suggestion which said don't use render, return a model with a map with a single item: data, and render this outside against some dummy jsp. Then you can get the model in the filter. The problem with this is that we have many hundreds of render statements, and we dont really want to retest everything (we have no automated testing).
Another other work around is to use tomcat request dumper filter, but this would not meet our requirements (e.g. we need to scrub certain output, such as passwords, want to be able to log to db, only want to log very specific things etc).
We tried this plugin: https://grails.org/plugin/httplogger , but it doesnt work with grails 2.5.
I defined a domain model class with a few properties and marked it as a RESTful resource using #Resource following the official Grails guide on Web services. Now, when testing the application (using Ruby's RestClient) I can see that things are working fine. However, after defining an associated Controller that overrides save method (for creating new resource), I've been getting 404 even on just simple GET requests. I defined some test objects using BootStrap so GET should be working.
My controller code looks like this:
class ModelController {
#Transactional
def save(Model model) {
def status = 201
if (model.validate()) {
model.save(flush: true, failOnError: true)
} else {
status = 422
}
render status:status
}
}
Do I still need to do something with the UrlMappings.groovy or is there something wrong with my controller code (all my unit tests for it are passing though)?
Update
I have created a sample project to demonstrate what's happening. Please check my bookstore-demo repository on GitHub. In the repository, I've defined 2 tags, rest-working, and rest-not-working. The first one marks the point where things are still working, and second one, as the name suggests, marks where I've created a controller that causes 404 response. This is pretty much what I've done with my actual project so far, and I'm getting the the same error.
The code looks OK, if you are getting a 404 then it sounds like its not even hitting this Controller. I would open developer console and try capture what URL it is hitting - URL being sent is potentially incorrect. If I am experimenting I tend to put println "1" println "2" and so on between my logics to see where the code is going or did it return it at all meaning it didn't even get there. so maybe if you doubt your code try this tactic between your if statements see which numbers get hit.
Also there is not a lot to go on to try give anything of more useful as feedback, but I would also check out the allowedMethods of this Controller if any defined...
unsure how it is being posted by if you have ..
static allowedMethods = [save: "POST", update: "POST", delete: "POST"]
try changing it to
static allowedMethods = [update: "POST", delete: "POST"]
unsure if this is still supported:
static allowedMethods = [save:['POST','GET'],update: "POST", delete: "POST"]
For all the methods refer to:
http://grails.org/doc/latest/ref/Controllers/allowedMethods.html
I am following the directions in the docs, here:
http://grails.org/doc/2.3.8/guide/webServices.html#hypermedia
Why won't grails produce HAL-formatted output, as shown in the documentation?
I have a domain object which I have mapped with the #Resource annotation:
#Resource(uri='/documentCatalogs', formats = ['json', 'xml'], readOnly = true)
class DocumentCatalog {
String entityType
String actionCode
...
}
...and in my conf/spring/resources.groovy, I have configured the HAL JSON renderer beans:
import com.cscinfo.platform.api.formslibrary.DocumentCatalog
import grails.rest.render.hal.HalJsonCollectionRenderer
import grails.rest.render.hal.HalJsonRenderer
// Place your Spring DSL code here
beans = {
halDocumentCatalogRenderer(HalJsonRenderer, DocumentCatalog)
halDocumentCatalogCollectionRenderer(HalJsonCollectionRenderer, DocumentCatalog)
}
Using the debugger, I confirmed that the initialize() method on HalJsonRenderer is called and that it is constructed with the correct targetType.
I send a rest call using Postman:
http://localhost:8080/formslibrary/documentCatalogs/3
Accept application/hal+json
And I get back a response which is regular JSON and doesn't contain any links:
{
"class": "com.cscinfo.platform.api.formslibrary.DocumentCatalog",
"id": 3,
"actionCode": "WITH",
"entityType": "LLP",
...
}
What did I miss? Is there some plugin or configuration setting I have to enable for this behavior? Is there some additional mapping property somewhere that's not documented?
Figured it out! There are multiple aspects of the fix...
I had to add "hal" as one of the listed formats in the #Resource annotation:
#Resource(uri='/documentCatalogs', formats = ['json', 'xml', 'hal'])
Some hunting around in the debugger revealed that Grails will blithely ignore the Accept header, based on the UserAgent string that is sent from the client. (In my case, since I'm using Postman, it was the Google Chrome UA string.)
One workaround for the Accept header issue is to add ".hal" to the end of the URL:
http://localhost:8080/formslibrary/documentCatalogs/3.hal
This isn't a very good solution IMO, since the HAL URLs generated by the renderer don't end in ".hal" by default.
A better solution is to fix Grails' handling of the accept header by updating the config. In Config.groovy, you will see a line that says:
grails.mime.disable.accept.header.userAgents = ['Gecko', 'WebKit', 'Presto', 'Trident']
Change it to:
grails.mime.disable.accept.header.userAgents = ['None']
This forces Grails to honor the Accept header, regardless of the user agent.
Hope this helps somebody else who's hitting the same issue.
P.S. It's really helpful to put a breakpoint in the ResponseMimeTypesApi#getMimeTypesFormatAware(...) method.
I had made a REST webservice using redirecting to various paths like if i need to delete some user then i will redirect the user to this address in the #Path annotation :
user/delete
and therefore there is no thing like RESPONSE i have used.
While going through a code given to me by my senior i came accross these lines :
java.net.URI uri = uriInfo.getAbsolutePathBuilder().path(id).build();
Response.created(uri).build();
What are these lines doing, i have no idea.
Can someone explain me this w/o wiki links or any other 'Basic Rest Service' links.
Without any explicit details about the uriInfo object I can only speculate its type is the JAX-RS UriInfo class.
The first line can be broken down as below:
java.net.URI uri = uriInfo.getAbsolutePathBuilder().path(id).build();
The getAbsolutePathBuilder is documented http://jackson.codehaus.org/javadoc/jax-rs/1.0/javax/ws/rs/core/UriInfo.html#getAbsolutePathBuilder%28%29
java.net.URI uri = uriInfo.getAbsolutePathBuilder().path(id).build();
The method returns a UriBuilder object. On which the 'path(...)' method is called passing the id so if the absolute path returned http://www.host.com (this may or may not have a port number) adding the id in this method will then result in the effectively Builder holding the two parts. The base URI and the path. The two values have not yet been put together
The build method then concatenates the two values resulting a full URI. For example http://www.google.com/id (Where http://www.google.com is the absolute path)
The second line
Response.created(uri).build();
Is basically saying 'Respond with a created (201) response code, and set a Location header containing the build uri value'
I am trying to figure out how to do a redirect within a controller action in Play (2.0) using Scala.
The redirect using
Redirect(routes.Application.index)
works just fine.
What I cannot figure out from the docs, API, or Google is how to add parameters to the call.
I am coming from Grails where this could be done easily as follows:
redirect action: "index", params: ["key": "value"]
.
The only way I have found is to call Redirect using a string url and a query string, which seems awkward.
Basically I would like to make use of Redirect(Call) somehow, but I do not how to create the Call object using reverse routing.
Am I missing something/not getting the concept in Play/Scala?
Thanks in Advance!
Ellou'
A route is just a function, so you can pass arguments as usual:
// Redirect to /hello/Bob
def helloBob = Action {
Redirect(routes.Application.hello("Bob"))
}
This snippet comes from http://www.playframework.org/documentation/2.0/ScalaRouting (at the bottom)
You can also avoid creating another function just for this in your controller. In your route config, you can simply add something like this:
GET /google #controllers.Default.redirect(to = "http://google.com")