Jersey calling a get with object as parameter does not work - json

I am new to Jersey, I am trying to develop a GET for search results. For this I need to send a object with the search criteria and data. I wonder what I am doing wrong. I am getting the following exception on my Junit test case
com.sun.jersey.api.client.UniformInterfaceException: GET http://localhost:8081/mCruiseOnCarPool4All/carpool4all/Search/Request/com.mcruiseon.carpool.concrete.SearchConcrete#676e3f returned a response status of 404 Not Found
at com.sun.jersey.api.client.WebResource.handle(WebResource.java:686)
at com.sun.jersey.api.client.WebResource.access$200(WebResource.java:74)
at com.sun.jersey.api.client.WebResource$Builder.get(WebResource.java:507)
at test.carpool4all.SingleSearchTest.testPost(SingleSearchTest.java:89)
My Server side GET
#GET
#Path ("Request/{search}")
#Consumes({ MediaType.APPLICATION_JSON })
#Produces({ MediaType.APPLICATION_JSON })
public Response search(#PathParam("search") SearchConcrete searchConcrete) {
SearchJourneyRequest request = new SearchJourneyRequest(searchConcrete) ;
SearchJourneyResponse response ;
clientSession = sessionManager.getClientSession(searchConcrete.getIdentityHash()) ;
clientSession.getSendQueue().sendRequest(request) ;
try {
response = (SearchJourneyResponse)clientSession.waitAndGetResponse(request) ;
} catch (WaitedLongEnoughException e) {
return Response.serverError().build() ;
} catch (UnableToResolveResponseException e) {
return Response.serverError().build() ;
}
return Response.ok(response.getSearchResults()).build();
}
Client Side Junit test
SearchConcrete searchProvider = new SearchConcrete(Globals.SearchCriteria.FlexiTime,
identityHash,
// more parameters
);
service = client.resource(UriBuilder.fromUri("http://localhost:8081/mCruiseOnCarPool4All/carpool4all/Search/Request/"+searchProvider).build());
Object[] searchResults = service.type(MediaType.APPLICATION_JSON).get(Object[].class);
Edit : Thanks to #eugen, to solve this, I added a concrete class with my Object[] as a private member. Instead of a GET, I used a POST here is the fixed code. Now my carpool search results are coming :).
service = client.resource(UriBuilder.fromUri(
"http://localhost:8081/mCruiseOnCarPool4All/carpool4all/Search/Request").build());
SearchResultsConcrete searchResults = service.type(MediaType.APPLICATION_JSON).post(SearchResultsConcrete.class, searchProvider);
assertNotNull(searchResults);
assertNotNull(searchResults.getSearchResults()) ;
assertTrue(searchResults.getSearchResults().length == 3) ;
assertTrue(searchResults.getSearchResults()[SearchJourneyResponse.FLEXI_POSITION].length > 0) ;
assertTrue(searchResults.getSearchResults()[SearchJourneyResponse.FLEXIENDTIME_POSITION].length > 0) ;
#POST
#Path ("Request")
#Consumes({ MediaType.APPLICATION_JSON })
#Produces({ MediaType.APPLICATION_JSON })
public Response search(JAXBElement<SearchConcrete> element) {
SearchJourneyRequest request = new SearchJourneyRequest((SearchConcrete)element.getValue()) ;
SearchJourneyResponse response ;
clientSession = sessionManager.getClientSession(((SearchConcrete)element.getValue()).getIdentityHash()) ;
clientSession.getSendQueue().sendRequest(request) ;
try {
response = (SearchJourneyResponse)clientSession.waitAndGetResponse(request) ;
} catch (WaitedLongEnoughException e) {
return Response.serverError().build() ;
} catch (UnableToResolveResponseException e) {
return Response.serverError().build() ;
}
return Response.ok(response.getSearchResults()).build();
}

