Sent parameter is not present even if I entered it via Postman - spring

In the controller :
#RestController
#RequestMapping("gmao/liaison_alfred")
#CrossOrigin(origins={"*"})
public class LiaisonAlfredGmaoController {
...
#PostMapping("/create_interv_auto_from_alfred_DT_sans_NI")
public ResponseEntity<ResultatPDD> createIntervAutoFromAlfredDTsansNI(#RequestParam("matriculechauffeur") String matriculechauffeur, #RequestParam("refdemandetransport") String refdemandetransport,
#RequestParam("nbkmaller") Double nbkmaller, #RequestParam("nbkmretour") Double nbkmretour,
#RequestParam("moyendetransport") String moyendetransport, #RequestParam("lieudedepart") String lieudedepart) {
try {
Intervention i = interventionService.createIntervAutoFromAlfredDTsansNI(matriculechauffeur, refdemandetransport, nbkmaller, nbkmretour, moyendetransport, lieudedepart);
ResultatPDD resultat = new ResultatPDD();
resultat.setStatus(HttpStatus.OK.toString());
resultat.setData("Numero intervention : " + i.getInterventionId());
resultat.setMessage("");
return new ResponseEntity<ResultatPDD>(resultat, HttpStatus.OK);
} catch (Exception e) {
ResultatPDD resultat = new ResultatPDD();
resultat.setStatus(HttpStatus.NOT_MODIFIED.toString());
resultat.setData(null);
resultat.setMessage(e.getMessage());
return new ResponseEntity<ResultatPDD>(resultat, HttpStatus.NOT_MODIFIED);
}
}
}
In Postman I make a POST request with the URL http://localhost:8080/gmao-0.0.1-SNAPSHOT/gmao/liaison_alfred/create_interv_auto_from_alfred_DT_sans_NI :
{
"matriculechauffeur": "xxxx",
"refdemandetransport": "DT-202108100950-1",
"nbkmaller": 12.5,
"nbkmretour": 12.5,
"moyendetransport": "vam",
"lieudedepart": "here"
}
But I get error :
"exception": "org.springframework.web.bind.MissingServletRequestParameterException",
"message": "Required String parameter 'matriculechauffeur' is not present"
So what is wrong ? The Maven I am using is 3.6.3

It seems you are sending it as the body of the request but the backend expects parameters so either change your request to http://<URL>/?matriculechauffeur=xxx&refdemandetransport=yyyy... or create a POJO
to hold the object
class Voyage {
private String matriculechauffeur;
private String refdemandetransport;
private Double nbkmaller;
private Double nbkmretour;
private String moyendetransport;
private String lieudedepart;
//getter and setters
}
and change to
public ResponseEntity<ResultatPDD> createIntervAutoFromAlfredDTsansNI(#RequestBody Voyage voyage){
...
}

these are request param, you have to put them like this
url?paramName1=value1&paramName2=value2
your example
http://localhost:8080/gmao-0.0.1-SNAPSHOT/gmao/liaison_alfred/create_interv_auto_from_alfred_DT_sans_NI?matriculechauffeur=xxxx,

Related

Spring WebFlux Mono.block() is not returning any response even no time out occured

