Missing parameters with RESTful request when upgrading to Grails 2.3.0 - rest

I am using Grails with RESTful to develop my web application. Everything works fine, till I upgrade my application to Grails 2.3. Here is my UrlMappings:
I still send request, submit or do some other things normally, but in POST, PUT requests, the parameters are missing. Server just recognize only the parameters I put on the URL directly, but the remain I enclose in form or model when submit cannot be found in the "params" variable. He is my UrlMappings:
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?"{ constraints {} }
name apiSingle: "/api/$controller/$id"(parseRequest:true){
action = [GET: "show", PUT: "update", DELETE: "delete"]
constraints { id(matches:/\d+/) }
}
name apiCollection: "/api/$controller"(parseRequest:true){
action = [GET: "list", POST: "save"]
}
name api2: "/api/$controller/$action"(parseRequest:true)
name api3: "/api/$controller/$action/$id"(parseRequest:true)
"/"(view:"/welcome")
"500"(view:'/error')
}
}
I have read the latest document of Grails 2.3, at http://grails.org/doc/latest/guide/theWebLayer.html#restfulMappings
but I think it is not clear. I have tried it follow the documentation but have no result. And there are no any sample about using Grails 2.3 with RESTful for me to refer.
How can I make it work normally as before, and can access all parameter values in REST request? Thank you so much!

According to this http://grails.1312388.n4.nabble.com/Grails-2-3-and-parsing-json-td4649119.html parseRequest has no effect since Grails 2.3
If you use JSON as request body you can accees request params as request.JSON.paramName
As a workaround you can add a filter that will populate data from JSON to params:
class ParseRequestFilters {
def filters = {
remoteCalls(uri: "/remote/**") {
before = {
if (request.JSON) {
log.debug("Populating parsed json to params")
params << request.JSON
}
}
}
}
}

Adding on to Kipriz's answer and cdeszaq's comment, you can write a recursive method to inject nested params. Something along these lines:
public void processNestedKeys(Map requestMap, String key) {
if (getParameterValue(requestMap, key) instanceof JSONObject) {
String nestedPrefix = key + ".";
Map nestedMap = getParameterValue(requestMap, key)
for (Map.Entry<String, Object> entry : nestedMap.entrySet()) {
String newKey = nestedPrefix + entry.key;
requestMap.put(newKey, getParameterValue(nestedMap, entry.key))
processNestedKeys(requestMap, "${nestedPrefix + entry.key}");
}
}
}
public static Map populateParamsFromRequestJSON(def json) {
Map requestParameters = json as ConcurrentHashMap
for (Map.Entry<String, Object> entry : requestParameters.entrySet()) {
processNestedKeys(requestParameters, entry.key)
}
return requestParameters
}

Related

Spring Cloud Contract - Transforming response in Groovy DSL

This is my spring contract in groovy file:
package com.stubs.contracts
import org.springframework.cloud.contract.spec.Contract
[
Contract.make {
description "Stub for my endpoint"
request {
method POST()
url("/rest/v1/value/validate") {
}
headers {
contentType applicationJson()
}
body(
file("Request_validate_200.json")
)
}
response {
body(
file("Response_validate_200.json")
)
headers {
contentType applicationJson()
}
status OK()
}
}
]
My question is:
How to override value for response body?
I want to:
1) load file with request JSON
2) take "id" attribute value from this request
3) replace "id" value property in loaded JSON response
Is it possible to do it?
You can't do it out of the box. You could play around with classloaders as we do behind the scenes here (https://github.com/spring-cloud/spring-cloud-contract/blob/master/specs/spring-cloud-contract-spec-java/src/main/java/org/springframework/cloud/contract/spec/internal/Common.java#L243-L255). In other words, you could create a closure like this:
Closure fileLocation = { String relativePath ->
URL resource = Thread.currentThread().getContextClassLoader()
.getResource(relativePath);
if (resource == null) {
throw new IllegalStateException("File [" + relativePath + "] is not present");
}
try {
return new File(resource.toURI());
}
catch (URISyntaxException ex) {
throw new IllegalStateException(ex);
}
}
}
call fileLocation("Response_validate_200.json") to retrieve the File, then convert it to a String like this fileLocation("Response_validate_200.json").text then use a JsonSlurper to parse it new groovy.json.JsonSlurper().parseText(fileLocation("Response_validate_200.json").text). From that you'd have to play around with the slurper to modify the contents, and that's pretty much it.

How to buld a rest ful API with RXJava and Micronaut?

I would like to write a REST API that return HTTP 400 when I try to create an already existing entity or when I try to update an non existing entity.
#Post
fun create(#Body entity: #Valid Entity): HttpResponse<Entity> {
val optional = entityService.find(entity)
if(optional.isPresent) {
return HttpResponse.badRequest()
}
return HttpResponse.created(entityService.save(entity))
}
How can I do that using non blocking endpoint with RXJava2 and Micronaut, I can only find examples with switchIfEmpty
#Post
#Status(HttpStatus.CREATED)
fun createMeal(#Body entity: #Valid Entity): Single<Entity> {
return entityService.find(entity)
.switchIfEmpty(entityService.save(entity))
.map{success -> entity}
}
but this code always return HTTP 200 even if nothing is saved, I don't think it's a good practice.
Thanks
You would use map to convert the entity to a bad request response since if it exists that is what you want to return. You could also use switchIfEmpty to save the entity which would only occur if the entity is not found. Ensure you wrap that code inside Flowable.defer to prevent the logic from executing no matter what. In your reactive example above the save will occur on every execution.
return entityService.find(entity)
.map(entity -> HttpResponse.badRequest())
.switchIfEmpty(Flowable.defer() -> {
//return a publisher that emits HttpResponse.created(entity)
})
Finally I do something like this :
fun update(name: String, entity: Entity): Single<Entity> {
val observable = BehaviorSubject.create<Entity>()
entitysRepository.find(name)
.subscribe(
{
entity.name = name
update(entity, observable)
},
{ observable.onError(RuntimeException("Entity : $name doesn't exist")) }
)
return observable.singleOrError()
}
fun save(entity: Entity): Single<Entity> {
val observable = BehaviorSubject.create<Entity>()
entitysRepository.find(entity.name)
.subscribe(
{ observable.onError(RuntimeException("Entity : ${entity.name} already exist")) },
{ save(entity, observable) }
)
return observable.singleOrError()
}
Don't really know if it's a good practice or not.

