we are using spring-session-data-redis starter, and meet one problem during this process.
We have two application A and B, and want to share same redis between A and B.
the problem is as below:
We call API in application A to add one User object to current Session:(url: http://localhost:9001/test) logic as below.
#RequestMapping("/test")
#ResponseBody
public User addUser(HttpServletRequest httpServletRequest) {
User user = new User("TEST", 10);
HttpSession session = httpServletRequest.getSession(true);
session.setAttribute("SESSION_USER", user);
return user;
}
Then call API in application B to visit another service ( this service is very simple, return a String) (url: http://localhost:9002/test) logic as below.
#RequestMapping("/test")
public String test() {
return "OK";
}
But application B will return error when visit url: http://localhost:9002/test, and application B does not have this class(com.du.demo.demo1.controller.model.User) under classpath..
There was an unexpected error (type=Internal Server Error, status=500).
Could not read JSON: Could not resolve type id 'com.du.demo.demo1.controller.model.User' into a subtype of [simple type, class java.lang.Object]: no such class found at [Source: [B#703e37; line: 1, column: 11]; nested exception is com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve type id 'com.du.demo.demo1.controller.model.User' into a subtype of [simple type, class java.lang.Object]: no such class found at [Source: [B#703e37; line: 1, column: 11]
My question is that whether there are some configuration to skip this? Exception will only throw when we use this object? Just want to stop the automatically deserialize process...
Related
spring-data-cassandra version 3.4.6
spring-boot version 2.7.7
cassandra version 3.11
DataStax Java driver 3.10.0
We have a Controller method POST /all calling a service class with a transactional method inserting a Flux of simple versioned entities:
Controller
#PostMapping("/all")
#Operation(summary = "Create all data for the request account")
public Flux<SampleCassandraData> create(#RequestBody Flux<SampleCassandraData> datas,
RequestContext context) {
datas = datas.doOnNext(data -> data.setAccountId(context.getAccountId()));
return service.create(datas);
}
Service method
#Transactional
public Flux<SampleCassandraData> create(#NonNull Flux<SampleCassandraData> datas) {
return repository.insert(datas);
}
The entity:
#Table("sample_table")
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class SampleCassandraData {
#PrimaryKeyColumn(name = "account_id", type = PARTITIONED)
#EqualsAndHashCode.Include
private String accountId;
#PrimaryKeyColumn(name = "id", type = CLUSTERED, ordering = ASCENDING)
#EqualsAndHashCode.Include
private UUID id;
#Column("content")
private String content;
#Version
private Long version;
}
We have an integration test using cassandra test container inserting an element and then sending an array of 2 elements including the existing one. The expected response status is 409 CONFLICT with an OptimisticLockingFailureException in the logs and no data inserted.
#Test
void when_POST_all_with_duplicated_in_1st_then_CONFLICT() {
val existingData = given_single_created();
datas.set(0, existingData);
val newData = given_new_data();
datas.set(1, newData);
when_create_all(datas);
then_error_json_returned(response, CONFLICT);
then_data_not_saved(newData);
}
Test results:
HttpStatus is 409 CONFLICT as expected
Response json body is our error response as expected
Service logs show the OptimisticLockingFailureException stacktrace as expected
Test fails: newData was saved in Cassandra when we expected the transaction to be fully rollbacked.
What are we doing wrong?
Is there a configuration, annotation field we must set to enable full rollback?
Or is it the reactive nature and we can't expect full rollback?
Thanks in advance
I am trying to port code that had been using Microsoft.WindowsAzure.Storage classes to use the newer classes in Azure.Data.Tables, Azure.Storage.Queues, etc. From what I have been able to discern, the StorageException class has been replaced by RequestFailedException. Unfortunately, there are some properties in StorageException that do not exist in RequestFailedException, making it difficult to log appropriate messages when an exception is encountered (for example: RequestId, RequestInformation, etc.).
The migration document does not address the differences between StorageException and the new RequestFailedException, or how to get error details from it.
It seems that either the new libraries are not yet mature enough for prime time, or maybe it is just because the documentation is lacking the relevant information and I can't find the appropriate methodologies for getting all of the error information from the RequestFailedException.
Does anyone know how to get more data out of the new class? Here are some examples of what we used to do:
catch (StorageException e)
{
operation.Telemetry.Properties.Add("AzureServiceRequestID", e.RequestInformation.ServiceRequestID);
Changing the above to use RequestFailedException is a problem because RequestInformation is not a property of RequestFailedException.
Here is another case:
catch (StorageException se)
{
var ri = se.RequestInformation;
if (ri.ErrorCode == "TableNotFound")
{
Logger.Info(
$"{SJResult.MakeInfo(64)} {ri.HttpStatusCode} {ri.HttpStatusMessage}, Storage Service code={ri.ErrorCode} This is OK if HL7 has not yet received messages."); // 60240040
}
else
{
Logger.Error(
$"{SJResult.MakeError(65)} HttpStatusCode: {ri.HttpStatusCode}, HttpStatusMessage: {ri.HttpStatusMessage}, Storage Service code={ri.ErrorCode}, " +
$"Extended.ErrorCode: {ri.ExtendedErrorInformation.ErrorCode} Extended.ErrorMessage: {ri.ExtendedErrorInformation.ErrorMessage}"); // E0240041
throw;
}
Again, RequestInformation is not available in RequestFailedException.
How do we get access to all the detailed information (RequestInformation) about an exception from the new RequestFailedException class?
As you can see the definition of RequestFailedException Class (Azure) and constuctors in the latest version of azure sdk.
RequestFailedException(Int32, String, String, Exception) : gives HTTP status code ,specified error message, error code, and a reference to the inner exception .
And
RequestFailedException(Response)
Gives error message, HTTP status code, error code obtained from the specified response.
The response in the argument represents the HTTP response from the service which has ClientRequestId as one of the properties as shown in the table which gets client request id that was sent to server in the form of x-ms-client-request-id headers.You can try the same while catching the error in the try-catch block.
In exception class you can give
public class RequestFailedException : Exception
{
...
public RequestFailedException(int status, string message, string? errorCode, Exception? innerException) : base(message , innerException) { }
}
Or use RequestFailedException(Response) from which you can get ClientRequestId.
I’ve not tested it myself, but please check if below can be worked around which is taken from the below references or check if something similar can give an idea.Also see if content property can be retrieved as a part of response.
try
{
...
}
catch (Exception aex)
{
foreach (var ex in aex.InnerExceptions)
{
if (ex is RequestFailedException except)
{
var innerException = excep.InnerException;
if (innerException != null && innerException.GetType() == typeof(WebException))
{
WebException webEx = innerException as WebException;
WebResponse resp = webEx.Response;
var responseHeaders = resp.Headers;
string requestId = responseHeaders["x-ms-request-id"];
Console.WriteLine("Request Id: " + requestId);
Console.WriteLine(except.InnerException.Message);
}
else
{
// (not a RequestFailedException)
Console.WriteLine($"{ex.Message}");
}
}
References:
How can I get Request ID when an exception occurs? (microsoft.com)
c# - How can you catch a RequestFailedException if making multiple
DownloadToAsync calls in parallel? - Stack Overflow
Consider this snippet from my REST client (Jersey 2.26). It's used to post objects and return the response object. If the REST server returns an error (status >= 400), then instead of returning an entity of type T I want to read an entity of type ErrorMessage and throw an exception containing the error message object.
protected <T> T post(final Class<T> type,
final Object entity,
final Map<String, Object> queryParams,
final String methodPath,
final Object... arguments) {
return postResponse(
getInvocationBuilderJson(methodPath,
queryParams,
arguments),
entity
).readEntity(type);
}
protected Response postResponse(final Invocation.Builder invocationBuilder,
final Object entity) {
return handleErrors(
invocationBuilder.post(Entity.entity(entity,
MediaType.APPLICATION_JSON_TYPE))
);
}
protected Response handleErrors(final Response response) {
if (response.getStatus() >= 400) {
throw new InvocationException(response.readEntity(ErrorMessage.class));
}
return response;
}
If no error occurs (status < 400), then my object of type T is returned as expected. However, when an error does occur, response.readEntity(ErrorMessage.class) returns null. But (and this is the strange part), this does get me data (at the handleErrors method):
byte[] data = readAllBytes((InputStream) response.getEntity());
I could use that and deserialize it manually.. but I would first like to know if there are any options to fix this without implementing workarounds.
After switching from the default MOXy JSON (de)serializer (we now are using a GSON provider) the problem was resolved.
We recently had a similar issue with JSON-B. There is turned out we had to add a getter and setter on our error object on order to (de)serialize the object.
I am use the FileHelper to parse CSV files. Error messages encountered when parsing the file are displayed to the end user. The end user may not be able to make sense of the technical error message. Not too many clerks know what an Int32 is or Class: UploadFooDto.
I would like to customize the error messages so they are more user friendly. Something like:
Line 1. Column 2. A string (a) was entered instead of a number
Line 2. Column 3. '13-14-15' is not a valid date
I cannot find anything in the API that would allow me to customize the error messages. The most I have so far are some extension methods to clean up the errors:
public static class FileHelperExceptionExtensions
{
public static string BuildMessage(this ErrorInfo error)
{
if (error.ExceptionInfo is ConvertException)
{
return ((ConvertException)error.ExceptionInfo).BuildMessage();
}
if (error.ExceptionInfo is BadUsageException)
{
var message = error.ExceptionInfo.Message;
var readTo = message.IndexOf("Class:");
return message.Substring(0, readTo);
}
return string.Format("Line: {0}. An unspecific error occured.", error.LineNumber);
}
public static string BuildMessage(this ConvertException exception)
{
return string.Format("Line: {0}. Column: {1}. Field: {2}. Cannot convert '{3}' to type: '{4}'", exception.LineNumber, exception.ColumnNumber, exception.FieldName, exception.FieldStringValue, exception.FieldType.Name);
}
}
but these extensions still leave a lot to be desired. Is it possible to customize the error messages?
It's hard to improve on your extension methods without it being more hassle than it's worth.
You cannot subclass the default converters (e.g., FileHelpers.ConvertHelpers.Int32Converter since they are internal and sealed). You could create your own custom converter for each type (and base it on the corresponding source code from FileHelpers, e.g., Int32Converter). Then you can raise an alternative to ConvertException (also sealed so you cannot subclass) which would format the message differently.
I have the following method.
public Foo GetById(int id)
{
ISession session = GetCurrentSession();
try
{
return session.Load<Foo>(id);
}
catch(NHibernate.ObjectNotFoundException onfe)
{
throw(onfe);
}
}
Unfortunately, onfe is never thrown. I want to handle the case that I only get
back a proxy because no adequate row exists in database.
I suggest you write your own ObjectNotFoundException and rewrite the method as:
public Foo GetById(int id)
{
Foo foo;
ISession session = GetCurrentSession();
foo = session.Get<Foo>(id);
if (foo == null)
{
throw new ObjectNotFoundException(string.Format("Foo with id '{0}' not found.", id));
}
}
There are two problems with your method as written:
You should use Get to load an entity by its key.
Your exception handling is wrapping the original exception and re-throwing for no reason.
If an entity is allowed to be lazy-loaded then Load method returns uninitialized proxy. The ObjectNotFoundException is thrown as soon as the proxy is about to be initialized.
Get method should be prefered when you are not sure that a requested entity exists.
See:
Nhibernate error: NO row with given identifier found error
,
https://sites.google.com/a/thedevinfo.com/thedevinfo/Home/or-persistence/hibernate/hibernate-faq, etc...