there i had tried to get a Mono from my repository of Mongo. But my debugger is lost after receiving the response from repo as Mono object and applied block() to it.
HERE IS THE DETAILED CODE...
private Map<String, Object> parameters(Bid bid,String tripId) {
final Map<String, Object> parameters = new HashMap<>();
ShipperLoad shipperLoad = (ShipperLoad)bid.getLoad();
// getting supplier data from other service..
SupplierUserDTO supplier =
WebClient.
create("http://localhost:8888/XXXXXXXX/"+bid.getSupplierId())
.get()
.retrieve()
.bodyToMono(SupplierUserDTO.class)
.block();
TripInvoiceDetails tripInvoice = bidService.getInvoiceDetails(tripId).block();
parameters.put("load", shipperLoad);
parameters.put("bid", bid);
parameters.put("logo", getClass().getResourceAsStream(logo_path));
parameters.put("supplier", supplier);
parameters.put("invoice", tripInvoice);
return parameters;
}
Bid service method:
public Mono<TripInvoiceDetails> getInvoiceDetails(String tripId)
{
Mono<TripInvoiceDetails> invoice = tripInvoiceRepository.findByTripId(tripId);
return invoice;
}
Repository
public interface TripInvoiceRepository extends ReactiveMongoRepository<TripInvoiceDetails, String>{
Mono<TripInvoiceDetails> findByTripId(String tripId);
}
The control is lossing on bidService.getInvoiceDetails(tripId).block();
TripInvoiceDetails.java
#Data
#Document("tripInvoice")
#NoArgsConstructor
#AllArgsConstructor
public class TripInvoiceDetails {
#Id
String id;
String invoiceNo;
Double invoiceamount;
Double invoiceamountGst;
ValueLabel packageType;
Integer noOfUnits;
ValueLabel materialType;
List<ValueLabel> materialTypeSecondary;
String hsnCode;
String consigneeName;
String address;
String gstOrPan;
String tripId;
String transporterId;
String shipperName;
String loadId;
}
console log
Resolved
try {
//pls don't remove futureData since. it is required to resolve futureData of Mono..
CompletableFuture<TripInvoiceDetails> futureData = tripInvoice.toFuture();
invoice = tripInvoice.toFuture().get();
} catch (Exception e) {
e.printStackTrace();
}
Thanks all for your valuable response. Finally, I got a solution, I just break the .bock() method into steps, which means the did the exact implementation of the .block() method instead of calling .block() it works for me, maybe the block in the earlier case is waiting for any producer which may be in this case not marked as a producer.
try {
//pls don't remove futureData since. it is required to resolve futureData of Mono..
CompletableFuture<TripInvoiceDetails> futureData = tripInvoice.toFuture();
invoice = tripInvoice.toFuture().get();
} catch (Exception e) {
e.printStackTrace();
}

REST API in Spring Boot Application not throwing Bad request though I am passing incorrect properties of the input POJO

This is my REST API:
#RequestMapping(value="/test1")
public String test1(#RequestBody TestPOJO tpj) {
if(tpj instanceof TestPOJO) {
System.out.println("Correct data format passed ");
}
return "working1";
}
This is structure of TestPOJO:
public class TestPOJO {
private String x;
private String y;
public String getX() {
return x;
}
public void setX(String x) {
this.x = x;
}
public String getY() {
return y;
}
public void setY(String y) {
this.y = y;
}
}
In POSTMAN client I am passing this requestbody to this API:
{
"sd":"u",
"egergdrg":34
}
Its printing "Correct data format passed ".Why this input structure is taken as a instance of "TestPOJO". I am passing completely different property inside the body even with different type of data. Isn't it supposed to give 400 Bad Request error ? Why its running successfully? How can I validate all the incoming request body is having proper structure similar to input parameter and if not return 400 error?
FYI Dependency added in pom.xml are "spring-boot-starter-web", "spring-boot-starter-test", "spring-boot-devtools".
You can validate your request body. Just annotate your reqúest body like this #Valid #RequestBody TestPOJO tpj then in your POJO Class, add e.g. following annotations to the fields:
#Size(min=5, max=5)
private String x;
which would actually check the length of the String x.
You can use more annotations, relying in the javax.annotation package.
Keep in minde that you need an annotation processor to make it work, e.g. hibernate-validator-annotation-processor
You can refer to this tutorial - Validating Form Input on Spring official website.
I modified your source as follows and tested it with Postman, now it will return 400 (Bad Request). And I don't think it should return 401 for wrong input data.
Your TestPOJO:
public class TestPOJO {
#NotNull
private String x;
#NotNull
private String y;
public String getX() {
return x;
}
public void setX(String x) {
this.x = x;
}
public String getY() {
return y;
}
public void setY(String y) {
this.y = y;
}
}
Your REST API:
#RequestMapping(value="/test1")
public String test1(#Valid #RequestBody TestPOJO tpj) {
if(tpj instanceof TestPOJO) {
System.out.println("Correct data format passed ");
}
return "working1";
}
BTW, once you add the dependency of spring-boot-starter-web into your pom file, the hibernate-validator will be added automatically.
this is because of the updates on the latest springboot version regarding the object mapper FailOnUnknownProperties. if you add this bean to your configuration, you will get 400 as expected
#Bean
public Jackson2ObjectMapperBuilder mapperBuilder() {
return new Jackson2ObjectMapperBuilder().failOnUnknownProperties(true);
}

Spring Cloud Stream default custom message headers