Headers in POST in Grails 3 app are not being sent with rest of service

Using Grails 3.0.9, and grabbing the freshest REST API with this snippet in gradle.build:
compile 'org.grails:grails-datastore-rest-client:4.0.7.RELEASE', {
['commons-codec', 'grails-async', 'grails-core',
'grails-plugin-converters', 'grails-web', 'groovy'].each {
exclude module: it
}
}
I am trying to make the following POST request:
def rest = new RestBuilder(headers:["X-LSS-Env":"devmo"], connectTimeout:10000, readTimeout:20000)
response = rest.post("http://..../..") {
accept "application/json"
contentType "application/json"
json jsonBuilder
}
Now, the POST receiver gets the json okay, give back a response okay, but this is the problem: it receives the headers as an empty map or as null!
So, what is the correct way of passing header data to the POST receiver? This is needed because the environment key X-LSS-Env could have different values, which instructs the receiver to do further routing based on it. Same with the GET request of course.
* UPDATE *
The consumer of my POST requests is actually a Java application, running on Apache Tomcat/8.0.26. The is how the service looks on the other side:
private javax.servlet.http.HttpServletRequest hsr;
#POST
#Path("/na")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response postSomething(Ggfp ggfp ){
try {
Enumeration<String> hnames = hsr.getHeaderNames();
int i = 0;
while (hnames.hasMoreElements()) {
String headerName = hnames.nextElement();
System.out.println(++i+ " headerName: " + headerName);
String val = hsr.getHeader(headerName);
System.out.println(" val: " + val);
}
String hval = hsr.getHeader("X-LSS-Env");
return Response.status(Status.OK).entity("X-LSS-Env is " + hval).build();
} catch (Exception e) {
}
}
Calling this service from Postman works, headers are identified. Calling it from the Grails app results into an empty map - like I am sending no headers!
The RestBuilder constructor never liked the way I used (or abused) it. Here is a clean way of achieving what I set out to do, with tryCatch logic if a timeout transpires.
def makePostWsr(serviceUrl, jsonBuilder) {
try {
def rest = new RestBuilder(connectTimeout:connectTimeout, readTimeout:readTimeout)
def response = rest.post("$wsUrl/$serviceUrl") {
header 'X-LSS-Env', 'devmo'
accept "application/json"
contentType "application/json"
json jsonBuilder
}
response
} catch (Exception e) {
println "== problem makePostWsr on $serviceUrl"
null
}
}

Grails URL id field not getting mapped to params

Here is my URLmappings.groovy
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?(.${format})?" {
constraints {
// apply constraints here
}
}
"/ewhet/$id"(controller : "ewhet", action : "show")
"/"(view: "/index")
"500"(view: '/error')
}
}
Here is my ewhetController's show action:
class EwhetController {
def index(){
}
def show(){
def ctx = startAsync()
ctx.start {
render params
//render "this invoked!!"
ctx.complete()
}
}
}
Now when I enter the url as: http://localhost:8080/g24/ewhet/abc
The abc does not get mapped to the params.id and when I render params, I get an empty map [:] . In case if url is entered as http://localhost:8080/g24/ewhet/show?id=abc the id field gets mapped to the params.id and I get:
['id':'abc']
So I just want to get the last part of the url mapped to the id parameter in params map without using any map in the url (like id=abc) as per Section 7.4.3 in Grails documentation So how is that possible and why is my approach not working?
Kindly note that I do not have any domain classes as I am using schemaless mongodb at my backend.
Try to reload the app after changing the UrlMappings.groovy to assure the new config is correctly loaded.

Grails REST Client Plugin - PUT body content

Documentation seems to be lacking on both the plugin side as well as the HTTPBuilder side of things. I'm trying to submit some json through the put method, but it keeps telling me that put() doesn't like the map I am feeding it.
Does anyone have an example of a PUT using the Grails REST Client plugin? Here is what I've tried:
withHttp(uri: "http://foo/doo/roo") {
def bodyContent = [
pano: jsonText
]
def json = put(body: bodyContent)
if (json.stat == 'ok') {
wsr.success = true
}
}
Error:
No signature of method: com.wbr.pano.PanService.put() is applicable for argument types: (java.util.LinkedHashMap) values: [[body:
{
"class":"com.wbr.platform.Pano",
"errorMessage":"null",
"imageSize":0,
"id":26,
"completed":"2011-03-20 3:50:27.257",
"downloading":"2011-03-20 3:49:12.269",
"processing":"2011-03-20 3:49:42.911",
"uploading":"2011-03-20 3:50:12.107"
}
]]
HTTPBuilder doesn't have a put method. Try changing withHttp to withRest so that your statements are executed with the RESTClient. Also, I think by default the body is encoded as URL encoded, so you might need to specify requestContentType: groovyx.net.http.ContentType.JSON as another parameter to your put.
import static groovyx.net.http.ContentType.*
withRest(uri: "http://foo/doo/roo") {
def bodyContent = [
pano: jsonText
]
def json = put(body: bodyContent, requestContentType: JSON)
if (json.status == 200) {
wsr.success = true
}
}