I have these mappings:
#GetMapping("rparam")
public void get(#RequestParam("test") String test) {
System.out.println(test);
}
#PostMapping("rparam")
public void post(#RequestParam("test") String test) {
System.out.println(test);
}
#PatchMapping("rparam")
public void patch(#RequestParam("test") String test) {
System.out.println(test);
}
#DeleteMapping("rparam")
public void delete(#RequestParam("test") String test) {
System.out.println(test);
}
#PutMapping("rparam")
public void put(#RequestParam("test") String test) {
System.out.println(test);
}
And apart from the GetMapping and PostMapping methods all the other ones fail with 400 Bad request because they can't find the test parameter.
edit:
How the requests are made (the post one works):
(stringifying the data and adding a contentType:application/json doesn't help either)
$.ajax({
url: "api/my-controller/rparam",
method: "POST",
data: {
test: "super test"
},
headers: {
"X-XSRF-TOKEN": cookie
}
}).done(function() {
console.log("super cool")
});
$.ajax({
url: "api/my-controller/rparam",
method: "PUT",
data: {
test: "super test"
},
headers: {
"X-XSRF-TOKEN": cookie
}
}).done(function() {
console.log("super cool")
});
edit: I just noticed that when I don't stringify the data and set a contentType it's sent as form data. And sending data via a form only supports post and get so maybe that has something to do with it?
Try giving some default value by changing your method:
public void delete(#RequestParam(value = "test" , defaultValue = "default") String test)
#RequestParam can work with with all kind of #RequestMapping:
#GetMapping
#PostMapping
#PutMapping
#DeleteMapping
#PatchMapping
However, about semantics, PATCH, PUT and DELETE are supposed to accept the body of the request, and then update / store / remove the object on the resource identified by the URI.
So, I recommand using #RequestBody instead of #RequestParam.
You can take a look at this very similar question.
Related
this may or may not be a somewhat long post, but I'm going to be pasting every single piece of information relating to this issue and the method I am testing, from Controller class with method to the a.jax snippet. I have asked about 4-6 developers and no one can find out the reason why its giving me a 415 error instead of a 200, because it just seems like I am doing everything correct. I just need some fresh eyes and new perspectives, hopefully someone could help me solve this. I will be pasting the classes and the relevant pieces now, and then a couple comments after the snippets.
Controller class
#Controller
#RequestMapping(value = "/user")
public class Controller
{
#Autowired
private Service service;
public Controller() {
}
#RequestMapping(value = "/landing/{age}/{name}/{title}/{family}/{gender}", method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON, produces = MediaType.APPLICATION_JSON)
public #ResponseBody String update(#RequestBody HouseModel model, #PathVariable int age, #PathVariable String name, #PathVariable String title, #PathVariable String family, #PathVariable String gender)
{
String result = service.update(model, age, name, title, family, gender);
// this will just return the string "Success" if update works or "Failed" if query is
wrong or not found
return result;
}
Service Class
#Service
public class Service
{
#Autowired
Dao dao;
public Service() {
}
public String update(HouseModel model, int age, String name, String title, String family)
{
return dao.update(HouseModel model, int age, String name, String title, String family);
}
}
Dao class
#Repository
public class Dao
{
public Dao () {
}
public String update(HouseModel model, int age, String name, String title, String family)
{
String result = "";
//some long query that updates the table and will populate result as "Success" or "Failed"
return result
}
}
Controller test class
#EnableWebMvc
#WebAppConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:applicationContext-testing.xml",
"classpath:applicationContext-EIPW.xml"})
public class ControllerTest {
#Autowired
private Controller controller;
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
#Before
public void setup() throws Exception {
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void test_update() throws Exception {
String URI = "/user/landing/22/Test/Mr/Yes/Male";
String content = "{\n \"HouseId\": 5,\n \"DateOfPurchase\": \"2019-01-01\",\n \"Price\": 100,\n \"Floors\": 5,\n \"Style\": \"Victorian\",\n}";
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.put(URI).contentType(MediaType.APPLICATION_JSON).content(content).accept(MediaType.APPLICATION_JSON);
MvcResult mvcResult = mockMvc.perform(requestBuilder).andDo(MockMvcResultHandlers.print()).andReturn();
}
j.ajax
$j.ajax({
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
type: "PUT",
async: false,
data: JSON.stringify(
buildEdit(editRecords[i], ecRecord)
),
url:
"/user/landing/22/Test/Mr/Yes/Male",
dataType: "text"
printed error message
MockHttpServletRequest:
HTTP Method = PUT
Request URI = /user/landing/22/Test/Mr/Yes/Male
Parameters = {}
Headers = {Content-Type=[application/json], Accept=[application/json]}
Handler:
Type = controller.Controller
Async:
Was async started = false
Async result = null
Resolved Exception:
Type = org.springframework.web.HttpMediaTypeNotSupportedException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
MockHttpServletResponse:
Status = 415
Error message = null
Headers = {Accept=[application/octet-stream, */*, text/plain;charset=ISO-8859-1, */*, application/xml, text/xml, application/*+xml, application/x-www-form-urlencoded, multipart/form-data]}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
Some Comments:
So I've had over 50 stack overflow tabs open relating to the same problem, and a lot of them had similar solutions that seemed so simple. Here are some, if not all of the things I did in attempts to try and solve this problem:
Switched around the content-type and accepts headers of requestBuilder to be MediaTypes of APPLICATION_JSON or APPLICATION_JSON_VALUE or ALL_VALUE, etc
Added produces or consumes = "application/json" or MediaType.APPLICATION_JSON/APPLICATION_JSON_VALUE/ALL_VALUE into the requestMapping().
Played around with a.jax to change content-type or accepts around
A couple of other things that I don't remember, but alas the 415 status is still here
I also do have setters and a default constructor in the HouseModel, and default constructors in every layer. I am 99.9% sure I have tried almost everything, if not everything, unless I am just missing something and am being stupid then yeah. I sent the request with the body as JSON raw an as:
{
"HouseId": 5,
"DateOfPurchase": "2019-01-01",
"Price": 100,
"Floors": 5,
"Style": "Victorian",
}
and it returned back success, I will attach its headers here:
[![Picture Link][1]][1]
[1]: https://i.stack.imgur.com/AqKnY.png
There is something interesting though, I did get one method to work but it required no arguments in its parameters, it was just a get method (dao calls database to store stuff in a list):
**Controller method**
#RequestMapping(value = "/levels", method = RequestMethod.POST, produces = "application/json")
public #ResponseBody String getLevels() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
// there is a long logic after this map to populate the map
Map<LevelObject, List<LevelObject>> map = new HashMap<LevelObject, List<LevelObject>>();
return mapper.writeValueAsString(map);
}
This is such a simple test and it worked perfectly fine, giving me a status 200 and my expected result.
**Test class method**
#Test
public void test_getLevels() throws Exception {
String URI = "/user/levels";
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.post(URI).accept(MediaType.APPLICATION_JSON);
MvcResult mvcResult = mockMvc.perform(requestBuilder).andExpect(status().isOk()).andReturn();
}
**j.ajax for the getLevels method**
$j.ajax({
type: "POST",
url: "user/levels",
async: false,
dataType: "json",
Thank you so much!
Though I am not a front-end developer, I am sure that problem is in below line
data: JSON.stringify(
buildEdit(editRecords[i], ecRecord)
)
Reason - I tested your code locally. Code works fine from postman, if I select request body as raw and type as JSON
But if select request body as raw and type as "TXT". I am getting same error as you.
[![enter image description here][2]][2]
So, I am sure that your request body is not being built as JSON. rather it is being sent as some other format. Hence, the error.
[2]: https://i.stack.imgur.com/cqSCC.png
Also, you can try to change dataType: "text" to dataType: "json"
Please try the below,
$j.ajax({
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
type: "PUT",
async: false,
data:
buildEdit(editRecords[i], ecRecord),
url:
"/user/landing/22/Test/Mr/Yes/Male",
dataType: "json"
Remove converting the json to String
Change the dataType to json
i have this method in my rest controller
(I can not use #RequestBody or #RequestParam for a couple of complicated reasons, so I need it this way):
#PostMapping(value="/crear")
public Resultado<Candidato> crear(Candidato candidato) throws IOException {
...Just code...
return resultado;
}
and i am trying to test it with MockMVC, but my "Candidato" is always Null in every attribute, this is my test until now:
#Test
public void crear() throws Exception {
String nombre = "Candidato Test2";
candidato.setNombre(nombre);
gsonBuilder.setDateFormat(EntitiesBuilderTestUtil.DATE_FORMAT);
when(procesoSeleccionService.findByToken(anyString())).thenReturn(EntitiesBuilderTestUtil.obtenerProcesoSeleccion());
when(candidatoValidator.validate(any(Candidato.class))).thenReturn(new Notificacion());
this.mockMvc.perform(post("/api/candidato/crear")
.content(gsonBuilder.create().toJson(candidato).getBytes())
.contentType(MediaType.APPLICATION_FORM_URLENCODED))
.andExpect(status().isOk())
.andExpect(content().contentType(IntegrationTestUtil.APPLICATION_JSON_UTF8));
}
In my ajax request i do the next:
crearCandidato: function(){
let formulario = app.funciones.obtenerFormulario();
$.ajax({
url:baseURL+"/api/candidato/crear",
type:"POST",
contentType: false,
processData: false,
data:formulario,
dataType:"JSON"
}).done(function(data){
... do something
}).fail(function(e){
... do something
});
}
my obtenerFormulario() function:
obtenerFormulario: function(){
let formdata = new FormData();
formdata.append("nombre",app.funciones.removerTagsHtml(app.$inpNombre.val()));
...more code
return formdata;
},
How could I test the method correctly?
Thanks for your help :D
-->Update
I already have other tests running correctly and as for the "Candidato", it is created correctly but the jackson creates it with null attributes
You can test REST services by using POSTman (https://www.getpostman.com/) or SOAPUI (https://www.soapui.org/).
However, I suspect the issue is with your javascript function, which you can verify by using the debug function of the chrome developer tools.
I've got Angular app and Java server.
I need to send POST request with JSON object consisting of string array and string field.
I'm using Angularjs $resource and Java javax.ws.rs.
My latest try as follows:
Client:
var messages = $resource('resources/messages/getmessages', {}, {
update: { method: 'POST', url: 'resources/messages/updatemessages' }
});
//...
var _args = { 'msgIdList': ['1', '2', '3'],
'action': 'makeSmth' };
return messages.update(_args).$promise.then(
function (data) {
//...
},
function (error) {
//...
}
)
Server:
#POST
#Path("updatemessages")
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
#Produces(MediaType.APPLICATION_JSON +"; charset=UTF-8")
public Response updateMessages( #FormParam("msgIdList") List<String> msgIdList,
#DefaultValue("") #FormParam("action") String action,
#CookieParam("rgsid") String c_sid,
#Context HttpServletRequest httpservletreq) {
//...
}
The problem is that I've got 415 Unsupported Media Type error, and don't know what to do next. I've tried lots of things, but may be I was wrong from the start, and I can't pass parameters this way?
Any help would be appreciated, thanks!
you can try this in your angular, maybe it can help.
var sendPost = $http({
method: "post",
url:"JAVA_SERVER_SERVICE_URL",
data: {
msgIdList: 'your_value',
action: 'your_value'
},
headers: { 'Content-Type': 'application/json' }
});
So, eventually I made a wrapper class, so now it looks this way:
#XmlRootElement
private static class RequestWrapper {
#XmlElement
private ArrayList<String> msgIdList;
#XmlElement
private String action;
public ArrayList<String> getMsgIdList() {
return msgIdList;
}
public void setMsgIdList(ArrayList<String> msgIdList) {
this.msgIdList = msgIdList;
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
public RequestWrapper() {
}
}
#POST
#Path("updatemessages")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON +"; charset=UTF-8")
public Response updateMessages( RequestWrapper requestData,
#CookieParam("rgsid") String c_sid,
#Context HttpServletRequest httpservletreq) {
//...}
Angular part stays unchanged.
I'm not really sure, if this the right way to go (class description and so on), but it works.
I ma using Spring MVC and trying to use jQuery. I have this on my web page:
$(document).ready(function () {
var entity = {mag: "status_key", paper: "View10"};
$("#btn").click(function () {
$.ajax({
url: "ajaxJsonPost",
type: 'post',
dataType: 'json',
data: JSON.stringify(entity),
contentType: 'application/json',
});
});
});
Spring server has this:
#RequestMapping(value = "ajaxJsonPost", method = RequestMethod.POST)
public void postJson(#RequestBody Entity en) throws IOException {
System.out.println("writing entity: " + en.toString());
}
OK, Entity cames to server. BUT browser console prints 404 not found. I know that my POST request needs any response. In the Internet I've found solution which recommends me to return ResponseEntity object, OR use annotation #ResponseStatus. They both return HttpStatus well, but I don't know in which cases I should use them. What is the best way?
#Controller
#RequestMapping("/apipath")
public class SomeController {
#RequestMapping(value = "/ajaxJsonPost", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public String postJson(#RequestBody final Entity en) {
System.out.println(en.toString());
//assuming you have a class "EntityService" and
//it has a method postData
//which takes Entity object as parameter and pushes into database.
EntityService.postData(en);
System.out.println("added");
return "success";
}
}
Entity object on the Server side
#JsonAutoDetect
public class Entity {
private String mag;
private String paper;
public String getMag() {
return mag;
}
public void setMag(final String mag) {
this.mag = mag;
}
public String getPaper() {
return paper;
}
public void setPaper(final String paper)
this.paper = paper;
}
}
ajax
$(document).ready(function () {
var entity = {mag: "status_key", paper: "View10"};
$("#btn").click(function () {
$.ajax({
url: "/apipath/ajaxJsonPost",
type: 'post',
dataType: 'json',
data: JSON.stringify(entity),
contentType: 'application/json',
success : function(response) {
alert(response);
},
error : function() {
alert('error');
}
});
});
});
And as far as why and when to use #ResponseStatus and #ResponseEntity, there is already a short and simple answer here by #Sotirios Delimanolis. When use #ResponseEntity .
It says :
ResponseEntity is meant to represent the entire HTTP response. You can
control anything that goes into it: status code, headers, and body.
#ResponseBody is a marker for the HTTP response body and
#ResponseStatus declares the status code of the HTTP response.
#ResponseStatus isn't very flexible. It marks the entire method so you
have to be sure that your handler method will always behave the same
way. And you still can't set the headers. You'd need the
HttpServletResponse or a HttpHeaders parameter.
Basically, ResponseEntity lets you do more.
I have a service, which I can access with the following jQuery code (from google chrome with --disable-web-security)
$.ajax({
type: 'POST',
url: "http://10.30.1.2:9234/myapp/v6/token/generate",
headers: {
"Content-Type":"application/json",
"Accept":"application/json"
},
data: JSON.stringify({
"staffId" : "13254",
"password" : "JustADummyPassword"
})
}).done(function(data) {
console.log(data);
});
$.ajax({
type: 'GET',
url: "http://10.30.1.2:9234/myapp/v6/user/appl/Firstname/Lastname/email#address.com/1998-01-01",
headers: {
"Content-Type":"application/json",
"Accept":"application/json"
}
}).done(function(data) {
console.log(data);
});
The first call sets a cookie, which is required for the second call to authenticate. This works fine, and both requests return expected results.
I am trying to set up automated testing for the service, and have this written in JAVA, using RestAssured.
public class UserApplication {
public static Map<String, String> authCookies = null;
public static String JSESSIONID = null;
public static void main(String[] args) {
Response resp = hello();
resp = apiUserApplication();
}
public static Response apiUserApplication() {
String userAppl = "http://10.30.1.2:9234/myapp/v6/user/appl/Firstname/Lastname/email#address.com/1998-01-01";
Response response = RestAssured.given()
.cookie("JSESSIONID", JSESSIONID).and()
.header("Accept", "application/json").and()
.header("Content-Type", "application/json").and()
.when().get(userAppl);
return response;
}
public static Response hello() {
String helloUrl = "http://10.30.1.2:9234/myapp/v6/hello";
Response response = RestAssured.given().cookies(authCookies)
.contentType("application/json").when().get(helloUrl);
return response;
}
}
The first call (hello) works fine, and returns 200 code, and gets a valid token for use in the second call. The error I am getting from the second call with a 400 status code is...
{"errors":["Content type 'null' not supported"]}
I'm not experienced with RestAssured, but your code is set up to first call, hello, then apiUserApplication. You have some class level variables that you default to null, but you never explicitly give them a value. In particular, it looks in the second call, apiUserApplication, you are setting the value of the cookie to a null value.
I also do not see why you are returning this response object? It would make sense if you were to examine it, ensure that the response has data as you expected and that you then set this session ID for the second request.
It looks like JSESSIONID and authCookies are initialized as null and not changing in this code.