You are doing things wrong.
First lets analyze your error :
com.sun.jersey.api.client.UniformInterfaceException: GET http://localhost:8081/mCruiseOnCarPool4All/carpool4all/Search/Request/com.mcruiseon.carpool.concrete.SearchConcrete#676e3f returned a response status of 404 Not Found.
We can see that com.mcruiseon.carpool.concrete.SearchConcrete#676e3f is appended at the end of the url of your request (confirmed by your test case "http://localhost:8081/mCruiseOnCarPool4All/carpool4all/Search/Request/"+searchProvider). It does not make sense.
I assume you want to send a json with your GET request? You can't do that! GET has no body but only the url (and the headers), and appending json to your url does not make sense. If you want to send json you must do a POST.
You are trying to do something similar to this post What is the maximum length of JSON object received by JAX-RS web service using GET?, have a look at my answer.
Here is how to do what you want with Genson library http://code.google.com/p/genson/.
Download genson with maven, it will automaticaly enable json support when it is in your classpath and handle all the databinding.
Then change your server side method:
#POST
#Consumes({ MediaType.APPLICATION_JSON })
#Produces({ MediaType.APPLICATION_JSON })
public Response search(SearchConcrete searchConcrete) {
...
}
Change your test to:
// configure your client to use genson
ClientConfig config = new DefaultClientConfig();
config.getClasses().add(GensonJsonConverter.class);
cli = Client.create(config);
// then the test code
TypeOfTheResponse response = cli.resource("http://localhost:8081/mCruiseOnCarPool4All/carpool4all/Search/Request")
.accept(MediaType.APPLICATION_JSON)
.type(MediaType.APPLICATION_JSON)
.post(TypeOfTheResponse.class, searchProvider);
Remarks: do not use Object[] as the response, use a concrete java class like MyResponseItem[]

You are missing a slash / here:
"...Request"+searchProvider
This must be
"...Request/" + searchProvider
Edit
You can't just add an Object to an URL. This
"http://localhost:8081/mCruiseOnCarPool4All/carpool4all/Search/Request/"+searchProvider
will cause the toString() method of SearchConcrete to be called. The result is
com.mcruiseon.carpool.concrete.SearchConcrete#676e3f
The server has no way to reconstruct the SearchConcrete from this.

Related

How to access JSON?

I am wrote API method, after calling that method , I got my response like
[
{
"spark_version": "7.6.x-scala2.12"
}
]
Now I want to have variable in my API method which store value 7.6.x-scala2.12.
API Controller method
[HttpGet]
public IActionResult GetTest(int ActivityId)
{
string StoredJson = "exec sp_GetJobJSONTest " +
"#ActivityId = " + ActivityId ;
var result = _context.Test.FromSqlRaw(StoredJson);
return Ok(result);
}
So how this variable should call on this response to get string stored in spark_version?
Thank you
As you have the JavaScript tag, here's how you'd do it in JS:
If you are able to call your API method, then you can just assign the response to a variable. For example, if you are calling it using the fetch API, it would look something like:
let apiResponse;
fetch('myApiUrl.com').then((response) => {
if (response.status === 200) {
apiResponse = response.body;
console.log('Response:', apiResponse[0]['spark_version']);
}
});
(I defined the variable outside the then to make it globally accessible)

Is there any way within middleware running on ASP.NET Core 2.2 to detect if the request is for an ApiController?

