In my android application I have to do some http request using android volley. If my request succeed everything's ok, the problem arise when I get an error and this error has status code 401. In this case I want to make some stuff and repeat the same request, same url and same parameters. Is there an official way to do that? If not, how can I get my params from error?
StringRequest req = new StringRequest(method, URL,
new Response.Listener<String>() {
#Override
public void onResponse(String response){
//VolleyLog.v("Response:%n %s", response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
NetworkResponse response = error.networkResponse;
if(response.statusCode == 401){
//make some stuff...
//here i want to resend my request
}
}
}) {
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
//get headers
}
#Override
public Map<String, String> getParams() throws AuthFailureError {
//get params
}
};
// add the request object to the queue to be executed
ApplicationController.getInstance().addToRequestQueue(req);
Any help would be appreciated.
You can create the RetryPolicy to change default retry behavior, only specify timeout milliseconds, retry count arguments :
public class YourRequest extends StringRequest {
public YourRequest(String url, Response.Listener<String> listener,
Response.ErrorListener errorListener) {
super(url, listener, errorListener);
setRetryPolicy(new DefaultRetryPolicy(DefaultRetryPolicy.DEFAULT_TIMEOUT_MS,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
}
}
the another way is estimate the VolleyError, re-execute the same request again when if was TimeoutError instance :
public static void executeRequest() {
RequestQueue.add(new YourRequest("http://your.url.com/", new Response.Listener<String>() {
#Override
public void onResponse(String response) {
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
if (error instanceof TimeoutError) {
// note : may cause recursive invoke if always timeout.
executeRequest();
}
}
}));
}
Hope this will help you
After modifying the request add the request in requestQueue.
Do it in ErrorListener.
Related
I'm using a Custom Filter to make the request Authetincation. Configuring the AutheticationFailureHandler, I want put the message error content, but it is attached to a existent default content, my goal is put together on the same body or just a new one.
Handler impl.:
protected void configure(HttpSecurity httpSecurity) throws Exception {
APIKeyAuthFilter apiKeyAuthFilter = new APIKeyAuthFilter(HEADER_USER_AGENT, HEADER_APIKEY);
apiKeyAuthFilter.setAuthenticationFailureHandler((request, response, exception) -> {
if(exception.getMessage().equals(MESSAGE_USER_AGENT_NOT_FOUND)) {
Map<String, Object> data = new HashMap<>();
data.put("message",MESSAGE_USER_AGENT_NOT_FOUND);
response.getOutputStream()
.println(objectMapper.writeValueAsString(data));
}
});
}
Response body:
{
"message": "User-Agent not found."
}
{
"timestamp": "2020-07-16T16:26:59.438+0000",
"status": 403,
"error": "Forbidden",
"message": "Access Denied",
"path": "/api"
}
Like shows, this is returning two JSON contents. How I set just one or append to existent?
Whole code:
public class APIKeyAuthFilter extends AbstractPreAuthenticatedProcessingFilter {
private String userAgentRequestHeader;
private String apiKeyRequestHeader;
public APIKeyAuthFilter(String userAgentRequestHeader, String apiKeyRequestHeader) {
this.userAgentRequestHeader = userAgentRequestHeader;
this.apiKeyRequestHeader = apiKeyRequestHeader;
}
#Override
protected RequestAuthVo getPreAuthenticatedPrincipal(HttpServletRequest request) {
String userAgent = request.getHeader(userAgentRequestHeader);
String apiKey = request.getHeader(apiKeyRequestHeader);
RequestAuthVo requestAuthVo = new RequestAuthVo(userAgent,apiKey);
return requestAuthVo;
}
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
return "N/A";
}
}
Configure:
#Override
#Order(2)
protected void configure(HttpSecurity httpSecurity) throws Exception {
APIKeyAuthFilter apiKeyAuthFilter = new APIKeyAuthFilter(HEADER_USER_AGENT, HEADER_APIKEY);
apiKeyAuthFilter.setAuthenticationManager(authentication -> {
RequestAuthVo requestAuthVo = (RequestAuthVo) authentication.getPrincipal();
if (!mapAuths.containsKey(requestAuthVo.getUserAgent())) {
throw new UserAgentNotFoundException(MESSAGE_USER_AGENT_NOT_FOUND);
}
if(mapAuths.containsKey(requestAuthVo.getUserAgent()) && mapAuths.get(requestAuthVo.getUserAgent()).equals(requestAuthVo.getApiKey())) {
authentication.setAuthenticated(true);
return authentication;
}
return authentication;
});
ObjectMapper objectMapper = new ObjectMapper();
apiKeyAuthFilter.setAuthenticationFailureHandler((request, response, exception) -> {
if(exception.getMessage().equals(MESSAGE_USER_AGENT_NOT_FOUND)) {
Map<String, Object> data = new HashMap<>();
data.put("message",MESSAGE_USER_AGENT_NOT_FOUND);
response.getOutputStream()
.println(objectMapper.writeValueAsString(data));
}
});
httpSecurity.antMatcher("/**").
csrf().disable().
sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).
and()
.addFilter(apiKeyAuthFilter).authorizeRequests().anyRequest().authenticated();
}
}
Can you try this?
apiKeyAuthFilter.setAuthenticationFailureHandler(
(request, response, exception) -> {
if(exception.getMessage().equals(MESSAGE_USER_AGENT_NOT_FOUND)) {
Map<String, Object> data = new HashMap<>();
data.put("message",MESSAGE_USER_AGENT_NOT_FOUND);
request.setAttribute("AGENT_NOT_FOUND",
objectMapper.writeValueAsString(data));
}
});
http.exceptionHandling()
.authenticationEntryPoint((request, response, e) ->
{
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
String value = request.getAttribute("AGENT_NOT_FOUND").toString();
response.getWriter().write(value);
});
}
I'm trying to make an application to work with the SMS distribution
service API. To send a message, I need to complete a curl request
https://email:api_key#gate.smsaero.ru/v2/sms/send?numbers[]=79990000000&numbers[]=79990000001&text=your+text&sign=SMS Aero&channel=DIRECT
How can this be implemented in Android?
This may works
public void send(String url) throws IOException {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
call.cancel();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
}
});
}
For a simple gRPC service defined by proto file:
service HelloService {
rpc sayHello() returns (stream string)
}
How can i wait on the client side to first wait for the headers to arrive before i start to process the response from server? I tried using ClientInterceptor and override the onHeaders() method but it gets called after the call to sayHello() is completed already. How can i validate for a specific header inside the sayHello client and proceed with the call based on the validity of the header?
public class SomeHeaderInterceptor implements ClientInterceptor {
private static final String FULL_METHOD_NAME = "sayHello";
public static CallOptions.Key<String> someHeader = CallOptions.Key.of("some_header_active", "false");
#Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> methodDescriptor,
CallOptions callOptions, Channel channel) {
return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(channel.newCall(methodDescriptor, callOptions)) {
#Override
public void start(Listener<RespT> responseListener, Metadata headers) {
super.start(new ForwardingClientCallListener.SimpleForwardingClientCallListener<RespT>(responseListener) {
#Override
public void onHeaders(Metadata headers) {
Metadata.Key<String> SAYHELLO_ACTIVE_HEADER = Metadata.Key.of("some_header_active",
Metadata.ASCII_STRING_MARSHALLER);
if (methodDescriptor.getFullMethodName().equals(FULL_METHOD_NAME)) {
if (!headers.containsKey(SAYHELLO_ACTIVE_HEADER)) {
LOGGER.logError("some_header activation missing from header: " + headers);
} else {
callOptions.withOption(someHeader, "true");
Context.current().withValue(Context.key("test"), "testvalue");
}
}
super.onHeaders(headers);
}
}, headers);
}
};
}
And the code for sayHello is as follows:
public Iterator<String> sayHello() {
Iterator<String> stream = blockingStub.sayHello();
// wait for the sayhello active header
boolean isActive = Boolean.parseBoolean(blockingStub.getCallOptions().getOption(SomeHeaderInterceptor. someHeader));
System.out.println("the some_header header value is: " + isActive);
System.out.println("the context key : " + Context.key("test").get(Context.current()));
return stream;
}
In the sayHello() code above, it doesn't wait for the headers to arrive and context to be set in the onHeaders(). how can i do that? I only want to return the stream back to the client caller after i validate the presence of the some_header in the stream sent by the server.
The headers are sent by the server before the first message, so the easy way would be to call stream.hasNext(), which will block waiting on a message. In many RPCs the first message comes pretty soon after the response headers, so this would work reasonably well.
As an aside, I noticed you experimenting with CallOptions and Context:
callOptions.withOption(someHeader, "true");
Context.current().withValue(Context.key("test"), "testvalue");
Neither of those lines really does anything because both objects are immutable. The with* calls create a new instance, so you have to use the return value for the line to do anything. Also, CallOptions and Context predominantly pass information in the opposite direction, like from the client application to interceptors. To "reverse" the direction, the application needs to set up a value that is mutable, like AtomicReference or a callback function, and then the interceptor could interact with that value.
If there may be a noticeable delay between when the server responds with the headers and the first message, then things get more complex. The code calling sayHello():
CompletableFuture<Boolean> future = new CompletableFuture<>();
Iterator<String> stream = blockingStub
.withOption(SomeHeaderInterceptor.SOME_HEADER, future)
.sayHello();
// wait for the sayhello active header
boolean isActive = future.get();
And then in the interceptor:
private static final String FULL_METHOD_NAME =
//"helloworld.Greeter/SayHello";
GreeterGrpc.getSayHelloMethod().getFullMethodName();
public static final CallOptions.Key<CompletableFuture<Boolean>> SOME_HEADER =
CallOptions.Key.create("some_header_active", CompletableFuture.completedFuture(false));
private static final Metadata.Key<String> SAYHELLO_ACTIVE_HEADER =
Metadata.Key.of("some_header_active", Metadata.ASCII_STRING_MARSHALLER);
#Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> methodDescriptor,
CallOptions callOptions, Channel channel) {
CompletableFuture<Boolean> future = callOptions.getOption(SOME_HEADER);
if (!methodDescriptor.getFullMethodName().equals(FULL_METHOD_NAME)) {
future.complete(false);
return channel.newCall(methodDescriptor, callOptions);
}
return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(channel.newCall(methodDescriptor, callOptions)) {
#Override
public void start(Listener<RespT> responseListener, Metadata headers) {
super.start(new ForwardingClientCallListener.SimpleForwardingClientCallListener<RespT>(responseListener) {
#Override
public void onHeaders(Metadata headers) {
if (!headers.containsKey(SAYHELLO_ACTIVE_HEADER)) {
// Could also be future.complete(false)
future.completeExceptionally(new Exception("some_header activation missing from header: " + headers));
} else {
future.complete(true);
}
super.onHeaders(headers);
}
#Override
public void onClose(Status status, Metadata trailers) {
// onHeaders() might not have been called, especially if there was an error
if (!future.isDone()) {
future.completeExceptionally(status.asRuntimeException(trailers));
}
super.onClose(status, trailers);
}
}, headers);
}
};
}
If you only need to do validation, not actually delay, then you could avoid the delay and just let the interceptor fail the call if validation fails:
private static final String FULL_METHOD_NAME =
//"helloworld.Greeter/SayHello";
GreeterGrpc.getSayHelloMethod().getFullMethodName();
private static final Metadata.Key<String> SAYHELLO_ACTIVE_HEADER =
Metadata.Key.of("some_header_active", Metadata.ASCII_STRING_MARSHALLER);
#Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> methodDescriptor,
CallOptions callOptions, Channel channel) {
if (!methodDescriptor.getFullMethodName().equals(FULL_METHOD_NAME)) {
return channel.newCall(methodDescriptor, callOptions);
}
// We use context to cancel since it is thread-safe, whereas ClientCall.cancel is not
CancellableContext context = Context.current().withCancellation();
class ValidatingListener extends ForwardingClientCallListener<RespT> {
private Listener<RespT> responseListener;
public ValidatingListener(Listener<RespT> responseListener) {
this.responseListener = responseListener;
}
#Override
protected Listener<RespT> delegate() {
return responseListener;
}
#Override
public void onHeaders(Metadata headers) {
if (!headers.containsKey(SAYHELLO_ACTIVE_HEADER)) {
Listener<RespT> saved = responseListener;
responseListener = new Listener<RespT>() {}; // noop listener; throw away future events
Status status = Status.UNKNOWN.withDescription(
"some_header activation missing from header: " + headers);
context.cancel(status.asRuntimeException());
saved.onClose(status, new Metadata());
return;
}
// Validation successful
super.onHeaders(headers);
}
#Override
public void onClose(Status status, Metadata trailers) {
context.close();
}
}
Context toRestore = context.attach();
try {
return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(channel.newCall(methodDescriptor, callOptions)) {
#Override
public void start(Listener<RespT> responseListener, Metadata headers) {
super.start(new ValidatingListener(responseListener), headers);
}
};
} finally {
context.detach(toRestore);
}
}
I can't save data loaded from okhttp. What is wrong in my code. When I try to load data on Log app crashes. Please Help.
public class MainActivity extends AppCompatActivity {
private static final String URL = "https://chroniclingamerica.loc.gov/search/titles/results/?terms=michigan&format=json&page=5";
private String data;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loadData();
Log.d("DATA", data);
}
private void loadData() {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(URL)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
}
#Override
public void onResponse(Call call, Response response) throws IOException {
data = response.body().string();
}
});
}
}
Explanation
The problem is around how your code is being executed (i.e sync vs async).
You call the method loadData() which will not set the class attribute data as you're expecting - it will set the attribute data once it gets a response however that function loadData will not wait for the response (i.e it doesn't block the current thread or execution code.) . Which means on the next line LOG.d("Data", data);, data has not been set, thus is null (and will crash your app).
Solution
If you just want to LOG data, then just move your log statement after you assign it on your onResponse callback.
private void loadData() {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(URL)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
// Some error has occurred
}
#Override
public void onResponse(Call call, Response response) throws IOException {
processData(response.body().string());
}
});
}
private void processData(String data) {
Log.d("DATA", data);
// To other stuff here given your value data.
}
I want to send a message from Android okhttp3 websocket client, not just echoing the messages coming from the server. here is my websocket class:
// Websocket
public final class EchoWebSocketListener extends WebSocketListener {
private static final int NORMAL_CLOSURE_STATUS = 1000;
#Override
public void onOpen(WebSocket webSocket, Response response) {
webSocket.send(message);
}
#Override
public void onMessage(WebSocket webSocket, String text) {
printer("text");
}
#Override
public void onClosing(WebSocket webSocket, int code, String reason) {
webSocket.close(NORMAL_CLOSURE_STATUS, null);
}
#Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
}
public void send(WebSocket webSocket) {
webSocket.send(finalMessage);
}
}
And here is how I call it from the onCreate method:
client= new OkHttpClient();
Request request = new Request.Builder().url(Url).build();
EchoWebSocketListener listener = new EchoWebSocketListener();
webso = myClient.newWebSocket(request, listener);
client.dispatcher().executorService().shutdown();
I tried to make an object to the class and call a function I named it 'send':
EchoWebSocketListener object = new EchoWebSocketListener ()
object.send(webso)
But that's not working?
recommend you this way RxWebSocket