Java / Jackson - 'Unrecognized token' passing JSON object parameter - java

Java JAX-RS web service with Jersey / Jackson, a service method expects a User parameter (POJO) as JSON. The client app (Angular 6) sends a POST request containing the User parameter (serialized as JSON). The service method call fails with error message: "Unrecognized token 'jsonUser': was expecting ('true', 'false' or 'null')".
Here is the User class (POJO) - you can see I tried annotating all the properties with #JsonProperty, but it's unnecessary, as I'm not "renaming" them:
import java.io.Serializable;
import javax.ws.rs.FormParam;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
//import org.glassfish.jersey.media.multipart.FormDataParam;
/**
* JavaBean for passing the User properties between the UI app (Angular)
* and TearsWs. Implementation requires this to be serializable (JSON).
*/
#JsonIgnoreProperties({ "DELIM" })
public class User implements Serializable {
private String userName;
private String employeeId;
private String employeeName;
private String homeUnitCode;
private boolean certifier;
private HomeUnit[] tkHomeUnits;
private boolean supervisor;
private Employee[] whoISupervise;
private boolean hrStaff;
private boolean collector;
private final static String DELIM = ", ";
public User() {
}
// getters / setters
//#JsonProperty("userName")
public void setUserName(String ldapUid) {
this.userName = ldapUid;
}
public String getUserName() {
return this.userName;
}
//#JsonProperty("employeeId")
public void setEmployeeId(String employeeId) {
this.employeeId = employeeId;
}
public String getEmployeeId() {
return this.employeeId;
}
//#JsonProperty("employeeName")
public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}
public String getEmployeeName() {
return this.employeeName;
}
//#JsonProperty("homeUnitCode")
public void setHomeUnitCode(String homeUnitCode) {
this.homeUnitCode = homeUnitCode;
}
public String getHomeUnitCode() {
return this.homeUnitCode;
}
//#JsonProperty("certifier")
public void setCertifier(boolean certifier) {
this.certifier = certifier;
}
public boolean getCertifier() {
return this.certifier;
}
//#JsonProperty("tkHomeUnits")
public void setTkHomeUnits(HomeUnit[] tkHomeUnitCodes) {
this.tkHomeUnits = tkHomeUnitCodes;
}
public HomeUnit[] getTkHomeUnits() {
return this.tkHomeUnits;
}
//#JsonProperty("supervisor")
public void setSupervisor(boolean supervisor) {
this.supervisor = supervisor;
}
public boolean isSupervisor() {
return this.supervisor;
}
//#JsonProperty("whoISupervise")
public void setWhoISupervise(Employee[] whoISupervise) {
this.whoISupervise = whoISupervise;
}
public Employee[] getWhoISupervise() {
return this.whoISupervise;
}
//#JsonProperty("hrStaff")
public void setHrStaff(boolean hrStaff) {
this.hrStaff = hrStaff;
}
public boolean isHrStaff() {
return this.hrStaff;
}
//#JsonProperty("collector")
public void setCollector(boolean collector) {
this.collector = collector;
}
public boolean isCollector() {
return this.collector;
}
//methods
public boolean hasTauthority() {
return this.certifier || this.collector;
}
public String toString() {
int tkHUs = (tkHomeUnits == null) ? 0 : tkHomeUnits.length;
return "[User: "
+ "userName=" + this.userName + DELIM
+ "employeeId=" + this.employeeId + DELIM
+ "employeeName=" + this.employeeName + DELIM
+ "homeUnitCode=" + this.homeUnitCode + DELIM
+ "certifier=" + this.certifier + DELIM
+ "hrStaff=" + this.hrStaff + DELIM
+ "collector=" + this.collector + DELIM
+ "I can certify " + tkHUs + " homeUnits" + "]";
}
}
Here is the (Java) service method, which should accept and process the POST request:
/**
* Web service method.
*/
#POST
#Path("getTkHomeUnitEmployees")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response getTkHomeUnitEmployees(User user, #HeaderParam("X-Request-Param") String homeUnitCode) throws Exception {
String exceptionMessage;
if (user == null) {
exceptionMessage = "getTkHomeUnitEmployees() received a null User.";
log.error(exceptionMessage);
Response response = Response
.status(500)
.entity(exceptionMessage)
.build();
return response;
}
if (homeUnitCode == null || homeUnitCode.equals("")) {
exceptionMessage = "getTkHomeUnitEmployees() received a null HomeUnitCode.";
log.error(exceptionMessage);
Response response = Response
.status(500)
.entity(exceptionMessage)
.build();
return response;
}
if (!user.hasTauthority()) {
exceptionMessage = "getTkHomeUnitEmployees() received a request from a non-timekeeper and non-collector.";
log.error(exceptionMessage);
Response response = Response
.status(500)
.entity(exceptionMessage)
.build();
return response;
}
try {
Employee[] tkHomeUnitEmployees = new SecurityDao().getTkHomeUnitEmployees(user.getEmployeeId(), homeUnitCode);
Response response = Response
.ok(tkHomeUnitEmployees)
.header("Access-Control-Allow-Origin", "*")
.build();
return response;
} catch (Exception ex) {
exceptionMessage = "getTkHomeUnitEmployees(): " + ex;
Response response = Response
.status(500)
.entity(exceptionMessage)
.build();
return response;
}
}
The User object (client side, Javascript) is converted to JSON and encapsulated as a parameter in HttpParams; the POST passes it in the body of the request.
Here is the (Angular) client method, which sends the POST request to the web service:
getTkHomeUnitEmployees(user: User, homeUnitCode: string): Observable<Employee[]> {
const headers = new HttpHeaders()
.set('Content-Type', 'application/json')
.set('X-Request-Param', homeUnitCode); // homeUnitCode parameter in HttpHeaders
const httpOptions = {
headers: headers
};
let jsonUser: string = JSON.stringify(user);
const httpParams = new HttpParams()
.set('jsonUser', jsonUser);
let postUrl = this.wsUrl + 'getTkHomeUnitEmployees';
//postUrl += '?homeUnitCode=' + homeUnitCode; // homeUnitCode parameter as QueryParam
let obsArrayEmployees: Observable<Employee[]> = this.httpClient.post<Employee[]>(postUrl, httpParams, httpOptions);
return obsArrayEmployees;
}
...here I'm debugging the client (# browser Dev Tools), with a break in the getTkHomeUnitEmployees() method:
...I've displayed the value of jsonUser in the Console:
...here is the error in the Response:
...and here is the Request Params.
So, it appears the Jackson JsonParser is attempting to read and parse the parameter sent in the request, but the parameter includes "jsonUser=" at the beginning as part of it's value (json to be parsed). This is clearly wrong...
The service method blows up before actually entering / processing code; I can't set a breakpoint within the service method to examine the value of the parameter. It behaves as a "parameter invalid, return to caller" response.
I thought to manually hack the "jsonUser=" out of it (# client side), but it's not there. At the client, "jsonUser=" is not part of the parameter value; I believe it's just the key=value syntax of an http parameter (parameter-name=parameter-value), perhaps it's being prepended when the parameter is encapsulated into the HttpParams object.
Obviously I'm doing something wrong, but I haven't been able to figure it out; I thought this was the correct way to do this, but apparently not. Hope someone can help soon, I've been stuck on this for a couple days already.

You don't need to covert the 'user' object to string to pass to backend. Try passing the user object as it is.
this.httpClient.post<Employee[]>(postUrl, user, httpOptions);
And also please check if parameters passed really match the rest service exposed.

Related

Getting Bad Request while trying to consume REST API in Java SpringBoot

Hi I am trying to consume a REST endpoint using POST, but I am getting the error below. The endpoint gives proper response in POSTMAN, but I am getting error in Java. Please let me know where the mistake is.
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.client.HttpClientErrorException$BadRequest: 400 Bad Request: [{
"error":"unsupported_grant_type",
"error_description":"The given grant_type is not supported"
}]] with root cause
org.springframework.web.client.HttpClientErrorException$BadRequest: 400 Bad Request: [{
"error":"unsupported_grant_type",
"error_description":"The given grant_type is not supported"
}]
Below is code:
Controller:
public class MemberController {
private static final Logger log = LoggerFactory.getLogger(MemberController.class);
#Autowired
MemberService memberService;
#PostMapping(value = "/token", headers = "Accept=application/json")
public String getToken() {
log.info("Test getToken method");
return memberService.callTokenService();
}
Service Class:
public class MemberService {
private static final Logger log = LoggerFactory.getLogger(MemberService.class);
private RestTemplate restTemplate = new RestTemplate();
final String tokenURL = "-------";
public String callTokenService() {
log.info("Inside Service");
TokenInput input = new TokenInput();
String clientId = "l7xxef159fc30ee8479e9a7dab859c458a4d";
String clientSecret = "a63d0b4a01b844c0b7e7eb724ef13959";
String grantType = "client_credentials";
input.setCLIENT_ID(clientId);
input.setCLIENT_SECRET(clientSecret);
input.setGRANT_TYPE(grantType);
ResponseEntity<TokenProperties> response = restTemplate.postForEntity(tokenURL, input, TokenProperties.class);
HttpStatus status = response.getStatusCode();
log.info("Status: "+status);
log.info("Response: "+response.toString());
return response.toString();
}
}
POJO class:
#JsonIgnoreProperties(ignoreUnknown = true)
public class TokenProperties {
String access_token;
String token_type;
String expires_in;
String scope;
public String getAccess_token()
{
return access_token;
}
public void setAccess_token(String access_token)
{
this.access_token = access_token;
}
public String getToken_type()
{
return token_type;
}
public void setToken_type(String token_type)
{
this.token_type = token_type;
}
public String getExpires_in()
{
return expires_in;
}
public void setExpires_in(String expires_in)
{
this.expires_in = expires_in;
}
public String getScope()
{
return scope;
}
public void setScope(String scope)
{
this.scope = scope;
}
#Override
public String toString() {
return "{" + "access_token='" + access_token + '\'' + ", token_type=" + token_type + ", expires_in=" + expires_in + '\'' + "scope='" + scope + '}';
}
}
TokenInput POJO:
package com.memberservice_v2;
public class TokenInput {
String CLIENT_ID;
String CLIENT_SECRET;
String GRANT_TYPE;
public String getCLIENT_ID() {
return CLIENT_ID;
}
public void setCLIENT_ID(String cLIENT_ID) {
CLIENT_ID = cLIENT_ID;
}
public String getCLIENT_SECRET() {
return CLIENT_SECRET;
}
public void setCLIENT_SECRET(String cLIENT_SECRET) {
CLIENT_SECRET = cLIENT_SECRET;
}
public String getGRANT_TYPE() {
return GRANT_TYPE;
}
public void setGRANT_TYPE(String gRANT_TYPE) {
GRANT_TYPE = gRANT_TYPE;
}
}
Can anyone please help me out? Please let me know where the mistake is. Thanks in Advance!
Your request ResponseEntity<TokenProperties> response = restTemplate.postForEntity(tokenURL, null, TokenProperties.class); to token endpoint is incomplete. You're not passing the payload (token request) as far as I can see in the aforementioned code.
First, create the token request and set the appropriate attributes such as client id, grant type etc.
TokenRequest tokenRequest = new TokenRequest();
// set the attributes as per the token endpoint
ResponseEntity<TokenProperties> response = restTemplate.postForEntity(tokenURL, tokenRequest, TokenProperties.class);

Json request body to java enum with springboot controller

I'm trying to parse json into an enum,
I found this post but none of the solutions seemed to work for me.
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum SentStatus {
NOT_SENT("NOT_SENT"),
MANUAL("MANUAL"),
MARKETPLACE("MARKETPLACE");
public String sentStatus;
SentStatus(String sentStatus) {
this.sentStatus = sentStatus;
}
#Override
public String toString() {
return sentStatus;
}
public String getSentStatus() {
return this.sentStatus;
}
#JsonCreator
public static SentStatus fromText(String sentStatus) { //called, but sentStatus is always null
for (SentStatus s : SentStatus.values()) {
if (s.getSentStatus().equals(sentStatus)) {
return s;
}
}
throw new IllegalArgumentException(); //<-- this is thrown
}
}
and the controller
#PutMapping(path = "/{id}/mark-as-sent")
public InvoiceResponse markAsSent(
#PathVariable UUID id, #RequestBody SentStatus sentStatus) {
return this.invoiceService
.markInvoiceAsSent(id, sentStatus);
}
But when trying to hit the endpoint with the following json body
{"sentStatus":"MARKETPLACE"}
The controller returns (with no error outputted in the console)
{
"message": "Message body not in valid format"
}

OkHttp: A simple GET request: response.body().string() returns unreadable escaped unicode symbols inside json can't convert to gson

When sending a request in Postman, I get this output:
{
"valid": false,
"reason": "taken",
"msg": "Username has already been taken",
"desc": "That username has been taken. Please choose another."
}
However when doing it using okhttp, I get encoding problems and can't convert the resulting json string to a Java object using gson.
I have this code:
public static void main(String[] args) throws Exception {
TwitterChecker checker = new TwitterChecker();
TwitterJson twitterJson = checker.checkUsername("dogster");
System.out.println(twitterJson.getValid()); //NPE
System.out.println(twitterJson.getReason());
System.out.println("Done");
}
public TwitterJson checkUsername(String username) throws Exception {
HttpUrl.Builder urlBuilder = HttpUrl.parse("https://twitter.com/users/username_available").newBuilder();
urlBuilder.addQueryParameter("username", username);
String url = urlBuilder.build().toString();
Request request = new Request.Builder()
.url(url)
.addHeader("Content-Type", "application/json; charset=utf-8")
.build();
OkHttpClient client = new OkHttpClient();
Call call = client.newCall(request);
Response response = call.execute();
System.out.println(response.body().string());
Gson gson = new Gson();
return gson.fromJson(
response.body().string(), new TypeToken<TwitterJson>() {
}.getType());
}
Which prints this:
{"valid":false,"reason":"taken","msg":"\u0414\u0430\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0443\u0436\u0435 \u0437\u0430\u043d\u044f\u0442\u043e","desc":"\u0414\u0430\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0443\u0436\u0435 \u0437\u0430\u043d\u044f\u0442\u043e. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0434\u0440\u0443\u0433\u043e\u0435."}
and then throws a NullPointerException when trying to access a twitterJson. Debugger shows that object as being null.
TwitterJson:
#Generated("net.hexar.json2pojo")
#SuppressWarnings("unused")
public class TwitterJson {
#Expose
private String desc;
#Expose
private String msg;
#Expose
private String reason;
#Expose
private Boolean valid;
public String getDesc() {
return desc;
}
public String getMsg() {
return msg;
}
public String getReason() {
return reason;
}
public Boolean getValid() {
return valid;
}
...
How can I fix the encoding issues with okhttp?
It is because the response object can be consumed only once. OKHTTP says that in their documentation. After the execute is invoked, you are calling the response object twice. Store the result of response.body().string() to a variable and then do the convert into GSON.
If I were to use a hello world example...
private void testOkHttpClient() {
OkHttpClient httpClient = new OkHttpClient();
try {
Request request = new Request.Builder()
.url("https://www.google.com")
.build();
Call call = httpClient.newCall(request);
Response response = call.execute();
System.out.println("First time " + response.body().string()); // I get the response
System.out.println("Second time " + response.body().string()); // This will be empty
} catch (IOException e) {
e.printStackTrace();
}
}
The reason it is empty the second time is because the response object can be consumed only once. So you either
Return the response as it is. Do not do a sysOut
System.out.println(response.body().string()); // Instead of doing a sysOut return the value.
Or
Store the value of the response to a JSON then convert it to GSON and then return the value.
EDIT: Concerning Unicode characters. It turned out since my location is not an English-speaking country, the json i was accepting was not in English as well. I added this header:
.addHeader("Accept-Language", Locale.US.getLanguage())
to the request to fix that.

In Zuul gateway,How to modify the service path in custom filter?

I have implemented a zuul gateway service for the communication between some micro services that i have wrote. I have a specific scenario like i want to change the service path in one of my custom filter and redirected to some other service. Is this possible with the zuul gateway?. I have tried putting "requestURI" parameter with the updated uri to the request context in my route filter but that didn't worked out well
Please help me out guys
thanks in advance
yes, you can. for that you need to implement ZuulFilter with type PRE_TYPE, and update response with specified Location header and response status either 301 or 302.
#Slf4j
public class CustomRedirectFilter extends ZuulFilter {
#Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
#Override
public int filterOrder() {
return FilterConstants.SEND_FORWARD_FILTER_ORDER;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
String requestUrl = ctx.getRequest().getRequestURL().toString();
if (shouldBeRedirected(requestUrl)) {
String redirectUrl = generateRedirectUrl(ctx.getRequest());
sendRedirect(ctx.getResponse(), redirectUrl);
}
return null;
}
private void sendRedirect(HttpServletResponse response, String redirectUrl){
try {
response.setHeader(HttpHeaders.LOCATION, redirectUrl);
response.setStatus(HttpStatus.MOVED_PERMANENTLY.value());
response.flushBuffer();
} catch (IOException ex) {
log.error("Could not redirect to: " + redirectUrl, ex);
}
}
private boolean shouldBeRedirected(String requestUrl) {
// your logic whether should we redirect request or not
return true;
}
private String generateRedirectUrl(HttpServletRequest request) {
String queryParams = request.getQueryString();
String currentUrl = request.getRequestURL().toString() + (queryParams == null ? "" : ("?" + queryParams));
// update url
return updatedUrl;
}
}

Spring MVC: Complex object as parameter

I started to learn spring boot and I'm faced with problems. I have following code:
#RestController
public class UserController {
#RequestMapping("/")
public String getMessageInfo(Message message) {
return "Id is " + message.getId() + ", message is " + message.getMessage() + ", parameter good is " + message.isGood();
}
}
Class Message:
public class Message {
private String message;
private int id;
private boolean good;
public Message() {}
public Message(int id) {this.id = id;}
public Message(String message) {this.message = message;}
public Message(boolean good) {this.good = good;}
public Message(String message, boolean good, int id) {
this.message = message;
this.good = good;
this.id = id;
}
public String getMessage() {
return message;
}
public int getId() {
return id;
}
public boolean isGood() {
return good;
}
}
And when I try to do something like this:
RestTemplate request = new RestTemplate();
String info = request.getForObject("http://localhost:8080/?id=4", String.class);
value of id is ignored. Same problem appears when I send request with boolean good parameter (for example localhost:8080/?good=true). It is called the default constructor instead of Message(boolean)/Message(int). But when I do something like localhost:8080/?message=1234 it isn't ignored. Can you explain me what is the problem?
And one more question: can I send instance of class Message to getMessageInfo in different way than localhost:8080/?message=1234&good=true&id=145? If I have more than 3 parameters? For example if class Message has 100 parameters?
since you are trying to deal with a complex object accept your object from a post request.
#RequestMapping("/",method=RequestMethod.POST)
public String getMessageInfo(#RequestBody Message message) {
return message;
}
in the above code i'm setting method attribute to POST then it will be called when you are making a POST request, and i am using #RequestBody Message message inside the method parameter. which will convert and form an Message object from the incoming request, if you dont put #requestBody annotation then a Bean will be injected to the method by spring instead of forming a one from the request.
you can try this code to make the request
final String uri = "http://localhost:8080/";
Message message = new Message(1, "Adam",true);
RestTemplate restTemplate = new RestTemplate();
Message result = restTemplate.postForObject( uri, message, Message.class);
when making an request create an Message object setting each and every field in it, otherwise you will end up in having Bad request error.
I solved the problem, if add smth like this:
#ModelAttribute("message")
public Message getMessage(String message, boolean good, int id){
return new Message(message, good, id);
}
#RequestMapping("/")
public String getUserInfo(#ModelAttribute("message") Message message) {
return "Id is " + message.getId() + ", message is " + message.getMessage() + ", parameter good is " + message.isGood();
}
all parameters aren't ignored.
You can use like this,
MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
params.add("id", 1);
params.add("good", true);
params.add("message", 1234);
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<MultiValueMap<String, String>>(params, requestHeaders);
RestTemplate restTemplate = new RestTemplate();
Message message= restTemplate.postForObject("http://localhost:8080/", requestEntity, Message.class);

Categories