Request Body has one parameter such as int data. Where as if i pass extra parameter which is not there in the class it does not throw an error.
Why its able to consume the data which is not part of request body.
By default spring will recognize only the declared request params remaining parameters will be ignored. If you want to restrict it. you can do like this
Add HttpServletRequest request in the method parameter
String params = request.getQueryString();
in the method body validate the params.
But In my opinion a more pragmatic approach may be to ignore the invalid params.
Having used RESTful APIs from numerous vendors over the years, let me give you a "users" perspective.
A lot of times documentation is simply bad or out of date. Maybe a parameter name changed, maybe you enforce exact casing on the property names, maybe you have used the wrong font in your documentation .
So by default , REST API doesn't check for the extra attributes not presents in request body; otherwise checks whether the supplied attributes is present or not.
if you want not to deserialise the property which is not present; you can add validations as follows :
import java.io.Serializable;
import javax.validation.constraints.NotBlank;
public class DemoDto implements Serializable{
private static final long serialVersionUID = 1L;
private int id;
#NotBlank(message = "name can't be empty.")
private String name;
//getters & setters
}
Controller :
import javax.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class TestController {
#PostMapping("/test")
public ResponseEntity<DemoDto> test(#RequestBody #Valid DemoDto demoDto){
return ResponseEntity.ok(demoDto);
}
}
Related
I'm using Citrus 2.7.8 with Cucumber 2.4.0. I'm making a soap call and want to get the response and do some advanced parsing on it to validate a graphql response has matching values. (I understand how to do validations when it's something that just has one element, but I need something able to handle when there could be one or many elements returned (for example, 1 vehicle or 4 vehicles)). To make my validation very dynamic and able to handle many different 'quotes', I want to store the response to a Citrus variable and then make it available to java to read in the file and do the advanced parsing and validation.
The TestContext injection doesn't appear to currently work with cucumber (see https://github.com/citrusframework/citrus/issues/657) so I'm using the workaround here:
How to inject TestContext using TestRunner and cucumber to manually create the context. Without this I get a nullpointerexception on anything with the context.
I am able to use Citrus's message function to grab the soap response which is awesome. My echo statements in the console show that it successfully put the right value into the citrus variable. But I'm having problems making that available to java so that I can then open it up and parse through it.
I've scaled down my step definition file to just the pertinent code. My couple attempts are listed below along with the problems I encountered in their results.
Does anyone have any ideas on how I can successfully workaround the context issues and make my response available to java?
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import com.consol.citrus.Citrus;
import com.consol.citrus.annotations.CitrusFramework;
import com.consol.citrus.annotations.CitrusResource;
import com.consol.citrus.config.CitrusSpringConfig;
import com.consol.citrus.context.TestContext;
import com.consol.citrus.dsl.junit.JUnit4CitrusTestRunner;
import com.consol.citrus.dsl.runner.TestRunner;
import com.consol.citrus.ws.client.WebServiceClient;
import cucumber.api.java.en.When;
#ContextConfiguration(classes = CitrusSpringConfig.class)
public class CitrusSteps extends JUnit4CitrusTestRunner {
#CitrusFramework
private Citrus citrus;
#CitrusResource
private TestRunner runner;
#CitrusResource
private TestContext context;
#Autowired
private WebServiceClient getQuote;
#When("^I call getQuote with id \"([^\"]*)\"$")
public void i_call_getquote_with_id(String quoteId) throws Throwable {
context = citrus.createTestContext();
String soappayload = "my payload (taken out for privacy purposes)";
runner.soap(action -> action.client(getQuote)
.send()
.soapAction("getQuote")
.payload(soappayload));
runner.soap(action -> action.client(getQuote)
.receive()
.name("getQuoteResponseStoredMessage"));
//this bombs out on the context line with this: "com.consol.citrus.exceptions.CitrusRuntimeException: Unknown variable 'messageStoreGetQuoteResponse1'"
runner.variable("messageStoreGetQuoteResponse1", "citrus:message(getQuoteResponseStoredMessage.payload())");
runner.echo("First try: ${messageStoreGetQuoteResponse1}");
String firstTry = context.getVariable("messageStoreGetQuoteResponse1");
log.info("First Try java variable: " + firstTry);
//this bombs out on the context line with this: "com.consol.citrus.exceptions.CitrusRuntimeException: Unknown variable 'messageStoreGetQuoteResponse2'"
runner.createVariable("messageStoreGetQuoteResponse2", "citrus:message(getQuoteResponseStoredMessage.payload())");
runner.echo("Second try: ${messageStoreGetQuoteResponse2}");
String secondTry = context.getVariable("messageStoreGetQuoteResponse2");
log.info("Second Try java variable: " + secondTry);
//This stores the literal as the value - it doesn't store the message so it appears I can't use citrus functions within the context
context.setVariable("messageStoreGetQuoteResponse3", "citrus:message(getQuoteResponseStoredMessage.payload())");
String thirdTry = context.getVariable("messageStoreGetQuoteResponse3");
log.info("Third Try java variable: " + thirdTry);
}
}
A smart co-worker figured out a workaround for the injection not working w/ cucumber.
I replaced these two lines:
#CitrusResource
private TestContext context;
with these lines instead:
TestContext testContext;
public TestContext getTestContext() {
if (testContext == null) {
runner.run(new AbstractTestAction() {
#Override
public void doExecute(TestContext context) {
testContext = context;
}
});
}
return testContext;
}
Then within my step where I want the context, I can use the above method. In my case I wanted my message response, so I was able to use this and confirm that the response is now in my java variable:
String responseXML = getTestContext().getMessageStore().getMessage("getQuoteResponseStoredMessage").getPayload(String.class);
log.info("Show response XML: " + responseXML);
I am recording the application through Wiremock using JAVA DSL, Do we have the option to customize the mapping file names? instead of getting the filename which is generated from wiremock..
Example: searchpanel_arrivalairport_th-72f9b8b7-076f-4102-b6a8-aa38710fde1b.json (Generated form wiremock using java )
I am expecting the above file name with my desired naming convention like
seacrpanel_airport_LGW.json
Custom filenames can be added by customizing StubMappingJsonRecorder.
I added CustomStubMappingJsonRecorder and override writeToMappingAndBodyFile method.
if(fileName!=null && !fileName.equals("")){
mappingFileName=fileName+"-mapping.json";
bodyFileName=fileName+"-body.json";
}else {
mappingFileName = UniqueFilenameGenerator.generate(request.getUrl(),
"mapping", filed);
bodyFileName = UniqueFilenameGenerator.generate(request.getUrl(), "body",
fileId, ContentTypes.determineFileExtension(request.getUrl(),
response.getHeaders().getContentTypeHeader(), body));
}
There's no easy way to do this at the moment. It is however possible. As #santhiya-ps says you need to write your own implementation of RequestListener, probably using StubMappingJsonRecorder as a template.
You can't extend it and override writeToMappingAndBodyFile as that method is private, but that is the method you probably want to change.
import com.github.tomakehurst.wiremock.common.*;
import com.github.tomakehurst.wiremock.core.*;
import com.github.tomakehurst.wiremock.http.*;
import java.util.List;
import static com.github.tomakehurst.wiremock.core.WireMockApp.*;
class NameTemplateStubMappingJsonRecorder implements RequestListener {
private final FileSource mappingsFileSource;
private final FileSource filesFileSource;
private final Admin admin;
private final List<CaseInsensitiveKey> headersToMatch;
private final IdGenerator idGenerator = new VeryShortIdGenerator();
public NameTemplateStubMappingJsonRecorder(Admin admin) {
this.mappingsFileSource = admin.getOptions().filesRoot().child(MAPPINGS_ROOT);
this.filesFileSource = admin.getOptions().filesRoot().child(FILES_ROOT);
this.admin = admin;
this.headersToMatch = admin.getOptions().matchingHeaders();
}
#Override
public void requestReceived(Request request, Response response) {
// TODO copy StubMappingJsonRecorder changing as required...
}
}
You can then register your RequestListener as so:
WireMockServer wireMockServer = new WireMockServer();
wireMockServer.addMockServiceRequestListener(
new NameTemplateStubMappingJsonRecorder(wireMockServer)
);
wireMockServer.start();
So long as you still store the mapping files in the expected directory (stored in FileSource mappingsFileSource above, which will be ${rootDir}/mappings, where rootDir is configured as explained in Configuration - File Locations) they should be loaded successfully as all files with extension json in that dir are loaded as mappings.
It would be much easier if StubMappingJsonRecorder took a strategy for generating these names - it might be worth creating an issue on the WireMock repo asking for an easier way to do this. I'd suggest getting an agreement on a basic design before raising a PR though.
My REST endpoints return ResponseEntity<SomeDto> or only SomeDto. Out of this i generating swagger to be consumed by front end. Of course the type information SomeDto is crucial.
Within my endpoint methods i am doing some basic checks to prevent exception further down the road (fail as fast as possible).
Returning ResponseEntity.badRequest().build() in such cases works fine, but i want to add a error message to the body. But adding a String to the body is not possible in regards to ResponseEntity<SomeDto>.
Question
How i can add a error message while maintaining the signature/ contract without the need of raising an exception (see "Effective Java 3rd edition - "Item 69: Use exceptions only for exceptional conditions")?
one (bad) way i see is to add an error message field to all my dtos (could be specified by an interface).
You don't need to add error model to individual object instead extend Response model from Base class which has error definition defined, this is how i would implement it.
static abstract class ApiResponse {
enum Status {
SUCCESS,
FAILURE,
}
#Getter
#Setter
private Status status;
#Getter
#Setter
private String errorMessage;
}
class NamesDto extends ApiResponse {
#Setter
#Getter
String[] names;
}
#GetMapping(value = "namesdto")
public ResponseEntity<ApiResponse> getNames() {
final NamesDto namesDto = new NamesDto();
namesDto.setStatus(ApiResponse.Status.FAILURE);
namesDto.names = new String[]{"john", "doe"};
return ResponseEntity.ok(namesDto);
}
#GetMapping(value = "errordto")
public ResponseEntity<ApiResponse> erroDto() {
final NamesDto namesDto = new NamesDto();
namesDto.setErrorMessage("No names found");
namesDto.setStatus(ApiResponse.Status.FAILURE);
return ResponseEntity.ok(namesDto);
}
#Getter/#Setter are imports from import lombok.* package
Either using ResponseEntity.badRequest().body(xxx).build() or throwing an exception is fine, as soon as the contract is the same.
You are using a lib to generate a Swagger Doc (such as Springfox) ? Well, it does fine for some situations but it can't perform miracles. The documentation generation should not affect how you write your code. In such a case, #ApiResponse is what you need.
I mean "structural links" in the HATEOAS/hypermedia API sense. The more general question is how to augment the generated XML with data that depends on both the entity being marshalled, and also on the environment (in this case, at least the absolute URL).
I'm using Jersey 2.9 with Moxy 2.5 as the JAXB provider.
From this model:
package testing;
import java.util.ArrayList;
import java.util.List;
public class Planet {
private int id = 1;
private String name = "test";
private double radius = 3.0;
private String href;
private List<Moon> moons = new ArrayList<Moon>(0);
public void addMoon(Moon moon) {
moons.add(moon);
}
}
...plus Moon class
I want to get something like this XML (and the equivalent JSON):
<planet href="http://mytestserver/rest/planets/test">
<name>test</name>
<radius>3.0</radius>
<moons>
<moon href="http://mytestserver/rest/moons/moon1">
<name>moon1</name>
</moon>
<moon href="http://mytestserver/rest/moons/moon2">
<name>moon2</name>
</moon>
</moons>
</planet>
The model has no "href" field, nor can one be added. Ideally I could use UriBuilder to grab these paths straight from the resource classes.
So far I've come up with several possiblities. Can I ask you to consider which (if any) has the most legs, and then how you would work around the shortcomings of that method?
1. Augment the model with AspectJ (or Javassist).
And then use the existing declarative linking mechanisms in Jersey, all of which rely on there being a field in the model to receive the generated links. This obviously won't work if you don't have AspectJ in your build process and/or balk at exotic techniques like byte code manipulation.
2. Post-process the generated XML and JSON
For example, in a MessageBodyWriter:
ContextResolver<JAXBContext> resolver = providers.getContextResolver(JAXBContext.class, mediaType);
JAXBContext context = resolver.getContext(type);
Marshaller m = context.createMarshaller();
<--- here, marshall to e.g. a DOM then transform that
<--- then manipulate the JSON structures
I have absolutely no idea how to do any of that, hence the lack of code. There may be other ways to hook into the XML generation process, but as far as I can see none of Jersey's or JAXB's event handlers or interceptors actually allow you to manipulate the generated XML/JSON.
3. Use a Moxy XMLTransformationMapping
For example:
XML binding:
<java-type name="Planet" xml-customizer="testing.HrefCustomizer">
Customizer:
public class HrefCustomizer implements DescriptorCustomizer {
#Override
public void customize(ClassDescriptor descriptor) throws Exception {
XMLTransformationMapping xtm = new XMLTransformationMapping();
xtm.addFieldTransformer("#href", new HrefWriter());
descriptor.addMapping(xtm);
}
}
Transformer:
public class HrefWriter implements FieldTransformer {
#Override
public Object buildFieldValue(Object instance, String fieldName,
Session session) {
return "href"; // constant value just for proof-of-concept
}
#Override
public void initialize(AbstractTransformationMapping mapping) {
// TODO Auto-generated method stub
}
}
I have two problems with this approach:
It was so hard to find any documentation on it that I wonder if it is in fact unsupported usage.
I can't see how the transformer is going to get a UriBuilder to work with. At minimum it would need the root URL of the rest service.
4. Slightly different Moxy xml-transform approach
If we decide we can't provide the transformer with any meaningful context at instantiation time, the customizer is adding no value and we can simplify the above to just this:
<java-type name="Planet">
<xml-root-element/>
<java-attributes>
<xml-transformation java-attribute="name">
<xml-write-transformer transformer-class="testing.HrefWriter" xml-path="#href"/>
</xml-transformation>
<xml-element java-attribute="name"/>
With the slight oddity that we are hanging the transformer off another field ("name", in this example).
5. ?????
Or, I'm completely barking up the wrong tree. Help!!
AspectJ approach
Synopsis
Use AspectJ to add a field to the model classes (called "href" in this example)
Add the Jersey #InjectLink annotation to that field
Jersey will then populate the field with the right URL as defined by the resource class
Specify the marshaling of the href field using an external mapping file.
You could also specify the marshaling of href by adding JAXB annotations to it via the same AspectJ intertype declaration mechanism.
Example code
These are the most informative bits. See http://lagod.id.au/blog/?p=494 for the full example.
The aspect
package testing;
import org.glassfish.jersey.linking.InjectLink;
import org.glassfish.jersey.linking.Binding;
public aspect HrefInjector {
private String Planet.href;
declare #field : * Planet.href : #InjectLink(
resource=Services.class,
style=InjectLink.Style.ABSOLUTE
) ;
private String Moon.href;
declare #field : * Moon.href : #InjectLink(
resource=Services.class,
method="moon",
bindings={#Binding(
name="moonid", value="${instance.name}"
)},
style=InjectLink.Style.ABSOLUTE
) ;
}
Model classes
POJOs with no REST-specific cruft. See Jersey + Moxy + JAXB - how to marshal XML without annotations.
package testing;
import java.util.ArrayList;
import java.util.List;
public class Planet {
private int id = 1;
private String name = "test";
private double radius = 3.0;
private List<Moon> moons = new ArrayList<Moon>(0);
public void addMoon(Moon moon) {
moons.add(moon);
}
}
package testing;
public class Moon {
private String name;
// No-arg constructor is a requirement of JAXB
public Moon() {
}
public Moon(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Resource class
This is a standard JAX-RS resource class. For demo purposes, we're just returning freshly instantiated model instances.
package testing;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
#Path("/services")
#Produces({MediaType.APPLICATION_XML,MediaType.APPLICATION_JSON})
public class Services {
private Planet initPlanet() {
Planet p = new Planet();
p.addMoon(new Moon("moon1"));
p.addMoon(new Moon("moon2"));
return p;
}
#GET
public Planet planet () {
return initPlanet();
}
#GET #Path("/moons/{moonid}")
public Moon moon (#PathParam("moonid") String name) {
return new Moon(name);
}
}
Moxy mapping file
Note that you can choose for any given type whether or not you want to actually marshal the href field. In fact, by using multiple mapping files, you can include the href field in some representations and not in others.
<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="testing"
xml-mapping-metadata-complete="true"
xml-accessor-type="NONE">
<java-types>
<java-type name="Planet">
<xml-root-element/>
<java-attributes>
<xml-attribute java-attribute="href"/>
<xml-element java-attribute="name"/>
<xml-element java-attribute="radius"/>
<xml-element java-attribute="moons" name="moon">
<xml-element-wrapper name="moons"/>
</xml-element>
</java-attributes>
</java-type>
<java-type name="Moon">
<xml-root-element/>
<java-attributes>
<xml-attribute java-attribute="href"/>
<xml-element java-attribute="name"/>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
Sample output
Ta-dah! Structural links derived automatically from the JAX-RS resource class without altering model source code. Because we're using Moxy, we also get JSON for free.
<planet href="http://localhost:8080/reststructlinks/rest/services">
<name>test</name>
<radius>3.0</radius>
<moons>
<moon href="http://localhost:8080/reststructlinks/rest/services/moons/moon1">
<name>moon1</name>
</moon>
<moon href="http://localhost:8080/reststructlinks/rest/services/moons/moon2">
<name>moon2</name>
</moon>
</moons>
</planet>
Error Description
Hey all,
I'm having trouble getting a response from my manually added controllers in a JHipster-based project. I scaffolded up the original project, and then hand-wrote my own services and controllers.
When I execute the call, the error result I get from SoapUI (which I am using for initial validation) is at the following url: http://imgur.com/04FpmEZ,Havk1EL#0
And if I look at my Eclipse console error, I see the following: http://imgur.com/04FpmEZ,Havk1EL#1
Controller
/**
* GET /courses/json -> get all the courses.
*/
#RequestMapping(value = "/json",
method = RequestMethod.GET,
produces = "application/json")
#Timed
public List<Course> getAll() {
log.debug("REST request to get all Courses");
return courseService.findAllCourses();
}
Service
package com.testapp.myapp.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.testapp.myapp.domain.Course;
import com.testapp.myapp.repository.CourseRepository;
#Service
#Transactional
public class CourseServiceImpl implements CourseService {
#Autowired
CourseRepository courseRepository;
public long countAllCourses() {
return courseRepository.count();
}
public void deleteCourse(Course course) {
courseRepository.delete(course);
}
public Course findCourse(Integer id) {
return courseRepository.findOne(id);
}
public List<Course> findAllCourses() {
return courseRepository.findAll();
}
public List<Course> findCourseEntries(int firstResult, int maxResults) {
return courseRepository.findAll(new org.springframework.data.domain.PageRequest(firstResult / maxResults, maxResults)).getContent();
}
public void saveCourse(Course course) {
courseRepository.save(course);
}
public Course updateCourse(Course course) {
return courseRepository.save(course);
}
}
What is confusing about this is that I ran the query provided by hibernate directly against my DB, and it returns the record set just fine. Is it possible that the service is being blocked due to some security or authentication constraint auto-loaded by JHipster?
A few issues existed, all related to migrating from Roo into JHipster:
I had built my new Controller class with org.sprinframework.stereotype.Controller's #Controller annotation, rather than #RestController... The original controller annotation was scaffolded up by Spring Roo (which is highly effective at generating services from an existing DB using their DBRE addon, I might add).
After switching over to #RestController, I ran into the second hurdle, which I had originally expected as a JHipster implementation : the service was being blocked due to authentication constraints.
This was fixed by going into com.[projectname].config and updating the SecurityConfiguration.java file, exposing specifically the APIs that I wanted.
Then, I had to make sure Hibernate was getting the full collection of the objects being requested (I had a lot of complex relational entities being built by Roo)... failed to lazily initialize a collection of role...
In the Domain entity, change your #OneToMany annotation as follows:
#OneToMany(fetch = FetchType.EAGER, mappedBy = "courseId", cascade = CascadeType.REMOVE)
Source of answer: Solve "failed to lazily initialize a collection of role" exception
Voila! Functioning, secure-able JSON-based APIs, fully reverse engineered from an existing Postgresql DB, loaded into a prescaffolded Angular front-end.