I am using spring boot, and while making a restcontroller or controller if I use the jsonobject type request then it doesnt work, whereas same works when I change type to string.
#Controller
#RequestMapping("rest/dummy")
public class CustomerController {
#GetMapping("test")
public ResponseEntity test(#RequestParam("req") JSONObject inputData) {
org.json.JSONObject response = new org.json.JSONObject();
response.put("abc", "123");
return new ResponseEntity(inputData.toString(), HttpStatus.OK);
}
pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.5.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20171018</version>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0.2</version>
</dependency>
I do want to use it both GET and POST type and also I want to use jsonobject for both request and response as the data can change on fly and its type.
In RequestParam , we send key values which added in URL,
To send Json object send it in RequestBody .
Use #RequestBody and send your Json in body part of your request.
Using real POJOs as params and return values is the better approach imo. Use Jackson annotations to configure those POJOs.
Anyways. This should work:
#GetMapping("test")
public ResponseEntity<String> test(#RequestParam("req") JSONObject inputData) {
org.json.JSONObject response = new org.json.JSONObject();
response.put("abc", "123");
return ResponseEntity.ok(inputData.toString());
}
alternatively
#GetMapping("test")
public ResponseEntity<SomeOutputDto> test(#RequestParam("req") String inputData) {
SomeOutputDto out = new SomeOutputDto();
out.setAbc(123);
return ResponseEntity.ok(dto);
}
this requires a additional class: SomeOutputDto, but on the other hand, you have more control over your code.
public class SomeOutputDto {
private int abc = 0;
public void setAbc(int v) {
this.abc = v;
}
public int getAbc() { return this.abc; }
}
Got it working by using apache-tomcat 8.0.15, the same doesnt work with apache-tomcat 8.0.49
Related
In my spring boot rest api I am using a POST method. I am using Azure AD to authenticate api. When hitting an endpoint it is giving status as 200 OK but not doing the required POST operations. Even loggers are not getting printed from the controller #PostMapping
Can some help what needs to be fixed ...
In POM spring security and below dependency.
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-spring-boot-starter</artifactId>
</dependency>
Registered required properties in properties file.
azure.activedirectory.session-stateless
azure.activedirectory.tenant-id
azure.activedirectory.user-group.allowed-groups
spring.security.oauth2.client.registration.azure.client-id
spring.security.oauth2.client.registration.azure.client-secret
NOTE: There is no front end as of now.
If you use #PostMapping to authenticate for access token, you don't need to use azure-spring-boot-starter. You could refer the code sample based on auth code flow:
Controller:
#PostMapping("/access_token")
public AuthenticationResult authorizeToken(#RequestBody #Valid AuthorizationRequest authorizationCode) throws Exception {
return tokenService.getAccessTokenFromAuthorizationCode(authorizationCode.getCode(), authorizationCode.getRedirectUri());
}
Service:
public AuthenticationResult getAccessTokenFromAuthorizationCode(String authorizationCode, String redirectUri) throws Exception {
AuthorizationCode request = new AuthorizationCode(authorizationCode);
try {
return tokenGenerator.getAccessToken(request, redirectUri);
} catch (Throwable throwable) {
return throwException(throwable);
}
}
TokenGenerator function:
public AuthenticationResult getAccessToken(
AuthorizationCode authorizationCode, String currentUri)
throws Throwable {
String authCode = authorizationCode.getValue();
ClientCredential credential = new ClientCredential(clientId,
clientSecret);
AuthenticationContext context = null;
AuthenticationResult result = null;
ExecutorService service = null;
try {
service = Executors.newFixedThreadPool(1);
context = new AuthenticationContext(authority + tenant + "/", true,
service);
Future<AuthenticationResult> future = context
.acquireTokenByAuthorizationCode(authCode, new URI(
currentUri), credential, resource, null);
result = future.get();
} catch (ExecutionException e) {
throw e.getCause();
} finally {
service.shutdown();
}
if (result == null) {
throw new ServiceUnavailableException(
"authentication result was null");
}
return result;
}
pom.xml
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
</dependency>
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>adal4j</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>oauth2-oidc-sdk</artifactId>
<version>4.5</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
application.properties
security.oauth2.client.clientId=xxx
security.oauth2.client.clientSecret=xxx
security.oauth2.client.tenant=xxx
security.oauth2.client.accessTokenUri=https://login.microsoftonline.com/<tenant-id>/oauth2/token
security.oauth2.client.userAuthorizationUri=https://login.microsoftonline.com/<tenant-id>/oauth2/authorize
security.oauth2.client.authority=https://login.microsoftonline.com/
security.oauth2.client.resource=https://graph.windows.net/ // scope of API
security.oauth2.resource.userInfoUri=https://graph.windows.net/me?api-version=1.6 // call API
If you would like to authenticate in backend with Spring Boot Starter, please refer to this example based on implicit grant flow.
I am getting the exception in the title, even after adding all the code found online in other solutions (I've added the HttpMessageConverters as well as the APPLICATION_JSON accept header.)
public MyObject myMethod(String arg) {
String uri = BASE_URI + "/var?arg=" + arg;
restTemplate.setMessageConverters(getMessageConverters());
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<MyObject> response = restTemplate.exchange(uri, HttpMethod.GET, entity, MyObject.class);
MyObject resource = response.getBody();
return resource;
}
private List<HttpMessageConverter<?>> getMessageConverters() {
List<HttpMessageConverter<?>> converters =
new ArrayList<>();
converters.add(new MappingJackson2HttpMessageConverter());
converters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
return converters;
}
MyObject:
public class MyObject {
private String test;
//more fields
#JsonCreator
public MyObject(String test) { //more args
this.test = test;
//more assignments
}
Anyone have any idea?
EDIT: relevant dependencies in pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
By default spring or springboot configures the following message converters during startup :
ByteArrayHttpMessageConverter – converts byte arrays
StringHttpMessageConverter – converts Strings
ResourceHttpMessageConverter – converts org.springframework.core.io.Resource for any type of octet stream
SourceHttpMessageConverter – converts javax.xml.transform.Source
FormHttpMessageConverter – converts form data to/from a MultiValueMap<String, String>.
Jaxb2RootElementHttpMessageConverter – converts Java objects to/from XML
MappingJackson2HttpMessageConverter – converts JSON
MappingJacksonHttpMessageConverter – converts JSON
AtomFeedHttpMessageConverter – converts Atom feeds
RssChannelHttpMessageConverter – converts RSS feeds
But for the Jackson converters to be added , spring has to detect that jackson is present in the classpath, so by adding jackson dependency to your application,the converter should be automatically configured, unless you are explicitly preventing the auto configuration by using the #EnableWebMVC annotation.
Also,ensure that if you are using a rest endpoint, the method is annotated correctly, that is either use #RestController for the class or else you will have to provide the #ResponseBody annotation to indicate spring that it is a rest endpoint.
From the documentation:
Annotation indicating a method parameter should be bound to the body of the web request. The body of the request is passed through an
HttpMessageConverter to resolve the method argument depending on the
content type of the request. Optionally, automatic validation can be
applied by annotating the argument with #Valid. Supported for
annotated handler methods in Servlet environments.
Note that for the HttpMessageConverter to be eligible for mapping, it's canRead method must return true.
In the case of AbstractJackson2HttpMessageConverter (parent of MappingJackson2HttpMessageConverter), it checks not only MediaType, but also if Jackson can deserialize the object.
#Override
public boolean canRead(Type type, #Nullable Class<?> contextClass, #Nullable MediaType mediaType) {
if (!canRead(mediaType)) {
return false;
}
JavaType javaType = getJavaType(type, contextClass);
AtomicReference<Throwable> causeRef = new AtomicReference<>();
if (this.objectMapper.canDeserialize(javaType, causeRef)) {
return true;
}
logWarningIfNecessary(javaType, causeRef.get());
return false;
}
Now, I believe your JsonCreator is incorrect.
NOTE: when annotating creator methods (constructors, factory methods), method must either be:
Single-argument constructor/factory method without JsonProperty annotation for the argument: if so, this is so-called "delegate creator", in which case Jackson first binds JSON into type of the argument, and then calls creator. This is often used in conjunction with JsonValue (used for serialization).
Constructor/factory method where every argument is annotated with either JsonProperty or JacksonInject, to indicate name of property to bind to
See also Why when a constructor is annotated with #JsonCreator, its arguments must be annotated with #JsonProperty?
I am trying to pass a parameter using Angular POST request to Tomcat server, Spring Framework. Somehow I see that the parameter is there when it is sent, but it somehow does not arrive/properly retrieved on the backend. Here is the Angular2 code:
addCompany() {
console.log("addCompany button clicked!");
console.log("company name: " + this.input);
let nameId = this.input;
let body = JSON.stringify({ input: nameId });
let headers = new Headers({ 'Content-Type': 'application/json', 'X-CSRF-TOKEN':this.getToken() });
console.log("csrf token: " + this.getToken());
let options = new RequestOptions({ headers: headers });
this.http.post('http://localhost:8080/views/addcompany', body, options).toPromise()
.then(() => {
console.log("company added!");
this.reloadList();
});;
}
When I am trying to get it in Spring I am getting null for the parameter:
#RequestMapping(value = "/addcompany", method = RequestMethod.POST)
#ResponseBody
public void addCompany(HttpServletRequest request,
HttpServletResponse response) {
String nameId = request.getParameter("input");
eventService.addCompany(nameId);
}
I tried also this way:
#RequestMapping(value = "/addcompany", method = RequestMethod.POST)
#ResponseBody
public void addCompany(Model model, #RequestParam("input") String nameId) {
eventService.addCompany(nameId);
}
And in Angular code I have been trying to change commas everywhere, like:
let nameId = this.input;
let body = JSON.stringify({ 'input': nameId });
etc.
I tried this one: Angular2 - Http POST request parameters
Following the suggestion JB Nizet I tried to create POJO:
public class Company {
public String input;
public Company() {
this.input = "";
}
public String getInput() {
return this.input;
}
public void setInput(String input) {
this.input = input;
}
}
Register it in my #Configuration file:
#Bean
public Company getCompany(){
return new Company();
}
And changed the request method to the following:
#RequestMapping(value = "/addcompany", method = RequestMethod.POST)
#ResponseBody
public void addCompany(Company company) {
eventService.addCompany(company.input);
}
After that I am getting Company object in the method with input=null.
Then I tried to deregister the Company #Bean from #Configuration and change the request method to the following:
#RequestMapping(value = "/addcompany", method = RequestMethod.POST)
#ResponseBody
public void addCompany(#RequestBody Company company) {
eventService.addCompany(company.input);
}
But after that I am getting 415 (Unsupported Media Type) error.
In pom.xml I have the following jackson import:
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.10</version>
</dependency>
Substituting it for second jackson version solved the issue:
<properties>
...
<jackson.version>2.7.5</jackson.version>
</properties>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
You're sending a JSON object as body, and you expect to get a request parameter containing an attribute of this JSON object.
That won't happen. You need a POJO that matches with the structure of the JSON sent, take that POJO as argument and annotate it with #RequestBody. Jackson will unmarshal the JSON to the POJO and the POJO will be passed to your method.
Request parameters can be used if the request contains an application/x-www-form-urlencoded payload: the kind of payload you send when submitting a HTML form, without doing any JavaScript.
Instead of
let body = JSON.stringify({ input: nameId });
try
let body = { input: nameId };
Try to use :
let body:string='input='+nameId;
And use this header
let headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded', 'X-CSRF-TOKEN':this.getToken() });
Only for other readers if you want to send more than 1 parameter. use something & fro separating parameter . like below code
let body :string='username='+username+'&password='+password;
I am working on setting up REST API with Spring 4. The HTTP Message converters are present by default for JSON & XML. I try to setup two end-points, one for returning JSON & another for XML. The JSON object seems to be returned as expected but when i try to hit xml, i end up with 406 exception,
The resource identified by this request is only capable of generating responses with characteristics not acceptable according to the request "accept" headers.
I have included the Maven dependencies for both JSON & XML. Below is the Snippet of pom.xml,
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>${jaxb-api.version}</version>
</dependency>
Below is the controller code,
#RestController
#RequestMapping(value="/employee")
public class HelloController {
#RequestMapping(method=RequestMethod.GET , produces="application/json",value="/hello.json")
public List<Employee> getEmployeeJson(){
Employee emp = new Employee();
emp.setId(1);
emp.setName("x");
Employee emp1 = new Employee();
emp1.setId(2);
emp1.setName("y");
List<Employee> res = new ArrayList<Employee>();
res.add(emp);
res.add(emp1);
return res;
}
#RequestMapping(method=RequestMethod.GET , produces="application/xml",value="/hello.xml")
public List<Employee> getEmployeeXml(){
Employee emp = new Employee();
emp.setId(1);
emp.setName("x");
Employee emp1 = new Employee();
emp1.setId(2);
emp1.setName("y");
List<Employee> res = new ArrayList<Employee>();
res.add(emp);
res.add(emp1);
return res;
}
}
Do share your thoughts on what am missing here
According to the documentation you should add jackson-dataformat-xml dependency to enable response body XML serialization. In case you are using Maven just add:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
I am trying to return a Map from spring mvc controller making an ajax call but i am not getting correct resposne.
I have used mvc annotation tag in my config file and also included the jackson jar file in my library.
The requirement for me is to return Map to success of my Ajax call so i can modify a table row in html.
Code for the controller :
#RequestMapping(value="/pricingrecall.do", method=RequestMethod.POST)
#ResponseBody
public Map<Integer,String> pricingUpdate(#RequestParam(value = "opp_Code", required = false) String opp_Code,
#RequestParam(value = "ref_id", required = false) String ref_id,
ModelMap model,
HttpServletRequest request, HttpServletResponse response) throws SQLException, Exception{
String User="fe0777";
List<CrossListViewBean>updatedRow = new ArrayList<CrossListViewBean>();
//String message="";
logger.info(methodLocation+"|"+"Calling pricing recall ....");
Map<String, Object> result = new HashMap<String, Object>();
updatedRow=crossCampService.getupdatedrowListview(opp_Code, ref_id, user);
Map<Integer,String> lbean= new HashMap<Integer,String>();
lbean=crossCampService.getUpdatedDataPosition(updatedRow.get(0));
return lbean;
}
Call from Ajax:
jQuery.ajax( {
url : '/Web/pricingrecall.do',
type: "POST",
cache : false,
timeout : 60000,
data : {
opp_Code :CampId ,
ref_id : index
},
success : function(result, textStatus, request) {
if(result)
{
alert(result);
//jQuery(".note"+index).html(data);
}else
{
alert("The user session has timed out. Please log back in to the service.");
window.location.replace("logout.do");
}
},
error : function(request, textStatus, errorThrown) {
alert("The system has encountered an unexpected error or is currently unavailable. Please contact the support number above if you have any questions.");
}
});
Here in the ajax sucess i am always getting error ,it gets diverted tot he error string.
How can i get the Json from MAp in the ajax sucess
Please help
I'm using flexjson to get the json output correct. I'm attaching a sample code of mine, using flexjson. You can use this as a reference and restructure your controller method to output the correcct json. This link will help you in how to serialize a map.
#RequestMapping(value = "/{id}", headers = "Accept=application/json")
#ResponseBody
public ResponseEntity<String> findUser(#PathVariable("id") Long id) {
User user = userService.find(id);
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/json; charset=utf-8");
return new ResponseEntity<String>(user.toJson(), headers, HttpStatus.OK);
}
#Entity
public class AppUser {
#NotNull
private String firstName;
#NotNull
private String lastName;
//Getter Setter goes here
public String AppUser.toJson() {
return new JSONSerializer().exclude("*.class").serialize(this);
}
}
I used jackson-databind and with #ResponseBody annotation on controller methods, it automatically converted returned data to json successfully. If you use maven, add these dependencies to your pom.xml (jackson.version is 2.4.0 for me).
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
Otherwise, you can add the jar files to your classpath.