I have a camel route like this:
this.from(uri).process(e -> {
String json = e.getIn().getBody(String.class);
JsonObject message = gson.fromJson(json, JsonObject.class);
Status status = gson.fromJson(message.get("data"), Status.class);
e.getIn().setHeader("JSON_OBJECT", message);
e.getIn().setBody(status);
}).to(jpaStatusUri).process(e -> {
JsonObject message = (JsonObject) e.getIn().getHeader("JSON_OBJECT");
StatusDetails details = gson.fromJson(message.get("data"), StatusDetails.class);
// If the first route was successfull i can get the id of the inserted entity here (this works)
details.setStatusId(e.getIn().getBody(Status.class).getId());
e.getIn().setBody(details);
}).to(jpaDetailsUri);
I send the entity Status to a jpa endpoint jpaStatusUri. The entity is inserted, and in the following process() method i create a corresponding StatusDetails entity, set it's statusId to the id of the previously saved Status and send it to the jpaDetailsUri endpoint.
This works as expected in case the Status entity has been saved successfully. In case of a ConstraintViolationException i.e. a unique key for the Status entity already exists this will throw the exception, i will not have a correct statusId and i will not be able to update the corresponding StatusDetails entity anymore.
Of course i could all handle this in the process() method, but what would be the "camel-way" to handle this ?
A camel-way to handle such things would be to use an onException :
onException(ConstraintViolationException.class)
.process(e -> {e.getIn().setBody(new ErrorObjectJson());})
.handled(true)
check out the documentation
There is also the try-catch way of handling exception that is provided by camel.
Related
Im getting an error with the blocking operation in Spring Webflux. I retrieve a Mono of list of Address documents and im using this Mono list of address documents to form the street address(withStreet)as shown below :
Mono<List<Address>> docs = getAddress(id, name);
AddressResponse addrResponse = new AddressResponse.Builder().
withStreet(docs.map(doc -> doc.stream().
map(StreetAddress::map).
collect(Collectors.toList())).block()).
build();
map method :
public static StreetAddress map(Address addr) {
return new Builder().
withId(addr.getId()).
withStreet(addr.getStreetAddress()).
build();
}
When i execute the above code, it throws a "block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-2". Could you please suggest how to fix. i want to retrieve AddressResponse without blocking it. This response will be further used in the code in Response Entity as shown below :
return Mono.just(ResponseEntity
.status(HttpStatus.OK)
.body(addrResponse)));
The problem is you try to mix reactive and imperative code.
Instead, just map it in the reactive pipeline:
Mono<AddressResponse> response = docs.map(addresses->{
return new AddressResponse.Builder()
.withStreet(addresses -> addresses.stream()
.map(StreetAddress::map)
.collect(Collectors.toList()))
.build();
})
Then you can return it as is, or map it into a Mono> type, apply the same method then above.
I'm using the ASP.NET Boilerplate Framework and I've created a custom app service that inherits from AsyncCrudAppServiceBase and I've overridden the Get function.
public override async Task<ExampleDto> Get(EntityDto<int> input)
{
var example = await Repository.GetAll().FirstOrDefault(x => x.Id == input.Id);
return ObjectMapper.Map<ExampleDto>(example);
}
However, because I'm using FirstOrDefault if the record doesn't exist it returns null and the HTTP Status is 200 instead of 404, which I want.
If I change the LINQ function to First or, ideally, Single then and exception is thrown but the HTTP Status is 500.
I can check if the record exists but how do I return a different HTTP status from an Application Service?
You should consider using repository get method in your app service.
See
https://aspnetboilerplate.com/Pages/Documents/Repositories#base-repository-methods
Reason being Repository.Get() will throw EntityNotFoundException when FirstOrDefault return null.
See https://github.com/aspnetboilerplate/aspnetboilerplate/blob/6bce9a0572170e8daf1b21ee982a869714579e2d/src/Abp/Domain/Repositories/AbpRepositoryBase.cs#L73-L82
And when Abp wraps the response, EntityNotFoundException will have NotFound as the HttpStatusCode
See https://github.com/aspnetboilerplate/aspnetboilerplate/blob/15e5f33885442bfd0996936191fd964144a95652/src/Abp.AspNetCore/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs#L99-L102
I have an http module where I'm adding a response filter below for compression. This works for all API calls except for 1, the call to MetaData. If I remove the [BreezeController] decoration it works fine. I think it has to do with action filter attribute that converts the string return type into an HttpResponse return type with string content.
The error I'm getting is " Exception message: The stream state of the underlying compression routine is inconsistent."
I've done some testing where a method thats defined to return an HttpResponse works fine. So I think its the scenario where the method is defined to return string, and then the action filter changes it to HttpResponse at runtime.
Any ideas how I can get this to work?
Here's the response filter being added in BeginRequest:
HttpApplication app = (HttpApplication)sender;
// Check the header to see if it can accept compressed output
string encodings = app.Request.Headers.Get("Accept-Encoding");
if (encodings == null)
return;
Stream s = app.Response.Filter;
encodings = encodings.ToLower();
if (encodings.Contains("gzip"))
{
app.Response.Filter = new GZipStream(s, CompressionMode.Compress);
app.Response.AppendHeader("Content-Encoding", "gzip");
}
Don't know the specifics of what you're doing but I know that the [BreezeController] attribute strips out filters and adds back just the ones that breeze wants.
One approach might be to define a separate controller (ModelMetadataController) that only serves the metadata. This controller doesn't have the [BreezeController] attribute; it's a plain old Web API controller.
Then you create a "Breeze controller" (ModelController) with all of the usual methods except the Metadata method.
You call the metadata controller from the client during app launch via MetadataStore.fetchMetadata just to get metadata.
Once you have populated a metadataStore in this fashion, you use it in your EntityManager which sends query and save requests to the "real" Web API data controller.
The client code might look something like this:
var ds = new breeze.DataService({
serviceName: 'breeze/Model' // the breeze query & save controller
});
var ms = new MetadataStore({
namingConvention: breeze.NamingConvention.camelCase, // assuming that's what you want
});
ms.addDataService(ds); // associate the metadata-to-come with the "real" dataService
var manager = new breeze.EntityManager({
dataService: ds,
metadataStore: ms
});
// the fun bit: fetch the metadata from a different controller
var promise = ms.fetchMetadata('breeze/ModelMetadata') // the metadata-only controller!
return promise; // wait on it appropriately
I have the following code in my web application:
#ExceptionHandler(InstanceNotFoundException.class)
#ResponseStatus(HttpStatus.NO_CONTENT)
public ModelAndView instanceNotFoundException(InstanceNotFoundException e) {
return returnErrorPage(message, e);
}
Is it possible to also append a status message to the response? I need to add some additional semantics for my errors, like in the case of the snippet I posted I would like to append which class was the element of which the instance was not found.
Is this even possible?
EDIT: I tried this:
#ResponseStatus(value=HttpStatus.NO_CONTENT, reason="My message")
But then when I try to get this message in the client, it's not set.
URL u = new URL ( url);
HttpURLConnection huc = (HttpURLConnection) u.openConnection();
huc.setRequestMethod("GET");
HttpURLConnection.setFollowRedirects(true);
huc.connect();
final int code = huc.getResponseCode();
String message = huc.getResponseMessage();
Turns out I needed to activate custom messages on Tomcat using this parameter:
-Dorg.apache.coyote.USE_CUSTOM_STATUS_MSG_IN_HEADER=true
The message can be in the body rather than in header. Similar to a successful method, set the response (text, json, xml..) to be returned, but set the http status to an error value. I have found that to be more useful than the custom message in header. The following example shows the response with a custom header and a message in body. A ModelAndView that take to another page will also be conceptually similar.
#ExceptionHandler(InstanceNotFoundException.class)
public ResponseEntity<String> handle() {
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("ACustomHttpHeader", "The custom value");
return new ResponseEntity<String>("the error message", responseHeaders, HttpStatus.INTERNAL_SERVER_ERROR);
}
I have the following situation, I have a WCF Data Service with User objects and message objects and the message object has two relations to user, a sender and a receiver.
When I try to add a new Message object the related users are left null
Message message = new Message();
message.text = InputText; // string
message.Sender = Sender; // User object
message.Receiver = Receiver; // User object
context.AddToMessages(message);
context.BeginSaveChanges(new AsyncCallback((result) =>
{
// Some code
}));
Now the Sender and Receiver will be null. When I try to set a link before the BeginSaceChanges like this I get the error "InvalidOperationException: The context is not currently tracking the entity."
context.AddToMessages(message);
context.AddLink(message, "Sender", message.Sender);
context.AddLink(message, "Receiver", message.Receiver);
context.BeginSaveChanges(new AsyncCallback((result) =>
{
// Some code
}));
How do I make sure the relations are created properly?
Thanks to Pratik I found the solution. I had to use attach the already existing users Sender and Receiver to the context first because they weren't tracked (and added a if if they are on the second call). Then I add the message and use SetLink to set the link to both users (instead of AddLink)
if(context.GetEntityDescriptor(message.Sender) == null)
context.AttachTo("Users", message.Sender);
if (context.GetEntityDescriptor(message.Receiver) == null)
context.AttachTo("Users", message.Receiver);
context.AddToMessages(message);
context.SetLink(message, "Sender", message.Sender);
context.SetLink(message, "Receiver", message.Receiver);
context.BeginSaveChanges(new AsyncCallback((result) =>
{
// Some code
}));
I believe you need to use the DbSet.Attach method instead. I assume you use Entity Framework on the back end here.