I have an application with both MVC and 'new' ApiController endpoints in ASP.NET Core 2.2 co-existing together.
Prior to adding the API endpoints, I have been using a global exception handler registered as middleware using app.UseExceptionHandler((x) => { ... } which would redirect to an error page.
Of course, that does not work for an API response and I would like to return an ObjectResult (negotiated) 500 result with a ProblemDetails formatted result.
The problem is, I'm not sure how to reliably determine in my 'UseExceptionHandler' lambda if I am dealing with an MVC or a API request. I could use some kind of request URL matching (eg. /api/... prefix) but I would like a more robust solution that won't come back to bite me in the future.
Rough psuedo-code version of what I'm trying to implement is:
app.UseExceptionHandler(x =>
{
x.Run(async context =>
{
// extract the exception that was thrown
var ex = context.Features.Get<IExceptionHandlerFeature>()?.Error;
try
{
// generically handle the exception regardless of what our response needs to look like by logging it
// NOTE: ExceptionHandlerMiddleware itself will log the exception
// TODO: need to find a way to see if we have run with negotiation turned on (in which case we are API not MVC!! see below extensions for clues?)
// TODO: ... could just use "/api/" prefix but that seems rubbish
if (true)
{
// return a 500 with object (in RFC 7807 form) negotiated to the right content type (eg. json)
}
else
{
// otherwise, we handle the response as a 500 error page redirect
}
}
catch (Exception exofex)
{
// NOTE: absolutely terrible if we get into here
log.Fatal($"Unhandled exception in global error handler!", exofex);
log.Fatal($"Handling exception: ", ex);
}
});
});
}
Any ideas?
Cheers!
This might be a bit different than what you expect, but you could just check if the request is an AJAX request.
You can use this extension:
public static class HttpRequestExtensions
{
public static bool IsAjaxRequest(this HttpRequest request)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
if (request.Headers == null)
return false;
return request.Headers["X-Requested-With"] == "XMLHttpRequest";
}
}
And then middleware with an invoke method that looks like:
public async Task Invoke(HttpContext context)
{
if (context.Request.IsAjaxRequest())
{
try
{
await _next(context);
}
catch (Exception ex)
{
//Handle the exception
await HandleExceptionAsync(context, ex);
}
}
else
{
await _next(context);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception exception)
{
//you can do more complex logic here, but a basic example would be:
var result = JsonConvert.SerializeObject(new { error = "An unexpected error occurred." });
context.Response.ContentType = "application/json";
context.Response.StatusCode = 500;
return context.Response.WriteAsync(result);
}
see this SO answer for a more detailed version.
If you want to check whether the request is routed to ApiController, you could try IExceptionFilter to hanlde the exceptions.
public class CustomExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
if (IsApi(context))
{
HttpStatusCode status = HttpStatusCode.InternalServerError;
var message = context.Result;
//You can enable logging error
context.ExceptionHandled = true;
HttpResponse response = context.HttpContext.Response;
response.StatusCode = (int)status;
response.ContentType = "application/json";
context.Result = new ObjectResult(new { ErrorMsg = message });
}
else
{
}
}
private bool IsApi(ExceptionContext context)
{
var controllerActionDesc = context.ActionDescriptor as ControllerActionDescriptor;
var attribute = controllerActionDesc
.ControllerTypeInfo
.CustomAttributes
.FirstOrDefault(c => c.AttributeType == typeof(ApiControllerAttribute));
return attribute == null ? false : true;
}
}
Thanks to all of the advice from others, but I have realised after some more thought and ideas from here that my approach wasn't right in the first place - and that I should be handling most exceptions locally in the controller and responding from there.
I have basically kept my error handling middleware the same as if it was handling MVC unhandled exceptions. The client will get a 500 with a HTML response, but at that point there isn't much the client can do anyway so no harm.
Thanks for your help!

Httpclient request in angular json error

