Retrofit 2: 400 Bad Request when having body - java

I am trying to perform a status update PUT request. The following example returns 200 in Postman:
URL:
http://www.example.com/users/3/status?seId=1&dt=2016-11-01T00:00:00Z
HEADERS:
Content-Type:application/json
charset:utf-8
Authorization:Bearer LONG_TOKEN_HERE
BODY:
{ "status": 1 }
This is the structure of my Retrofit 2 request:
#PUT("users/{id}/status")
Call<Void> updateEventStatus(#Header("Authorization") String token,
#Path("id") int id,
#Query("seId") int seId,
#Query("dt") String dateTime,
#Body Status status);
The request's URL is the same as in Postman and so are the headers, so I suspect it is related to the body. Status is just a wrapping class with a single int field named status, which I created by following this answer (I did the same with credentials and it works well). I also tried making the status in body of type int but it results in Bad Request as well.
Any idea what could be the difference between the Postman request and the Retrofit 2 request? Thanks!
EDIT: This is the originalRequest in Retrofit 2:
Request{method=PUT, url=http://example.com/api/users/3/status?seId=0&dt=2016-10-04T05:30:00Z, tag=null}
headers: Authorization: Bearer LONG_TOKEN_HERE
contentType: application/json; charset=UTF-8
content:
0 = 123
1 = 34
2 = 115
3 = 116
4 = 97
5 = 116
6 = 117
7 = 115
8 = 34
9 = 58
10 = 51
11 = 125
Translated content:
{"status":3}

Eventually, it was a server-side bug (I received a false seId at first, and then tried to PUT using an seId that doesn't exist).

Related

Rest web services(post) consumes json not working

So I am trying to create a simple webservice post that consumes json. But I am geting the error RESTEASY002010: Failed to execute: javax.ws.rs.NotSupportedException: RESTEASY003065: Cannot consume content type
My webservice:
#POST
#Produces(MediaType.APPLICATION_XML)
#Path("teste1")
#Consumes(MediaType.APPLICATION_JSON)
public Response teste1(String product) {
String result = "Product created : " + product;
System.out.println("resultado");
System.out.println(result);
return Response.ok() //200
.entity("<erro> none </erro>")
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT")
.header("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With").build();
}
I also tried to do:
#Consumes("application/json")
But I am getting the same error. I can make it work if I do:
#Consumes("*/*")
But I can't understand why it doesn't work when I say it consumes json. To test the webservice I am using https://apitester.com/. With the folowing Post Data:
{
"key" : "value",
"array" : [
{ "key" : 1 },
{ "key" : 2, "dictionary": {
"a": "Apple",
"b": "Butterfly",
"c": "Cat",
"d": "Dog"
} },
{ "key" : 3 }
]
}
In general the
#Consumes("application/json")
specifies a content media type which webservice can handle.
But also you may need to explicitly specify an appropriate type in the Content-Type header for your request.
I am not familiar with the https://apitester.com but probably it does not send the Content-Type header by default
In such case your server can treat the request body as a plain text, for instance. That request would not be routed to your endpoint, because it is not designed for the plain text.
Setting the #Consumes(*/*) fixes that problem, because the wrong media type matches that pattern as well.
Could you please ensure that you sends the Content-Type: application/json with your POST request?

Bitstamp API: Always receiving API0000 (Missing key, signature and nonce parameters.)

Studying this SO question ( Authenticated Java Jersey REST call to Bitstamp ), I see that OP had a similar issue. Unfortunately, I can't make the call work in my own implementation: I tried sending key, nonce and signature both via query-parameters as well as json formatted in the request's body. I logged headers and content, so in those two cases, these are the resulting logs:
// send in body
---> POST https://www.bitstamp.net/api/v2/balance/ HTTP/1.1
Accept: application/json;charset=UTF-8
Content-Type: application/json;charset=UTF-8
Content-Length: 140
{"key":"12345678901234567890123456789012","nonce":1234567890,"signature":"1234567890123456789012345678901234567890123456789012345678901234"}
---> END HTTP (140-byte body)
<--- HTTP/1.1 403 Authentication Failed (49ms)
{"status": "error", "reason": "Missing key, signature and nonce parameters.", "code": "API0000"}
<--- END HTTP (96-byte body)
// send as query-parameters
---> POST https://www.bitstamp.net/api/v2/balance/?key=12345678901234567890123456789012&signature=1234567890123456789012345678901234567890123456789012345678901234&nonce=1234567890 HTTP/1.1
Accept: application/json;charset=UTF-8
Content-Type: application/x-www-form-urlencoded
---> END HTTP (0-byte body)
<--- HTTP/1.1 403 Authentication Failed (45ms)
{"status": "error", "reason": "Missing key, signature and nonce parameters.", "code": "API0000"}
<--- END HTTP (96-byte body)
What am I doing wrong? What does bitstamp mean when it states
For a successful authentication you need to provide your API key, a signature and a nonce parameter.
(https://www.bitstamp.net/api/)
I use Feign as REST client. The two methods look like this:
#PostMapping(value = "api/v2/balance/", produces = MediaType.APPLICATION_JSON_UTF8_VALUE, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
ResponseEntity<Map<String, Object>> getAccountBalanceQuery(#RequestParam("key") String key,
#RequestParam("signature") String signature,
#RequestParam("nonce") Integer nonce);
#PostMapping(value = "api/v2/balance/", produces = MediaType.APPLICATION_JSON_UTF8_VALUE, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
ResponseEntity<Map<String, Object>> getAccountBalanceBody(#RequestBody BitstampAuth body);
Thanks!

Apache Jmeter : Post an object not working with ModelAttribute

I am working on stress testing our webapplication written in Spring-MVC.
I would like to send an object Person to the application. I have added a system out to get the email, but whenever I am sending the object, it is null. What am I doing wrong?
Server code :
#RequestMapping(value = "/person/add", method = RequestMethod.POST)
public String addPerson(#ModelAttribute("person") Person person, BindingResult bindingResult) {
try {
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(person);
System.out.println("String is "+json);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("Person add called"+person.getUsername());
person.setUsername(this.stripHTML(person.getUsername()));
int personId = this.personService.addPerson(person);
if (!(personId == 0)) {
Person person1 = this.personService.getPersonById(personId);
Collection<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
Authentication authentication = new UsernamePasswordAuthenticationToken(person1, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
return "redirect:/canvaslisting";
} else {
return "redirect:/";
}
}
Object sent in body data :
{"person":{"id":0,"username":"testemail#gmail.com","firstName":"test","cleanCacheFlag":false,"googleDrive":false,"dropbox":false,"evernoteConsumed":false,"statusChangeTimeStamp":null,"useCalendar":false,"newsletterFlag":false,"tourSteps":null,"profession":null,"notiz":null,"telePhone":null,"lastVisitedBoards":null,"leftGroup":null,"optionalEmail":null,"facebookLink":null,"xingLink":null,"linkedinLink":null,"lastOnlineTimestamp":null,"userRole":null,"homePage":null,"excelImportQuota":0,"toEmail":null,"code":null,"authorities":null,"role":null,"newpassword":null,"token":null,"profilePhotoString":null,"accountNonExpired":true,"credentialsNonExpired":true,"accountNonLocked":true,"active":true,"enabled":false}
}
Output :
Person add callednull
Screenshot :
Sample result
Thread Name: Thread Group 2-5
Sample Start: 2017-06-29 15:17:11 IST
Load time: 6
Connect Time: 0
Latency: 6
Size in bytes: 237
Sent bytes:0
Headers size in bytes: 205
Body size in bytes: 32
Sample Count: 1
Error Count: 1
Data type ("text"|"bin"|""): text
Response code: 415
Response message: Unsupported Media Type
Response headers:
HTTP/1.1 415 Unsupported Media Type
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=utf-8
Content-Language: en
Content-Length: 1048
Date: Thu, 29 Jun 2017 09:47:11 GMT
Connection: close
HTTPSampleResult fields:
ContentType: text/html;charset=utf-8
DataEncoding: utf-8
Request:
POST http://127.0.0.1:8080/person/add/
POST data:
{"person":{"id":0,"username":"testemail#gmail.com","firstName":"test","cleanCacheFlag":false,"googleDrive":false,"dropbox":false,"evernoteConsumed":false,"statusChangeTimeStamp":null,"useCalendar":false,"newsletterFlag":false,"tourSteps":null,"profession":null,"notiz":null,"telePhone":null,"lastVisitedBoards":null,"leftGroup":null,"optionalEmail":null,"facebookLink":null,"xingLink":null,"linkedinLink":null,"lastOnlineTimestamp":null,"userRole":null,"homePage":null,"excelImportQuota":0,"toEmail":null,"code":null,"authorities":null,"role":null,"newpassword":null,"token":null,"profilePhotoString":null,"accountNonExpired":true,"credentialsNonExpired":true,"accountNonLocked":true,"active":true,"enabled":false}
}
[no cookies]
Request Headers:
Connection: close
Content-Length: 717
Content-Type: application/x-www-form-urlencoded
Change header Content-Type in HTTP Header Manager from:
Content-Type: application/x-www-form-urlencoded
To:
Content-Type: application/json
Shouldn't you just have an #RequestBody annotation on person ?
public String addPerson(#RequestBody Person person);
Well, there is a typo or copy-paste issue in your JMeter request
Also your JMeter configuration might be missing HTTP Header Manager configured to send Content-Type header with the value of application/json

How to print pretty JSON in Jersey Trace?

I have a REST API with Jersey and the requests traces is like this:
InformaciĆ³n: 5 * Server out-bound response
5 < 200
5 < Content-Type: application/json
5 < X-Jersey-Trace-000: accept root resource classes: "/vales"
5 < X-Jersey-Trace-001: match path "/vales" -> "/application\.wadl(/.*)?", "/selacservicios(/.*)?", "/proveedores(/.*)?", "/vehiculos(/.*)?", "/empresas(/.*)?", "/vales(/.*)?", "/ok(/.*)?"
5 < X-Jersey-Trace-002: accept right hand path java.util.regex.Matcher[pattern=/vales(/.*)? region=0,6 lastmatch=/vales]: "/vales" -> "/vales" : ""
5 < X-Jersey-Trace-003: accept resource: "vales" -> #Path("/vales") com.grupogimeno.senda.siccagest.services.rest.ValeCompraResource#3f494991
5 < X-Jersey-Trace-004: match path "" -> ""
5 < X-Jersey-Trace-005: accept resource methods: "vales", GET -> com.grupogimeno.senda.siccagest.services.rest.ValeCompraResource#3f494991
5 < X-Jersey-Trace-006: matched resource method: public java.util.Map com.grupogimeno.senda.siccagest.services.rest.ValeCompraResource.getValesCompraDeUsuario(java.lang.String,java.lang.Integer)
5 < X-Jersey-Trace-007: matched message body writer: java.util.LinkedHashMap#b690aa46, "application/json" -> com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider#12b315ef
5 <
{"success":true,"data":[{"fGeneracion":1428579118957,"numValeCompra":"001562","abreviaturaEmpresa":"AGU. NUMANCIA","nombreProveedor":"MAJIMSA VALENCIA, S.A."},{"fGeneracion":1428579081230,"numValeCompra":"005647","abreviaturaEmpresa":"A.VINAROS-UTE","nombreProveedor":"EXCAV BABILONI, S.A."},{"fGeneracion":1428579081230,"numValeCompra":"005647","abreviaturaEmpresa":"A.VINAROS-UTE","nombreProveedor":"EXCAV BABILONI, S.A."}]}
Is there a way to print the JSON response prettier? Like this:
{
"success": true,
"data": [
{
"fGeneracion": 1428579118957,
"numValeCompra": "001562",
"abreviaturaEmpresa": "AGU. NUMANCIA",
"nombreProveedor": "MAJIMSA VALENCIA, S.A."
},
{
"fGeneracion": 1428579081230,
"numValeCompra": "005647",
"abreviaturaEmpresa": "A.VINAROS-UTE",
"nombreProveedor": "EXCAV BABILONI, S.A."
},
{
"fGeneracion": 1428579081230,
"numValeCompra": "005647",
"abreviaturaEmpresa": "A.VINAROS-UTE",
"nombreProveedor": "EXCAV BABILONI, S.A."
}
]
}
This is the filter in web.xml:
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
<param-value>com.sun.jersey.api.container.filter.LoggingFilter</param-value>
</init-param>
Thanks.
You can configure the ObjectMapper to configure(SerializationFeature.INDENT_OUTPUT, true);. You can configure it in a ContextResolver, as seen here.
Note though that this will format the actual response, and not just the logging. It seems the logging just prints the response "as-is", so I don't know how you would have one without the other (except by writing your own logger)
Use com.google.gson.Gson -
The code will then be -
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String json = gson.toJson(JsonObject);
LOGGER.info("JSON prettier response: "+json);

Java - JAIN SIP INVITE does not return response

public void call(Response response) {
try {
cseq++;
current_process = cseq + " INVITE";
ArrayList viaHeaders = new ArrayList();
ViaHeader viaHeader = this.headerFactory.createViaHeader(this.ip,
5060, "udp", null);
viaHeaders.add(viaHeader);
// The "Max-Forwards" header.
MaxForwardsHeader maxForwardsHeader = this.headerFactory
.createMaxForwardsHeader(70);
// The "Call-Id" header.
CallIdHeader callIdHeader = this.sipProvider.getNewCallId();
// The "CSeq" header.
CSeqHeader cSeqHeader = this.headerFactory.createCSeqHeader(cseq,
"INVITE");
Address fromAddress = addressFactory.createAddress("sip:"
+ username + '#' + server);
Address toAddress = addressFactory.createAddress("sip:"+this.toPrefix+this.toNumber+'#'+this.toUri);
FromHeader fromHeader = this.headerFactory.createFromHeader(
fromAddress, String.valueOf(this.tag));
// The "To" header.
ToHeader toHeader = this.headerFactory.createToHeader(toAddress,
null);
ContentLengthHeader contentLength = this.headerFactory.createContentLengthHeader(300);
ContentTypeHeader contentType = this.headerFactory.createContentTypeHeader("application", "sdp");
String sdpData = "v=0\n" +
"o=user1 392867480 292042336 IN IP4 192.168.10.31\n" +
"s=-\n" +
"c=IN IP4 192.168.10.31\n" +
"t=0 0\n" +
"m=audio 8000 RTP/AVP 0 8 101\n" +
"a=rtpmap:0 PCMU/8000\n" +
"a=rtpmap:8 PCMA/8000\n" +
"a=rtpmap:101 telephone-event/8000\n" +
"a=sendrecv";
byte[] contents = sdpData.getBytes();
// this.contactHeader = this.headerFactory
// .createContactHeader(contactAddress);
request = this.messageFactory.createRequest("sip:"+this.toPrefix+this.toNumber+'#'
+ this.toUri + " SIP/2.0\r\n\r\n");
request.addHeader(viaHeader);
request.addHeader(maxForwardsHeader);
request.addHeader(toHeader);
request.addHeader(fromHeader);
request.addHeader(callIdHeader);
request.addHeader(cSeqHeader);
request.addHeader(contactHeader);
request.addHeader(contentLength);
request.addHeader(contentType);
if (response != null) {
retry = true;
System.out.println("DEBUG: Response: "+response);
}
inviteTid = sipProvider.getNewClientTransaction(request);
// send the request out.
inviteTid.sendRequest();
this.dialog = inviteTid.getDialog();
// Send the request statelessly through the SIP provider.
// this.sipProvider.sendRequest(request);
// Display the message in the text area.
logger.debug("Request sent:\n" + request.toString() + "\n\n");
} catch (Exception e) {
// If an error occurred, display the error.
e.printStackTrace();
logger.debug("Request sent failed: " + e.getMessage() + "\n");
}
}
And this is the processResponse:
#Override
public void processResponse(ResponseEvent responseEvent) {
System.out.println(responseEvent.getResponse());
int statusCode = responseEvent.getResponse().getStatusCode();
if (statusCode > 400 && statusCode < 410) {
if(statusCode == 401 && current_process.contains("REGISTER")) {
register(responseEvent.getResponse());
}
else if(statusCode == 401 && current_process.contains("INVITE")) {
System.out.println("Invite 401");
}
else if(statusCode == 200) {
System.out.println("Registered");
call(null);
}
else {
System.out.println("Other code: "+ statusCode);
}
}
I have successfully registered the account to the server, but when it began the INVITE request, there is no response returned.
Below is the response I got (including the REGISTER responses), in the end you will see that my outgoing INVITE request doesn't return any responses.
DEBUG - Request sent:
REGISTER sip:sip.dialnow.com SIP/2.0
Via: SIP/2.0/UDP 192.168.10.31:5060;branch=z9hG4bK-343133-c591d8f797a179ed25a2114311df1854
Max-Forwards: 70
To: <sip:Frax01#sip.dialnow.com>
From: <sip:Frax01#sip.dialnow.com>;tag=706572717
Call-ID: 6e49a1b2631c4b107199a8b7d9c5ee52#192.168.10.31
CSeq: 1 REGISTER
Contact: <sip:Frax01#192.168.10.31:5060;transport=UDP>
Expires: 120
Content-Length: 0
SIP/2.0 401 Unauthorized
Via: SIP/2.0/UDP 192.168.10.31:5060;branch=z9hG4bK-343133-c591d8f797a179ed25a2114311df1854
From: <sip:Frax01#sip.dialnow.com>;tag=706572717
To: <sip:Frax01#sip.dialnow.com>
Contact: <sip:77.72.169.129:5060>
Call-ID: 6e49a1b2631c4b107199a8b7d9c5ee52#192.168.10.31
CSeq: 1 REGISTER
Server: (Very nice Sip Registrar/Proxy Server)
Allow: ACK,BYE,CANCEL,INVITE,REGISTER,OPTIONS,INFO,MESSAGE
WWW-Authenticate: Digest realm="sip.dialnow.com",nonce="2849852658",algorithm=MD5
Content-Length: 0
12d2ad0d764601c0573be88a3d94a2d8
8b3a241c08615fba24fb161a2a344cc9
40810f9cf3d19138dc0875dea15e3970
DEBUG - Request sent:
REGISTER sip:sip.dialnow.com SIP/2.0
Via: SIP/2.0/UDP 192.168.10.31:5060;branch=z9hG4bK-343133-3ccb8a78b8614638665f0af2a998a686
Max-Forwards: 70
To: <sip:Frax01#sip.dialnow.com>
From: <sip:Frax01#sip.dialnow.com>;tag=706572717
Call-ID: 50baef7c3a407e50d4d539a1e35cff41#192.168.10.31
CSeq: 2 REGISTER
Contact: <sip:Frax01#192.168.10.31:5060;transport=UDP>
Expires: 120
Authorization: Digest realm="sip.dialnow.com",nonce="2849852658",username="Frax01",uri="sip:sip.dialnow.com",algorithm=MD5,response="40810f9cf3d19138dc0875dea15e3970"
Content-Length: 0
SIP/2.0 100 Trying
Via: SIP/2.0/UDP 192.168.10.31:5060;branch=z9hG4bK-343133-3ccb8a78b8614638665f0af2a998a686
From: <sip:Frax01#sip.dialnow.com>;tag=706572717
To: <sip:Frax01#sip.dialnow.com>
Contact: <sip:77.72.169.129:5060>
Call-ID: 50baef7c3a407e50d4d539a1e35cff41#192.168.10.31
CSeq: 2 REGISTER
Server: (Very nice Sip Registrar/Proxy Server)
Allow: ACK,BYE,CANCEL,INVITE,REGISTER,OPTIONS,INFO,MESSAGE
Content-Length: 0
Other code: 100
SIP/2.0 200 Ok
Via: SIP/2.0/UDP 192.168.10.31:5060;branch=z9hG4bK-343133-3ccb8a78b8614638665f0af2a998a686
From: <sip:Frax01#sip.dialnow.com>;tag=706572717
To: <sip:Frax01#sip.dialnow.com>
Contact: <sip:Frax01#192.168.10.31:5060;transport=udp>;expires=120
Call-ID: 50baef7c3a407e50d4d539a1e35cff41#192.168.10.31
CSeq: 2 REGISTER
Server: (Very nice Sip Registrar/Proxy Server)
Allow: ACK,BYE,CANCEL,INVITE,REGISTER,OPTIONS,INFO,MESSAGE
Content-Length: 0
Registered
DEBUG - Request sent:
INVITE sip:6281386725319#dialnow.com SIP/2.0
Via: SIP/2.0/UDP 192.168.10.31:5060;branch=z9hG4bK-343133-e18f3eca8f76bd0a4378a12dd6e7a0d7
Max-Forwards: 70
To: <sip:6281386725319#dialnow.com>
From: <sip:Frax01#sip.dialnow.com>;tag=706572717
Call-ID: 66341e5e43e50e106a7fbf2866c0a1d3#192.168.10.31
CSeq: 3 INVITE
Contact: <sip:Frax01#192.168.10.31:5060;transport=UDP>
Content-Type: application/sdp
Content-Length: 300
As you can see it just stopped after the INVITE request is sent.
Thank you in advance,
Adam.
Further information:
I have tried user2932861's suggestions to no luck. I have tried using my public IP address but it's still stuck at the INVITE request. Also, I have set my SDP content, below is my SDP content:
ContentTypeHeader contentType = this.headerFactory.createContentTypeHeader("application", "sdp");
String sdpData = "v=0\n" +
"o=user1 392867480 292042336 IN IP4 *my IP*\n" +
"s=-\n" +
"c=IN IP4 *my IP*\n" +
"t=0 0\n" +
"m=audio 8000 RTP/AVP 0 8 101\n" +
"a=rtpmap:0 PCMU/8000\n" +
"a=rtpmap:8 PCMA/8000\n" +
"a=rtpmap:101 telephone-event/8000\n" +
"a=sendrecv";
byte[] contents = sdpData.getBytes();
request.setContent(contents, contentType);
Note: the IP address is censored.
Thank you in advance!
As a matter of fact, it didn't exactly return NO responses at all, after a while being silent (not returning anything), the console would show "transaction timeout." Any idea why this is happening?
transaction timeout corresponds to sip timeout which should be 32s. You get this 408 answer because you received no answer. This is an automated answer generated by the stack itself, not an answer by a remote party.
Did you check your firewall settings? If you have no problem with your OS firewall, did you check your home gateway?
Are REGISTER and INVITE requests sent to the same ip address? you can check using wireshark.
yohann
My guess for your woes:
Your contact headers are showing private addresses. Try using publicly routable addresses here. ( You can use STUN to discover your public address or some other means.) If you are going through a B2BUA (AKA session border controller), it would rewrite those for you. Firewall and NAT traversal in SIP is non trivial.
Also, you have a content length in your INVITE but do you have content? Did you forget to set your SDP content?
Instead of messing with low level SIP, why don't you try a higher level framework such as SIP Servlets https://code.google.com/p/sipservlets/ which let you focus on your core business logic ? You can even try an Amazon AMI https://aws.amazon.com/marketplace/pp/B00G9G94JW/ref=srh_res_product_title?ie=UTF8&sr=0-3&qid=1383128640630#support

Categories