Is there a way to configure the default Message<T> headers when the message is generated from the method return value:
#Publisher(channel = "theChannelname")
public MyObject someMethod(Object param) {
...
return myObject;
}
or
#SendTo("theChannelname")
public MyObject someMethod(Object param) {
...
return myObject;
}
In the examples above the Message<MyObject> will be automatically generated.
So, how can I control the default message generation?
Not really - the assumption is that if you return a payload then you don't care much about the headers. You can have the method return a Message and add your own headers there.
You can do that via #Header annotation for the method arguments:
#Publisher(channel="testChannel")
public String defaultPayload(String fname, #Header("last") String lname) {
return fname + " " + lname;
}
http://docs.spring.io/spring-integration/reference/html/message-publishing.html#publisher-annotation

Template variables with ControllerLinkBuilder

I want my response to include this:
"keyMaps":{
"href":"http://localhost/api/keyMaps{/keyMapId}",
"templated":true
}
That's easy enough to achieve:
add(new Link("http://localhost/api/keyMaps{/keyMapId}", "keyMaps"));
But, of course, I'd rather use the ControllerLinkBuilder, like this:
add(linkTo(methodOn(KeyMapController.class).getKeyMap("{keyMapId}")).withRel("keyMaps"));
The problem is that by the time the variable "{keyMapId}" reaches the UriTemplate constructor, it's been included in an encoded URL:
http://localhost/api/keyMaps/%7BkeyMapId%7D
So UriTemplate's constructor doesn't recognise it as containing a variable.
How can I persuade ControllerLinkBuilder that I want to use template variables?
It looks to me like the current state of Spring-HATEOAS doesn't allow this via the ControllerLinkBuilder (I'd very much like to be proven wrong), so I have implemented this myself using the following classes for templating query parameters:
public class TemplatedLinkBuilder {
private static final TemplatedLinkBuilderFactory FACTORY = new TemplatedLinkBuilderFactory();
public static final String ENCODED_LEFT_BRACE = "%7B";
public static final String ENCODED_RIGHT_BRACE = "%7D";
private UriComponentsBuilder uriComponentsBuilder;
TemplatedLinkBuilder(UriComponentsBuilder builder) {
uriComponentsBuilder = builder;
}
public static TemplatedLinkBuilder linkTo(Object invocationValue) {
return FACTORY.linkTo(invocationValue);
}
public static <T> T methodOn(Class<T> controller, Object... parameters) {
return DummyInvocationUtils.methodOn(controller, parameters);
}
public Link withRel(String rel) {
return new Link(replaceTemplateMarkers(uriComponentsBuilder.build().toString()), rel);
}
public Link withSelfRel() {
return withRel(Link.REL_SELF);
}
private String replaceTemplateMarkers(String encodedUri) {
return encodedUri.replaceAll(ENCODED_LEFT_BRACE, "{").replaceAll(ENCODED_RIGHT_BRACE, "}");
}
}
and
public class TemplatedLinkBuilderFactory {
private final ControllerLinkBuilderFactory controllerLinkBuilderFactory;
public TemplatedLinkBuilderFactory() {
this.controllerLinkBuilderFactory = new ControllerLinkBuilderFactory();
}
public TemplatedLinkBuilder linkTo(Object invocationValue) {
ControllerLinkBuilder controllerLinkBuilder = controllerLinkBuilderFactory.linkTo(invocationValue);
UriComponentsBuilder uriComponentsBuilder = controllerLinkBuilder.toUriComponentsBuilder();
Assert.isInstanceOf(DummyInvocationUtils.LastInvocationAware.class, invocationValue);
DummyInvocationUtils.LastInvocationAware invocations = (DummyInvocationUtils.LastInvocationAware) invocationValue;
DummyInvocationUtils.MethodInvocation invocation = invocations.getLastInvocation();
Object[] arguments = invocation.getArguments();
MethodParameters parameters = new MethodParameters(invocation.getMethod());
for (MethodParameter requestParameter : parameters.getParametersWith(RequestParam.class)) {
Object value = arguments[requestParameter.getParameterIndex()];
if (value == null) {
uriComponentsBuilder.queryParam(requestParameter.getParameterName(), "{" + requestParameter.getParameterName() + "}");
}
}
return new TemplatedLinkBuilder(uriComponentsBuilder);
}
}
Which embeds the normal ControllerLinkBuilder and then uses similar logic to parse for #RequestParam annotated parameters that are null and add these on to the query parameters. Also, our client resuses these templated URIs to perform further requests to the server. To achieve this and not need to worry about stripping out the unused templated params, I have to perform the reverse operation (swapping {params} with null), which I'm doing using a custom Spring RequestParamMethodArgumentResolver as follows
public class TemplatedRequestParamResolver extends RequestParamMethodArgumentResolver {
public TemplatedRequestParamResolver() {
super(false);
}
#Override
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest webRequest) throws Exception {
Object value = super.resolveName(name, parameter, webRequest);
if (value instanceof Object[]) {
Object[] valueAsCollection = (Object[])value;
List<Object> resultList = new LinkedList<Object>();
for (Object collectionEntry : valueAsCollection) {
if (nullifyTemplatedValue(collectionEntry) != null) {
resultList.add(collectionEntry);
}
}
if (resultList.isEmpty()) {
value = null;
} else {
value = resultList.toArray();
}
} else{
value = nullifyTemplatedValue(value);
}
return value;
}
private Object nullifyTemplatedValue(Object value) {
if (value != null && value.toString().startsWith("{") && value.toString().endsWith("}")) {
value = null;
}
return value;
}
}
Also this needs to replace the existing RequestParamMethodArgumentResolver which I do with:
#Configuration
public class ConfigureTemplatedRequestParamResolver {
private #Autowired RequestMappingHandlerAdapter adapter;
#PostConstruct
public void replaceArgumentMethodHandlers() {
List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<HandlerMethodArgumentResolver>(adapter.getArgumentResolvers());
for (int cursor = 0; cursor < argumentResolvers.size(); ++cursor) {
HandlerMethodArgumentResolver handlerMethodArgumentResolver = argumentResolvers.get(cursor);
if (handlerMethodArgumentResolver instanceof RequestParamMethodArgumentResolver) {
argumentResolvers.remove(cursor);
argumentResolvers.add(cursor, new TemplatedRequestParamResolver());
break;
}
}
adapter.setArgumentResolvers(argumentResolvers);
}
}
Unfortunately, although { and } are valid characters in a templated URI, they are not valid in a URI, which may be a problem for your client code depending on how strict it is. I'd much prefer a neater solution built into Spring-HATEOAS!
With latest versions of spring-hateoas you can do the following:
UriComponents uriComponents = UriComponentsBuilder.fromUri(linkBuilder.toUri()).build();
UriTemplate template = new UriTemplate(uriComponents.toUriString())
.with("keyMapId", TemplateVariable.SEGMENT);
will give you: http://localhost:8080/bla{/keyMapId}",
Starting with this commit:
https://github.com/spring-projects/spring-hateoas/commit/2daf8aabfb78b6767bf27ac3e473832c872302c7
You can now pass null where path variable is expected. It works for me, without workarounds.
resource.add(linkTo(methodOn(UsersController.class).someMethod(null)).withRel("someMethod"));
And the result:
"someMethod": {
"href": "http://localhost:8080/api/v1/users/{userId}",
"templated": true
},
Also check related issues: https://github.com/spring-projects/spring-hateoas/issues/545
We've run into the same problem. General workaround is we have our own LinkBuilder class with a bunch of static helpers. Templated ones look like this:
public static Link linkToSubcategoriesTemplated(String categoryId){
return new Link(
new UriTemplate(
linkTo(methodOn(CategoryController.class).subcategories(null, null, categoryId))
.toUriComponentsBuilder().build().toUriString(),
// register it as variable
getBaseTemplateVariables()
),
REL_SUBCATEGORIES
);
}
private static TemplateVariables getBaseTemplateVariables() {
return new TemplateVariables(
new TemplateVariable("page", TemplateVariable.VariableType.REQUEST_PARAM),
new TemplateVariable("sort", TemplateVariable.VariableType.REQUEST_PARAM),
new TemplateVariable("size", TemplateVariable.VariableType.REQUEST_PARAM)
);
}
This is for exposing the parameters of a controller response of a PagedResource.
then in the controllers we call this an append a withRel as needed.
According to this issue comment, this will be addressed in an upcoming release of spring-hateoas.
For now, there's a drop-in replacement for ControllerLinkBuilder available from de.escalon.hypermedia:spring-hateoas-ext in Maven Central.
I can now do this:
import static de.escalon.hypermedia.spring.AffordanceBuilder.*
...
add(linkTo(methodOn(KeyMapController.class).getKeyMap(null)).withRel("keyMaps"));
I pass in null as the parameter value to indicate I want to use a template variable. The name of the variable is automatically pulled from the controller.
I needed to include a link with template variables in the root of a spring data rest application, to get access via traverson to an oauth2 token. This is working fine, maybe useful:
#Component
class RepositoryLinksResourceProcessor implements ResourceProcessor<RepositoryLinksResource> {
#Override
RepositoryLinksResource process(RepositoryLinksResource resource) {
UriTemplate uriTemplate = new UriTemplate(
ControllerLinkBuilder.
linkTo(
TokenEndpoint,
TokenEndpoint.getDeclaredMethod("postAccessToken", java.security.Principal, Map )).
toUriComponentsBuilder().
build().
toString(),
new TemplateVariables([
new TemplateVariable("username", TemplateVariable.VariableType.REQUEST_PARAM),
new TemplateVariable("password", TemplateVariable.VariableType.REQUEST_PARAM),
new TemplateVariable("clientId", TemplateVariable.VariableType.REQUEST_PARAM),
new TemplateVariable("clientSecret", TemplateVariable.VariableType.REQUEST_PARAM)
])
)
resource.add(
new Link( uriTemplate,
"token"
)
)
return resource
}
}
Based on the previous comments I have implemented a generic helper method (against spring-hateoas-0.20.0) as a "temporary" workaround. The implementation does consider only RequestParameters and is far from being optimized or well tested. It might come handy to some other poor soul traveling down the same rabbit hole though:
public static Link getTemplatedLink(final Method m, final String rel) {
DefaultParameterNameDiscoverer disco = new DefaultParameterNameDiscoverer();
ControllerLinkBuilder builder = ControllerLinkBuilder.linkTo(m.getDeclaringClass(), m);
UriTemplate uriTemplate = new UriTemplate(UriComponentsBuilder.fromUri(builder.toUri()).build().toUriString());
Annotation[][] parameterAnnotations = m.getParameterAnnotations();
int param = 0;
for (Annotation[] parameterAnnotation : parameterAnnotations) {
for (Annotation annotation : parameterAnnotation) {
if (annotation.annotationType().equals(RequestParam.class)) {
RequestParam rpa = (RequestParam) annotation;
String parameterName = rpa.name();
if (StringUtils.isEmpty(parameterName)) parameterName = disco.getParameterNames(m)[param];
uriTemplate = uriTemplate.with(parameterName, TemplateVariable.VariableType.REQUEST_PARAM);
}
}
param++;
}
return new Link(uriTemplate, rel);
}

