I wanna send some DTO object to server. Server have "Valid" annotation, and when server getting not valid DTO, he should send validation errors and something like "HttpStatus.BAD_REQUEST", but when I'm trying to send HttpStatus.BAD_REQUEST doOnError just ignore it.
POST-request from client
BookDTO bookDTO = BookDTO
.builder()
.author(authorTf.getText())
.title(titleTf.getText())
.publishDate(LocalDate.parse(publishDateDp.getValue().toString()))
.owner(userAuthRepository.getUser().getLogin())
.fileData(file.readAllBytes())
.build();
webClient.post()
.uri(bookAdd)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(bookDTO)
.retrieve()
.bodyToMono(Void.class)
.doOnError(exception -> log.error("Error on server - [{}]", exception.getMessage()))
.onErrorResume(WebClientResponseException.class, throwable -> {
if (throwable.getStatusCode() == HttpStatus.BAD_REQUEST) {
log.error("BAD_REQUEST!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); --My log doesn't contain this error, but server still has errors from bindingResult
return Mono.empty();
}
return Mono.error(throwable);
})
.block();
Server-part
#PostMapping(value = "/add", consumes = {MediaType.APPLICATION_JSON_VALUE})
public HttpStatus savingBook(#RequestBody #Valid BookDTO bookDTO, BindingResult bindingResult) {
List<FieldError> errors = bindingResult.getFieldErrors();
if (bindingResult.hasErrors()) {
for (FieldError error : errors ) {
log.info("Client post uncorrected data [{}]", error.getDefaultMessage());
}
return HttpStatus.BAD_REQUEST;
}else{libraryService.addingBookToDB(bookDTO);}
return null;
}
doOnError is a so-called side effect operation that could be used for instrumentation before onError signal is propagated downstream. (e.g. to log error).
To handle errors you could use onErrorResume. The example, the following code handles the WebClientResponseException and returns Mono.empty instead.
...
.retrieve()
.doOnError(ex -> log.error("Error on server: {}", ex.getMessage()))
.onErrorResume(WebClientResponseException.class, ex -> {
if (ex.getStatusCode() == HttpStatus.BAD_REQUEST) {
return Mono.empty();
}
return Mono.error(ex);
})
...
As an alternative as #Toerktumlare mentioned in his comment, in case you want to handle http status, you could use onStatus method of the WebClient
...
.retrieve()
.onStatus(HttpStatus.BAD_REQUEST::equals, res -> Mono.empty())
...
Update
While working with block it's important to understand how reactive signals will be transformed.
onNext(T) -> T in case of Mono and List<T> for Flux
onError -> exception
onComplete -> null, in case flow completes without onNext
Here is a full example using WireMock for tests
class WebClientErrorHandlingTest {
private WireMockServer wireMockServer;
#BeforeEach
void init() {
wireMockServer = new WireMockServer(wireMockConfig().dynamicPort());
wireMockServer.start();
WireMock.configureFor(wireMockServer.port());
}
#Test
void test() {
stubFor(post("/test")
.willReturn(aResponse()
.withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.withStatus(400)
)
);
WebClient webClient = WebClient.create("http://localhost:" + wireMockServer.port());
Mono<Void> request = webClient.post()
.uri("/test")
.retrieve()
.bodyToMono(Void.class)
.doOnError(e -> log.error("Error on server - [{}]", e.getMessage()))
.onErrorResume(WebClientResponseException.class, e -> {
if (e.getStatusCode() == HttpStatus.BAD_REQUEST) {
log.info("Ignoring error: {}", e.getMessage());
return Mono.empty();
}
return Mono.error(e);
});
Void response = request.block();
assertNull(response);
}
}
The response is null because we had just complete signal Mono.empty() that was transformed to null by applying block
I have a problem getting an object from ListenableFutureCallback when I use delete function of AsyncRestTemplate. This is my source code:
ListenableFutureCallback<Object> callbackDelete = new ListenableFutureCallback<Object>() {
#Override
public void onFailure(Throwable ex) {
logger.error("Error processing the delete request: " + ex.getMessage());
}
#Override
public void onSuccess(Object result) {
System.out.println("Async Delete Success : " + result.toString() );
logger.debug("The request has been processed successfully");
}
};
ListenableFuture<?> response = asyncRestTemplate.delete(DELETE_URL + "/" + id);
response.addCallback(callbackDelete);
When I execute result.toString(), result is always null.
Thanks
I am new to REST. I have written a small REST resource and Whenever I try to invoke the REST service from POSTMAN, i get a empty response {} and status code 200
The Request :
http://localhost:8080/demo/managers
#GET
#Path("managers")
#Produces({"application/json"})
public Response getManagers() throws GeneralException, JSONException
{
JSONArray valueString = COMING_FROM_OTHER_METHOD();
System.out.println("==== "+valueString.toString());
return Response.ok(valueString,MediaType.APPLICATION_JSON).build();
}
The correct value I can see in System.out.println():
[{"display":"john","id":"003"},{"display":"hansi","id":"004"},{"display":"samy gayle","id":"005"}]
I want to a JSONArray Response but everytime I get an empty response
{}
But when modify the code like below it gives correct response
#GET
#Path("managers")
#Produces({"application/json"})
public String getManagers() throws GeneralException, JSONException
{
JSONArray valueString = COMING_FROM_OTHER_METHOD();
System.out.println("==== "+valueString.toString());
return valueString.toString();
}
Kindly Help. why am I getting {} when trying to return a Response object J
I would use domain objects rather than String instances:
class Manager {
private String id;
private String display;
... setters/getters ...
}
public ResponseEntity<ArrayList<Manager>> getManagers() throws GeneralException {
ArrayList<Manager> managers = COMING_FROM_OTHER_METHOD();
return new ResponseEntity<>(managers, HttpStatus.OK);
}
Can someone tell me if it is possible to silently print using google cloud print from an android device?
The goal is that my app grabs a file from a URL or from the SD card and then sends it to a specific printer - all without interaction from anyone looking at the screen or touching anything. It will actually be triggered by a barcode scan on a blue tooth connected device.
Thanks
Well, it is possible but I don't know why there's not too much information about it in the documentation...
The tricky part is connecting to the google cloud print API using only the android device (with no third party servers as the documentation explains here: https://developers.google.com/cloud-print/docs/appDevGuide ), so that's what I'm going to explain.
First, you have to include in your app the Google sign-in API, I recommend firebase API https://firebase.google.com/docs/auth/android/google-signin
Then you have to go to your Google API console: https://console.developers.google.com in the menu, go to Credentials scroll to OAuth 2.0 client IDs select Web client (auto created by Google Service) and save into your project the Client ID and Client secret keys... In my project, I saved them as "gg_client_web_id" and "gg_client_web_secret" as you will see below in the code.
Next, I'm going to paste all the code and then I'll explain it:
public class MainActivity extends AppCompatActivity
implements GoogleApiClient.OnConnectionFailedListener {
private GoogleApiClient mGoogleApiClient;
private FirebaseAuth mAuth;
private FirebaseAuth.AuthStateListener mAuthListener;
private static final int REQUEST_SINGIN = 1;
private TextView txt;
public static final String TAG = "mysupertag";
public static final String URLBASE = "https://www.google.com/cloudprint/";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txt = (TextView) findViewById(R.id.txt);
mAuth = FirebaseAuth.getInstance();
// Configure Google Sign In
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(R.string.gg_client_web_id))
.requestEmail()
.requestServerAuthCode(getString(R.string.gg_client_web_id))
.requestScopes(new Scope("https://www.googleapis.com/auth/cloudprint"))
.build();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */)
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
.build();
findViewById(R.id.sign_in_button).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
signIn();
}
});
mAuthListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
FirebaseUser user = firebaseAuth.getCurrentUser();
if (user != null) {
// User is signed in
Log.d(TAG, "onAuthStateChanged:signed_in:" + user.getUid());
} else {
// User is signed out
Log.d(TAG, "onAuthStateChanged:signed_out");
}
// ...
}
};
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
Log.d(TAG, "error connecting: " + connectionResult.getErrorMessage());
Toast.makeText(this, "error CONN", Toast.LENGTH_LONG).show();
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...);
if (requestCode == REQUEST_SINGIN) {
GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
if (result.isSuccess()) {
// Google Sign In was successful, authenticate with Firebase
GoogleSignInAccount account = result.getSignInAccount();
firebaseAuthWithGoogle(account);
} else {
// Google Sign In failed, update UI appropriately
// ...
Toast.makeText(this, "error ", Toast.LENGTH_LONG).show();
}
}
}
private void signIn() {
Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
startActivityForResult(signInIntent, REQUEST_SINGIN);
}
#Override
public void onStart() {
super.onStart();
mAuth.addAuthStateListener(mAuthListener);
}
#Override
public void onStop() {
super.onStop();
if (mAuthListener != null) {
mAuth.removeAuthStateListener(mAuthListener);
}
}
private void firebaseAuthWithGoogle(final GoogleSignInAccount acct) {
Log.d(TAG, "firebaseAuthWithGoogle:" + acct.getId());
AuthCredential credential = GoogleAuthProvider.getCredential(acct.getIdToken(), null);
mAuth.signInWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
Log.d(TAG, "signInWithCredential:onComplete:" + task.isSuccessful());
// If sign in fails, display a message to the user. If sign in succeeds
// the auth state listener will be notified and logic to handle the
// signed in user can be handled in the listener.
FirebaseUser user = task.getResult().getUser();
txt.setText(user.getDisplayName() + "\n" + user.getEmail());//todo
if (!task.isSuccessful()) {
Log.w(TAG, "signInWithCredential", task.getException());
Toast.makeText(MainActivity.this, "Authentication failed.",
Toast.LENGTH_SHORT).show();
}
getAccess(acct.getServerAuthCode());
}
});
}
private void getPrinters(String token) {
Log.d(TAG, "TOKEN: " + token);
String url = URLBASE + "search";
Ion.with(this)
.load("GET", url)
.addHeader("Authorization", "Bearer " + token)
.asString()
.withResponse()
.setCallback(new FutureCallback<Response<String>>() {
#Override
public void onCompleted(Exception e, Response<String> result) {
Log.d(TAG, "finished " + result.getHeaders().code() + ": " +
result.getResult());
if (e == null) {
Log.d(TAG, "nice");
} else {
Log.d(TAG, "error");
}
}
});
}
private void getAccess(String code) {
String url = "https://www.googleapis.com/oauth2/v4/token";
Ion.with(this)
.load("POST", url)
.setBodyParameter("client_id", getString(R.string.gg_client_web_id))
.setBodyParameter("client_secret", getString(R.string.gg_client_web_secret))
.setBodyParameter("code", code)
.setBodyParameter("grant_type", "authorization_code")
.asString()
.withResponse()
.setCallback(new FutureCallback<Response<String>>() {
#Override
public void onCompleted(Exception e, Response<String> result) {
Log.d(TAG, "result: " + result.getResult());
if (e == null) {
try {
JSONObject json = new JSONObject(result.getResult());
getPrinters(json.getString("access_token"));
} catch (JSONException e1) {
e1.printStackTrace();
}
} else {
Log.d(TAG, "error");
}
}
});
}}
As you can see, in the onCreate the important part is creating the GoogleSignInOptions WITH the google cloud print scope AND calling the requestIdToken/requestServerAuthCode methods.
Then in the firebaseAuthWithGoogle method call the getAccess method in order to get the OAuth access token, for making all requests I'm using Ion library: https://github.com/koush/ion
Next with the access_token you can now do requests to the google cloud print API, in this case I call the getPrinters method, in this method I call the "search" method (from google cloud print API) to get all the printers associated to the google account that has signed in.. (to associate a printer to a google account visit this: https://support.google.com/cloudprint/answer/1686197?hl=en&p=mgmt_classic ) Note the .addHeader("Authorization", "Bearer " + token), this is the important part of the request, the "token" var is the access_token, you NEED add this Authorization header in order to use the API and don't forget to refresh when it expires, as explained here : https://developers.google.com/identity/protocols/OAuth2ForDevices in the "Using a refresh token" part.
And that's it, you can now print something sending a POST request to the "submit" method of the google cloud print API, I recommend to go here: https://developers.google.com/cloud-print/docs/appInterfaces and see all the methods available and how to use them (wich parameters send to them, etc). Of course in that link explains the "submit" method too.
EDIT:
EXAMPLE OF HOW TO SEND A REQUEST TO "/submit" FOR PRINTING USING ION LIBRARY AND MJSON LIBRARY (https://bolerio.github.io/mjson/) THE MJSON IS FOR CREATING A JSON OBJECT, YOU CAN CREATE IT THE WAY YOU PREFER
private void printPdf(String pdfPath, String printerId) {
String url = URLBASE + "submit";
Ion.with(this)
.load("POST", url)
.addHeader("Authorization", "Bearer " + YOUR_ACCESS_TOKEN)
.setMultipartParameter("printerid", printerId)
.setMultipartParameter("title", "print test")
.setMultipartParameter("ticket", getTicket())
.setMultipartFile("content", "application/pdf", new File(pdfPath))
.asString()
.withResponse()
.setCallback(new FutureCallback<Response<String>>() {
#Override
public void onCompleted(Exception e, Response<String> result) {
if (e == null) {
Log.d(TAG, "PRINTTT CODE: " + result.getHeaders().code() +
", RESPONSE: " + result.getResult());
Json j = Json.read(result.getResult());
if (j.at("success").asBoolean()) {
Toast.makeText(MainActivity.this, "Success", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(MainActivity.this, "ERROR", Toast.LENGTH_LONG).show();
}
} else {
Toast.makeText(MainActivity.this, "ERROR", Toast.LENGTH_LONG).show();
Log.d(TAG, e.toString());
}
}
});
}
private String getTicket() {
Json ticket = Json.object();
Json print = Json.object();
ticket.set("version", "1.0");
print.set("vendor_ticket_item", Json.array());
print.set("color", Json.object("type", "STANDARD_MONOCHROME"));
print.set("copies", Json.object("copies", 1));
ticket.set("print", print);
return ticket.toString();
}
Yes, You can achieve silent print using this REST API(https://www.google.com/cloudprint/submit) ,I have done it using WCF Service.
you need to download contents from url as base64 content, then add
contentType=dataUrl
in the request.
Here is the code..
postData = "printerid=" + PrinterId;
postData += "&title=" + JobTitle;
postData += "&ticket=" + ticket;
postData += "&content=data:" + documentContent.ContentType + ";base64," + documentContent.Base64Content;
postData += "&contentType=dataUrl";
postData += "&tag=test";
Then , please make a request to submit REST API in this way.
var request = (HttpWebRequest)WebRequest.Create("https://www.google.com/cloudprint/submit");
var data = Encoding.ASCII.GetBytes(postData);
request.Headers.Add("Authorization: Bearer " + Token);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = data.Length;
request.UseDefaultCredentials = true;
using (var stream = request.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
var response = (HttpWebResponse)request.GetResponse();
string responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
JavaScriptSerializer json_serializer = new JavaScriptSerializer();
PrintJobResponse printInfo = json_serializer.Deserialize<PrintJobResponse>(responseString);
return printInfo;
Thanks.
For anybody reading this now, after a lot of searching around I have found it is a lot easier and faster to set up to just use Zapier to catch a hook and print to google cloud print (from cordova at least, i can't speak for native apps)
I'm trying to create an application which is able to work even when network is down.
The idea is to store data returned from RequestFactory on the localStorage, and to use localStorage when network isn't available.
My problem - I'm not sure exactly how to differentiate between server errors(5XX, 4XX, ...) and network errors.
(I assume that on both cases my Receiver.onFailure() would be called, but I still don't know how to identify this situation)
Any help would be appreciated,
Thanks,
Gilad.
The response code when there is no internet connection is 0.
With RequestFactory to identify that the request was unsuccessful because of the network the response code has to be accessed. The RequestTransport seems like the best place.
Here is a rough implementation of an OfflineAwareRequestTransport.
public class OfflineAwareRequestTransport extends DefaultRequestTransport {
private final EventBus eventBus;
private boolean online = true;
public OfflineAwareRequestTransport(EventBus eventBus) {
this.eventBus = eventBus;
}
#Override
public void send(final String payload, final TransportReceiver receiver) {
// super.send(payload, proxy);
RequestBuilder builder = createRequestBuilder();
configureRequestBuilder(builder);
builder.setRequestData(payload);
builder.setCallback(createRequestCallback(receiver, payload));
try {
builder.send();
} catch (RequestException e) {
}
}
protected static final int SC_OFFLINE = 0;
protected RequestCallback createRequestCallback(final TransportReceiver receiver,
final String payload) {
return new RequestCallback() {
public void onError(Request request, Throwable exception) {
receiver.onTransportFailure(new ServerFailure(exception.getMessage()));
}
public void onResponseReceived(Request request, Response response) {
if (Response.SC_OK == response.getStatusCode()) {
String text = response.getText();
setOnline(true);
receiver.onTransportSuccess(text);
} else if (response.getStatusCode() == SC_OFFLINE) {
setOnline(false);
boolean processedOk = processPayload(payload);
receiver.onTransportFailure(new ServerFailure("You are offline!", OfflineReceiver.name,
"", !processedOk));
} else {
setOnline(true);
String message = "Server Error " + response.getStatusCode() + " " + response.getText();
receiver.onTransportFailure(new ServerFailure(message));
}
}
};
}