I am doing http client request
export class MapjsonService{
theUrl = 'http://localhost:4200/api/Lat_Long.json';
constructor(private http: HttpClient) { }
fetchNews(): Observable<any>{
return this.http.get(this.theUrl)
}
It is working about 99.99% of the time sadly this is running so often that is fails like once every 10 mins with
HttpErrorResponse {headers: HttpHeaders, status: 200, statusText: "OK", url: "http://localhost:4200/api/Lat_Long.json", ok: false, …}
and
"Http failure during parsing for http://localhost:4200/api/Lat_Long.json"
Now I figured out for some reason my nrql query from newrelic (which is what is being stored in '/api/lat_long.json' does not have the final closing '}' once every orange moon. and this is what is throwing this error. my question is there any whay for me to check if the returned value is valid json and if it is not try the GET request again without terminating the process that called it. Thx
Your code is throwing an error because the json is not correct, therefore it can't be parsed, and therefore the observable throws an error:
fetchNews(): Observable<any>{
return this.http.get(this.theUrl)
}
By default, the http client expect json because that's usually what users expect from it. It's not always the case, like the situation you are in right now.
We can tell the http client not to parse the json on its own by specifying what we want from it using the {responseType: 'text'} parameter.
fetchNews(): Observable<any>{
return this.http.get(this.theUrl, {responseType: 'text'})
}
But then you need to parse the json when possible. So we will map the observable and parse the content here if possible.
fetchNews(): Observable<any>{
return this.http.get(this.theUrl, {responseType: 'text'}).map(res => {
try{
return JSON.parse(res);
} catch {
return null;
}
})
}
Then do whatever you want, the value returned by the observable will be null if it can't be parsed.
RXJS 6 syntax:
fetchNews(): Observable<any>{
return this.http.get(this.theUrl, {responseType: 'text'}).pipe(
map(res => {
try{
return JSON.parse(res);
} catch {
return null;
}
})
)
}

RestEasy - JSON response - From Angular2 client, how to only get JSON Object

I'm new to REST services, I have an Angular2 client calling a RestEasy JAX-RS service. All I am trying to get is a "Hello World" message in JSON format. I was expecting only a JSON object, but I get my response with the following structure:
_body: "{"message":"Hello World!!"}"
headers: t
ok: true
status: 200
statusText: "OK"
type: 2
url: "http://localhost:8080/helloapp/rest/hello/world"
__proto__: ...
My question is, Is that the way it should be?
I mean, I thought I would be able to access the JSON object straight from the response. Something like
this.service.getHello()
.then( result => {
console.log(JSON.parse(result)); //{message: "Hello World"}
this.message = JSON.parse(result).message;
});
But I actually have to get it from _body:
this.service.getHello()
.then( result => {
this.message = JSON.parse(result._body).message;
console.log(this.message);//Hello World
});
Is it a RestEasy configuration thing, is there a way to change that?
Or
Should I consider that I will always have a field _body in my response with my data, and that's the default response structure?
For eventual consideration, here is my backend code:
HelloWorld Service:
#Path("/hello")
#Produces({ "application/json" })
#Consumes({ "application/json" })
public class HelloWorld {
public HelloWorld() {}
#GET
#Path("/world")
public Message getHello(){
return new Message("Hello World!!");
}
}
My RestEasy version is 3.1.1.Final running in Wildfly 10.1.0.Final
What you're getting back is the Response object from the Http request. This is what all Http operations will return. The easiest way to parse the JSON from that is to just call the json() method on it
this.service.getHello()
.then((res: Response) => {
let obj = res.json();
});
If you want the getHello to just return the object without having to parse it (on the calling client), then you can do it inside the getHello method by mapping it (using the Observable.map operation)
getHello() {
this.http.get(..)
.map((res: Response) => res.json())
.toPromise();
}
As peeskillet says above, you're getting back the entire Response from the request, and while sometimes you may want to examine the headers, perhaps to handle the different return conditions (retry or redirect on 4xx or 5xx responses for example), most of the time we assume a successful request and we just want the payload.
Angular2 encourages the use of Observables, so your service might look something like this:
getHello()
{
return this.http.get(http://localhost:8080/helloapp/rest/hello/world)
}
And your component may look something like this:
data: string;
ngOnInit() {
this.service
.getHello()
.map(response => response.json())
.subscribe (
data => {
this.data = data,
},
err => console.log('Error',err),
() => console.log('data',this.data)
);
}
You call the service, which is an http.get() and returns an Observable object, and we use .map to parse the response as JSON, which also returns an Observable, which we subscribe to.
Subscribe has three callback functions,
.subscribe(success, failure, complete)
In the example above on success we assign the payload - data - to this.data, if the subscribe fails, you log the error, and when it completes, we can do whatever we like, but in this case, we log this.data to the console - that's optional, but I log out the results while developing and then strip them out later.

Spring MVC either sending JSON or Redirecting to another page

Hello I have been using Spring 3 for my project, I have been stuck in on point.
if(ajax){
User user = userTemplate.getUser(form.getCreator_id());
int isPremium = user.getPremium();
if ( isPremium == 1 ){
Map<String,String> resultMap = new HashMap<String,String>();
response.setHeader("Access-Control-Allow-Headers", "*");
response.addHeader("Access-Control-Allow-Origin", "*");
resultMap.put("result", "success");
return new Gson().toJson(resultMap);
}else{
return "redirect:/f/redirectedUrl?url="+form.getWeb_page();
}
}
redirectedUrl controller is just for redirecting, but if the request is ajax request then i want to response the request as json.
How can I achieve this, thanks.
Edit : I can understand if request is ajax or not. My problem is if it is ajax i want to response json, if it is not then i want to redirect.
Use this code in your controller to identify if request is ajax or not and based on that you can add your logic.
boolean ajax = "XMLHttpRequest".equals(
getRequest().getHeader("X-Requested-With"));
You can decide it from header("X-Requested-With") of your httpRequest object.
public ModelAndView getDetails(HttpServletRequest request, HttpServletRespone response) {
if(ajax) {
try {
new MappingJacksonHttpMessageConverter().write(object, MediaType.APPLICATION_JSON, new ServletServerHttpResponse(response));
} catch(Exception e) {
logger.error("Error when converting to json");
}
return null;
} else {
return new ModelAndView("viewName");
}
}