ActionContext.getContext().getParameters() returns null during StrutsJUnit4TestCase

I am running a JUnit test via maven where a struts action java method is being tested that makes the following call:
// Gets this from the "org.apache.struts2.util.TokenHelper" class in the struts2-core jar
String token = TokenHelper.getTokenName();
Here is the method in "TokenHelper.java":
/**
* Gets the token name from the Parameters in the ServletActionContext
*
* #return the token name found in the params, or null if it could not be found
*/
public static String getTokenName() {
Map params = ActionContext.getContext().getParameters();
if (!params.containsKey(TOKEN_NAME_FIELD)) {
LOG.warn("Could not find token name in params.");
return null;
}
String[] tokenNames = (String[]) params.get(TOKEN_NAME_FIELD);
String tokenName;
if ((tokenNames == null) || (tokenNames.length < 1)) {
LOG.warn("Got a null or empty token name.");
return null;
}
tokenName = tokenNames[0];
return tokenName;
}
The 1st line in this method is returning null:
Map params = ActionContext.getContext().getParameters();
The next LOC down, "params.containKey(...)" throws a NullPointerException because "params" is null.
When this action is called normally, this runs fine. However, during the JUnit test, this Null Pointer occurs.
My test class looks like this:
#Anonymous
public class MNManageLocationActionTest extends StrutsJUnit4TestCase {
private static MNManageLocationAction action;
#BeforeClass
public static void init() {
action = new MNManageLocationAction();
}
#Test
public void testGetActionMapping() {
ActionMapping mapping = getActionMapping("/companylocation/FetchCountyListByZip.action");
assertNotNull(mapping);
}
#Test
public void testLoadStateList() throws JSONException {
request.setParameter("Ryan", "Ryan");
String result = action.loadStateList();
assertEquals("Verify that the loadStateList() function completes without Exceptions.",
result, "success");
}
}
The ActionContext.getContext() is at least no longer null after I switched to using StrutsJUnit4TestCase.
Any idea why .getParameters() is returning null?
You need to initialize parameters map by yourself inside your test method. Additionally if you want to get token name you need to put it in parameters map.
Map<String, Object> params = new HashMap<String, Object>();
params.put(TokenHelper.TOKEN_NAME_FIELD,
new String[] { TokenHelper.DEFAULT_TOKEN_NAME });
ActionContext.getContext().setParameters(params);

Resources