I'm using a web service to do some tasks. I have a problem in parsing the response of one of the APIs provided, which gives response as like below in different cases. The response code is 200 for both the cases.
A String(Not a JSON), when there are no records found in their database or invalid API version passed in the request URL.
A JSON, when the response is success(that is, in most of the cases)
So, I have gone with the below approach to fix this parsing issue. My doubt is, using Optional in this case, is right option or not?
// inside json parse utlity method
try {
// success
return Optional.of(objectMapper.readValue(responseBody, MyObject.class));
} catch(JsonParseException e) {
// when the response is not JSON. That is a `String`
return Optional.empty();
} catch(Exception e) {
throw new RuntimeException(e);
}
// do other stuff if value is present in Optional, otherwise skip
I'm using jackson library and Spring Boot REST service. If there is a better approach, please help me with that.
Related
I have a Core 2.2 Web API project as my back-end for an Angular front-end. On one screen I allow the user to select x amount of records for processing from a data grid. Before the records can be processed I need to check the database to see if records exist in another table by passing a list of 3 fields (intelligent key) to my API. I put this list into an object array, do a Json.stringify on that object and send it to my API as a Get request. This works fine as long as I select 1-3 records. As soon as I select 4 or more records I get "Access to XMLHttpRequest at 'request url' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource."
My Cors policy indicates it should let anything through and I'm also confused by why 1-3 records works fine.
In my startup.cs -> ConfigureServices method I have the Cors policy defined like so:
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.WithOrigins("http://localhost:4200")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
In my Angular service I make this call which serializes by array (apiUrl is my url to call my API: https://localhost/api/controller)
getRecordsByIntelligentKey(intelligentKey: IntelligentKey[]): Observable<Record[]>{
const jsonObject = JSON.stringify(intelligentKey);
const url = `${apiUrl}/${jsonObject}`;
return this.http.get<Record[]>(url).pipe(
tap(_ => console.log('fetching records based on intelligent key')),
catchError(this.handleError('getRecordsByIntelligentKey', []))
);
}
In my controller GET action I deserialize my string. I mean I'd like to pass an object but I think I need to do a POST for that.
[HttpGet("{jsonObject}")]
public ActionResult<List<Commission>> GetByCustomerAndTerritoryManager(string jsonObject)
{
try
{
var intelligentKey = JsonConvert
.DeserializeObject<List<ThreeFields>>(jsonObject);
return _repo.GetRecordsByIntelligentKey(intelligentKey);
}
catch (Exception ex)
{
_logger.Error(ex, "Error retrieving records.");
throw;
}
}
Now my problem is the user could select 1000s of records. When I select over 1000 records I just get ERR_CONNECTION_RESET probably due to the querystring being way too long.
I'm thinking I need an object but everything I've researched seems to advise against doing that with a GET and using the POST request instead. Problem is, it's a restful API and I'm already using the POST request for the processing portion. I guess I could use PUT or DELETE but it just feels wrong. I'm going to wire up the PUT right after I post this question to see if it will work but ultimately I'd like to find the correct solution for this.
UPDATE: The PUT method works fine even with over 1000 records selected so I guess this will be my interim solution for now. I still feel like there's code smell and would love to use a GET but at least this allows me to proceed.
Using yii2-httpclient, what is the correct way to access the corresponding yii\httpclient\Request instance from the resulting yii\httpclient\Response object?
I am trying to write a custom XML parser which needs to know what URL it is parsing. It does not seem to be possible to access the original Request (through which I could get the URL) from a parser instance (only the Response).
I have considered utilizing yii\httpclient\Client::EVENT_AFTER_SEND to copy the request into a variable, but that would not be thread-safe, so I need a better solution.
If your parser needs to know URL of request to parse response, it is probably not a parser and you're overusing parser API and ParserInterface. I suggest to create some component which will wrap and hide all request-response-parser logic. Then you will be able to implement custom parser and call it manually:
public function get($url) {
$client = new Client();
$response = $client->createRequest()
->setUrl($url)
->send();
return (new MyParser($url, $response))->getContent();
}
I have a WCF service that allows me make a request using an DTO and replies with a DTO for a WPF application. For example I pass a filter object for products which has a few properties for things I want to filter on and a couple of extras for paging, (the server will take care processing the filter object and getting the data) an example is like this.
public async Task<ObservableCollection<ProductListItem>> GetProductList(ProductFilter filter, int startIndex, int pageSize, string sortBy)
I am wondering if there exists any other technologies beside WCF that allow such an operation, From my preliminary research which may be quite off is that WebAPI uses the GET, POST, PUT verbs and routing rules which is quite different.
ServiceStack looks like it might be able to do this I can see on slide 37 at https://servicestack.net/features
it says.
List<Product> productOver2Bucks = client.Get(new FindProducts{PriceGreaterThan = 2})
Which seems pretty close but might still require Rest verbs as it uses a Get().
I don't know it it is FUD or not but I have been reading that soap over WCF is believed by some to be a legacy technology and JSON is the way of the future. So is there a replacement technology that will work with a method signature to the one I have above? That i could call from platforms such as Windows universal applications.
In ServiceStack if you design your Service with the Any method name, e.g
public object Any(Request request)
{
return new Response { ... };
}
This will allow calling this Service from Any HTTP Verb on any Format or endpoint (e.g. JSON, XML, MsgPack, Protocol Buffers, SOAP, Message Queue's, etc).
Also you don't need to define any [Route] for your Request DTO's since it will automatically fallback into using the pre-defined Routes when none are available.
public class Request : IReturn<Response> { ... }
public class Response { }
So with the above Service you can use ServiceStack .NET ServiceClients to call the API's using any verb, e.g:
var client = new JsonServiceClient(baseUrl);
Response response = client.Get(new Request { ... });
Response response = client.Post(new Request { ... });
When preferred you can also use the async API's, e.g:
var response = await client.GetAsync(new Request { ... });
var response = await client.PostAsync(new Request { ... });
Which if you don't care for using verbs you can use the generic Send API, e.g:
Response response = client.Send(new Request { ... });
Which just uses POST underneath, although it's highly recommended to use Get for "read only" queries as it will allow the Services HTTP responses to be cached by any intermediate HTTP Middleware or proxies.
Add ServiceStack Reference
Also if you're coming from WCF you'll also enjoy ServiceStack's, Add ServiceStack Reference which provides a number of advantages over WCF's Add Service Reference feature but still provides the same utility in being able to generate a typed API from a remote url for:
C# Add Reference
F# Add Reference
VB.NET Add Reference
TypeScript Add Reference
With more languages to follow.
Advantages over SOAP
Whilst ServiceStack still enables WSDL's, XSD's for your Services so they can be called from SOAP 1.1/1.2 endpoints for legacy compatible reasons - there are a number of reasons why using clean HTTP and JSON/XML API's are preferred.
I'm using the WebApi httpclient to build up a .net api library for use against a REST webservice.
The rest service returns JSON.
Problem i am having is that for one request, it is possible that i get diffrent JSON formats back.
If the query was successful, I get back a JSON array which I have made a strong c# type to hold it.
Using the ReadAsAsync< T > method to get it out of the content.
If the request had a bad api key in or another error happens, the rest service returns a JSON object with some properties like status=error and an explanation message etc.
I cant then just use the ReadAsAsync< T > method as I dont know what format is comming back. I don't know much about the JSON linq library but is there a way I can put the JSON response into some JSON holder object and then check if there is a status=error in it and then use the correct deserialization to my strong type.
I seem to be able to store it in a JRaw object but don't know where to go from here.
Many thanks.
If the request had a bad api key in or another error happens, the rest service returns a JSON object with some properties like status=error and an explanation message etc.
In this case, the status code returned will not be successful. You can do a check on the status code and then deserialize your response content appropriately:
if (httpResponseMessage.IsSuccessStatusCode)
{
// Deserialize your JSON array
}
else
{
// Deserialize the error
}
You can use error handling in this case
try
{
//Deserialize your JSON Array..this will throw an exception in case of type mismatch
}
catch(Exception e)
{
//Deserialize your JSON object which will give you Error code or message
}
I understand if I use springMVC and pass a json object to the controller, it will try to bind the json object to the controller pararmenter, but how to handle the binding error? I use something like this but seems not userful.
public String save(#RequestBody #Valid SomeList list, BindingResult result){
if(result.hasError()){
System.out.println(result);
}
}
Generally, you can return the same view that submitted the data. If you have <form:error> tags there, they will be displayed (because of the binding information).
But this is most certainly an ajax call, so what you can do is set a specific response status in the if body:
response.setStatus(HttpServletResponse.NOT_ACCEPTABLE);
and then look for that status code (406) in the ajax response handler. If you want precise validation information, you can try serializing the binding result itself as a response.
System.out.println will do next to nothing. Basically what it's saying is output the binding result to the server jvm std out.
Since you're returning a String, I'm going to assume you're returning a view name, so you might want to redirect the user to an error page.