I have this request
#RequestMapping(value = "/test", method = RequestMethod.POST)
public void test(ModelMap modelMap, #RequestParam(value = "name") String name) {
modelMap.put("result",name);
}
When I call this request from Postman and pass the Name parameter in the request body and in the URL, the result is like this :
But if I remove the parameter from request body, the request is like this :
Why does #RequestParam annotation bind the value from the request body first? and if it doesn't exist in the body, it bind the value from URL parameters
Because it's how ServletRequest works. Behind the scene #RequestParam is using ServletRequest#getParameter. If you take a look at the java doc it clearly state that query parameter or form post data are used.
For HTTP servlets, parameters are contained in the query string or posted form data.
If there is a multiple value for instance same key in query and post data then it returns the first value in the array returned by getParameterValues.
Furthermore you are using multipart/form-data content type so spring handle it with DefaultMultipartHttpServletRequest where parameters found in the body are returned first:
#Override
public String[] getParameterValues(String name) {
String[] parameterValues = super.getParameterValues(name);
String[] mpValues = getMultipartParameters().get(name);
if (mpValues == null) {
return parameterValues;
}
if (parameterValues == null || getQueryString() == null) {
return mpValues;
}
else {
String[] result = new String[mpValues.length + parameterValues.length];
System.arraycopy(mpValues, 0, result, 0, mpValues.length);
System.arraycopy(parameterValues, 0, result, mpValues.length, parameterValues.length);
return result;
}
}
I'm trying to call a local rest webservice to test a simple login function
I have tried with a Get function and it works fine now I'm trying with Post function and I have no idea how to pass parameters to my request
import { HttpClient,HttpParams } from '#angular/common/http';
import { Injectable } from '#angular/core';
import {Storage} from "#ionic/storage";
// import { HttpParams } from '#angular/http';
/*
Generated class for the RestProvider provider.
See https://angular.io/guide/dependency-injection for more info on providers
and Angular DI.
*/
#Injectable()
export class RestProvider {
apiUrl = 'http://localhost:8080/CrunchifyTutorials/api/crunchifyService';
user : any;
constructor(public http: HttpClient, public storage: Storage) {
console.log('Hello RestProvider Provider');
//console.log(this.storage.get('user'));
}
// getUsers() {
// return new Promise(resolve => {
// this.http.get(this.apiUrl).subscribe(data => {
// resolve(data);
// this.storage.set('user', data);
// }, err => {
// console.log(err);
// });
// });
// }
//--------------------------------------------
getUsers() {
// let data = new URLSearchParams();
// data.append('user', 'khaled');
// data.append('pass', 'khaled');
//data =Object.assign(urlSearchParams, {search: "person"});
const data = new HttpParams()
.set('user', 'khaled').set('pass', 'khaled');
this.http
.post(this.apiUrl, data, {headers:{'Content-Type': 'application/json'}})
.subscribe(data => {
alert('ok');
}, error => {
console.log(error.json());
});
}
}
And the Java part
#POST
#Path("/crunchifyService")
#Produces("application/json")
#Consumes("application/json")
public Modele add (String user, String pass) throws ClassNotFoundException, SQLException{
Auth_Ctrl auth = new Auth_Ctrl();
int result =auth.connect(user, pass);
return auth.info(result);
}
the problem is that when I debug the server part all my parameters are null and there is no error in the client part
You are using #QueryParam which uses get parameters. These parameters should be included in the url. The http.post method encodes the data as application/json inside the body of the request.
You should change either one of these to match the other.
My advise would be to change the java here to consume applications/json due to the security concerns of putting a password in the url
For whom it might interest i modified my code like that
i added a class "Log" which contains two fields user and pass in my webservice following thislink
and then i modified my method like that
#POST
#Path("/crunchifyService")
#Produces("application/json")
#Consumes("application/json")
public Modele auth (Log user) throws ClassNotFoundException, SQLException{
Auth_Ctrl auth = new Auth_Ctrl();
int result =auth.connect(user.user, user.pass);
return auth.info(result);
}
and i noticed that i need to add jackson-mapper-asl to my classpath
and after that everything works fine
I'm trying to do post a string (later it will probably be an array or map) to my Jersey application but I can't get it to work.
jersey method :
#GET
#javax.ws.rs.Path("/angu/{param}")
#Consumes(MediaType.APPLICATION_JSON)
public void testAngu(#PathParam("param") String param){
}
Angular service:
services.factory('testFactory', function ($resource) {
return $resource('/app/api/folders/angu/:param', {}, {
save: {
method: 'POST',
params: {param : '#param'}
}
})
});
Angular controller :
app.controller('scanController', ['$scope', 'firstScanFactory', 'testFactory', function ($scope, firstScanFactory, testFactory) {
firstScanFactory.get({}, function (firstScanFactory) {
$scope.shows = firstScanFactory.listShows;
})
$scope.callJersey = function() {
testFactory.save("toto");
}
}]);
And finally the button for the call :
<a class="ls-sc-button default" ng-click="callJersey()">Valider</a>
What am I doing wrong ?
Your Jersey-method is annotated with #GET, however your Angular code uses method: 'POST'.
Because of that, Jersey will never select testAngu() as the matching route.
If you use the #, you need to pass the parameter as a property of an object. The #param string will tell the resource to replace :param with the property param on the object you pass it as parameters:
testFactory.save({"param": "toto"});
The API documentation says:
If the parameter value is prefixed with # then the value for that parameter will be extracted from the corresponding property on the data object (provided when calling an action method). For example, if the defaultParam object is {someParam: '#someProp'} then the value of someParam will be data.someProp.
Before I ask my question I have to say that I have read more than 20 questions and articles about this problem and none of them could solve it.
My problem is I have a restful server in java like this:
#RequestMapping (value = "/downloadByCode", method = RequestMethod.POST)
#ResponseBody
public void downloadByCode(#RequestBody final String stringRequest, final HttpServletResponse response)
{
try
{
final ObjectMapper objectMapper = new ObjectMapper();
final JsonNode jsonRequest = objectMapper.readValue(stringRequest, JsonNode.class);
// ...
// some processings here to create the result
// ....
final ServletOutputStream outputStream = response.getOutputStream();
outputStream.write(result);
// Flush the result
outputStream.flush();
}
catch (final Exception exception)
{
LOG.debug("Exception Thrown [downloadByCode]", exception);
}
}
And I have tried different ways to send a json to this server with jquery (but all of them create errors):
$.ajax({
url:"/downloadByCode",
type:"POST",
data: JSON.stringify({"name":"value"}) });
415 "errors message" : "Content type 'application/x-www-form
urlencoded;charset=UTF-8' not supported", "type" :
"HttpMediaTypeNotSupportedError"
So I tried to fix it by adding contentType:
$.ajax({
url:"/downloadByCode",
contentType:"application/json",
type:"POST",
data: JSON.stringify({"name":"value"}) });
400 "errors message" : "Could not instantiate JAXBContext for class
[class java.lang.String]: null; nested exception is
javax.xml.bind.JAXBException\n - with linked
exception:\n[java.lang.NullPointerException", "type"
:"HttpMessageConversionError"
I tried to send json object directly instead of JSON.stringify and it gives the same 400 error.
I tried to add different consumes to the #requestMapping but still no luck.
I tried to define my own class instead of JsonNode but it does not change anything.
Any ideas?
Please try to create new class :
public class InputData{
private String name;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
}
Then
public void downloadByCode(#RequestBody InputData stringRequest, final HttpServletResponse response)
And
$.ajax({
url:"/downloadByCode",
contentType:"application/json",
type:"POST",
data: JSON.stringify({"name":"value"}) });
try #RequestBody final Map<String, String> stringRequest
also you will need consumes = "application/json" on the #RequestMapping because you have that in your AJAX call
You will get 400 if spring doesn't like the format in which you send your ajax - I've had so much trouble with this in the past and it seems better to just ignore header types and content types unless necessary
You might try sending your response back as a ResponseEntity instead of using the HttpServletResponse directly. My hunch is that second argument, the HttpServletRequest argument, is what is causing the problem. I've never used that. I've always send my response back using the spring mvc api.
With Jersey api you can try just:
#POST
public void downloadByCode(String stringRequest)
and I think you'll find the body of your post in stringRequest.
You can take request body as string with usage of org.springframework.http.HttpEntity<String> as request type, here is example with your code as base:
#RequestMapping (value = "/downloadByCode", method = RequestMethod.POST)
#ResponseBody
public void downloadByCode(final HttpEntity<String> request, final HttpServletResponse response)
{
try
{
final ObjectMapper objectMapper = new ObjectMapper();
final JsonNode jsonRequest = objectMapper.readValue(request.getBody(), JsonNode.class);
// ...
// some processings here to create the result
// ....
final ServletOutputStream outputStream = response.getOutputStream();
outputStream.write(result);
// Flush the result
outputStream.flush();
}
catch (final Exception exception)
{
LOG.debug("Exception Thrown [downloadByCode]", exception);
}
}
But maybe it will be better to use also String as return type, if you are planning to return result as string value, like this:
#RequestMapping (value = "/downloadByCode", method = RequestMethod.POST)
#ResponseBody
public String downloadByCode(HttpEntity<String> request) {
String requestBody = request.getBody();
String result;
// ...
// some processings here to create the result text
// ....
return result;
}
I made simple application using Spring Boot with usage of proposed solutions using HttpEntity and also additional example of usage POJO, to run application you need to have Maven and JDK >= 1.7.
#clonning repository with sample
git clone git#github.com:mind-blowing/samples.git
#change current folder to sample application root
cd samples/spring-boot/rest-handler-for-plain-text
#running application using maven spring-boot plugin
mvn spring-boot:run
After application will be started you can open http://localhost:8080 and you will see html page with simple usage of JQuery to send POST requests, text of request and response will visible on html page, in controller I added two handlers, first with usage of HttpEntity and second with usage of POJO.
Controller: SampleRestController.java
HTML page: index.html
Project: https://github.com/mind-blowing/samples/tree/master/spring-boot/rest-handler-for-plain-text
First of all If you are using maven you should add dependency for jackson
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.4.1</version>
</dependency>
or you can download the jar and put it in our project class path (you can use other mapper as well)
then you should create a model or DTO class where you can map your json
public class Data{
private String name;
pubilc Data(){}
//getter and setter
}
THEN you controller
#RequestMapping (value = "/downloadByCode", method = RequestMethod.POST)
#ResponseBody
public Data downloadByCode(#RequestBody final Data data, final HttpServletResponse response)
{
//your code
return data;
}
AJAX CALL
$.ajax({
url:"/downloadByCode",
contentType:"application/json",
type:"POST",
data: JSON.stringify({"name":"value"}) });
(Optional)You can override behavior by telling object mapper not to fail on missing properties by defining the bean as follows:
#Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false));
return converter;
}
http://websystique.com/springmvc/spring-mvc-requestbody-responsebody-example/
Looking at your errors, it's clear that you have configured 'Jaxb2RootElementHttpMessageConverter' or similar XML converter in your spring configuration. And since you have registerned an XML converter, the #RequestBody and #ResponseBody work based on the registered message converters.
So, to solve your problem, go with a JSON message converter such as 'MappingJacksonHttpMessageConverter'. Once you register a JSON message converter, create a bean class to hold your json data and use it with RequestBody as below:
// It has to meet the json structure you are mapping it with
public class YourInputData {
//properties with getters and setters
}
Update 1:
Since you have defined multiple message converters, Spring tries to use the first one available by default. In order to use specific message converter(in this case Jackson converter), you should specify 'Accept' header from client like below:
$.ajax({
headers: {
"Accept" : "application/json; charset=utf-8",
"Content-Type": "application/json; charset=utf-8"
}
data: "data",
success : function(response) {
...
} })
The final answer is a combination of a number of answers/comments in this question that I am going to summarize them here:
1- You have to make sure you have an appropriate json converter in your spring config such as MappingJacksonHttpMessageConverter (credits to #java Anto)
2- You have to create a POJO class with same structure as your json object (see #Vinh Vo answer)
3- Your POJO class cannot be an inline class unless it is a static class. It means it should have its own java file or it should be static. (credits to #NTyler)
4- Your POJO class can miss parts of your json object if you set it appropriately in your object mapper (see #Aman Tuladhar answer)
5- Your ajax call requires contentType:"application/json", and you should send your data with JSON.stringify
Here is the Final code that is working perfectly:
public static class InputData
{
private String name
public String getName()
{
return name;
}
public void setName(final String name
{
this.name = name;
}
}
#RequestMapping(value = "/downloadByCode", method = RequestMethod.POST)
#ResponseBody
public void downloadByCode(#RequestBody final InputData request, final HttpServletResponse response)
{
try
{
String codes = request.getName();
// ...
// some processings here to create the result
// ....
final ServletOutputStream outputStream = response.getOutputStream();
outputStream.write(result);
// Flush the result
outputStream.flush();
}
catch (final Exception exception)
{
LOG.debug("Exception Thrown [downloadByCode]", exception);
}
}
And it is the jquery Ajax request:
$.ajax({
url:"/downloadByCode",
contentType:"application/json",
type:"POST",
data: JSON.stringify({"name":"value"}) });
Delete the #ResponseBody on your downloadByCode method
Change your method downloadByCode() return type to String and then return the String
Response body will automatically convert the returned String to JSON and then use the data appropriately
I am not that well versed with java but as much as I know your java code must be something like this.
public class downloadByCode{
#GET
#Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
public Response downloadByCode(#QueryParam("paramater1") final String parameter 1, #Context HttpServletRequest httpRequest) {
If this not helps you can keep you code somewhere and share it.
I'm a little bit confused. I'm writing an MVC application and have a simple controller like this:
#Controller
public class ProfileController {
final String DEFAULT_MALE_AVATAR = "../resources/graphics/avatarMan.PNG";
final String DEAULT_FEMALE_AVATAR = "../resources/graphics/avatarWoman.PNG";
#Autowired
UserService userService;
#RequestMapping(value = "/profile", method = RequestMethod.GET)
public String index() {
return "user/profile";
}
#RequestMapping(value = "profile/getavatar", method = RequestMethod.GET)
public #ResponseBody String getLoggedUserAvatar() {
String userMail = SecurityContextHolder.getContext()
.getAuthentication().getName();
User loggedUser;
if (userMail != null) {
loggedUser = userService.findUserByEmail(userMail);
return loggedUser.getAvatar();
} else {
return DEFAULT_MALE_AVATAR;
}
}
I've also got a simple js file called with "onload" in my body html tag while entering /profile section.
function init() {
var url = "profile/getavatar";
$.ajax({
url : url
}).then(function(data) {
avatarLink = data;
loadAvatar(avatarLink);
});
function loadAvatar(avatarLink){
$("#userAvatar").attr("src", avatarLink);
}
}
And for some strange reason I get ridirected to "profile/getavatar" and the page contains text with value returned by getLoggedUserAvatar(). The funny thing is I've also got some other controllers to other sections with almost the same js files and controllers - and they work like a charm.
What am I missing?
I hope when you hit the URL directly, you are getting expected response. If that is not happening, then there is something else wrong. If you are getting proper response when you directly hit the url in browser, then try doing the below when doing the ajax call. It passes the content type that is expecting back from the server.
function init() {
var url = "profile/getavatar";
$.ajax({
url : url,
dataType: "json"
}).then(function(data) {
avatarLink = data;
loadAvatar(avatarLink);
});
function loadAvatar(avatarLink){
$("#userAvatar").attr("src", avatarLink);
}
}
If you are using spring 4, Please make sure that you have Jakson jars in your dependency library. framework will automatically pickup the content negotiator as JSON and will find for the Jakson jars in the background to transport JSON to server and get JSON data back from server
use JAXB jars , in case you need to handle XML as content negotiator.