so I coded this class to Download URLs but it's returning Null Response
I tried to debug but didn't understand anything
package com.example.instaup;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class Downloader
{
private String myResponse;
public String DownloadText(String url)
{
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(#NotNull Call call, #NotNull IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(#NotNull Call call, #NotNull Response response) throws IOException {
if (response.isSuccessful()) {
myResponse = response.body().toString();
}
}
});
return myResponse;
}
}
Can Someone Help me? I'm kinda new to this
You should reuse the client, and use the synchronous form execute instead of the enqueue callback API which returns almost immediately before the request has finished.
import java.io.IOException;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class Downloader {
OkHttpClient client = new OkHttpClient();
public String DownloadText(String url) throws IOException {
Request request = new Request.Builder().url(url).build();
try (Response myResponse = client.newCall(request).execute()) {
return myResponse.body().string();
}
}
}
Related
The next code is the netty http server handler, it can operation normally.
package com.sunquakes.jsonrpc4j.server;
import com.alibaba.fastjson.JSON;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import org.springframework.context.ApplicationContext;
public class JsonRpcNettyHttpServerHandler extends ChannelInboundHandlerAdapter {
private ApplicationContext applicationContext;
public JsonRpcNettyHttpServerHandler(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
FullHttpRequest httpRequest = (FullHttpRequest) msg;
ByteBuf buf = httpRequest.content();
HttpMethod method = httpRequest.method();
if (!HttpMethod.POST.equals(method)) {
send(ctx, "", HttpResponseStatus.METHOD_NOT_ALLOWED);
return;
}
String body = buf.toString(CharsetUtil.UTF_8);
JsonRpcServerHandler jsonRpcServerHandler = new JsonRpcServerHandler(applicationContext);
Object res = jsonRpcServerHandler.handle(body);
String output = JSON.toJSONString(res);
send(ctx, output, HttpResponseStatus.OK);
httpRequest.release();
}
private void send(ChannelHandlerContext ctx, String context, HttpResponseStatus status) {
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, Unpooled.copiedBuffer(context, CharsetUtil.UTF_8));
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json; charset=utf-8");
response.headers().set(HttpHeaderNames.CONNECTION, "keep-alive");
ctx.channel().writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
}
But when I drop .addListener(ChannelFutureListener.CLOSE), The client won't receive message. I want to keep the channel keepalive. What can I do.
I've just get the code from response, and it says, that my request parameters are wrong, what should my api call look like then?
Here's the hardcoded api call from documenatation
https://api.themoviedb.org/3/discover/movie?api_key=[API_KEY]&with_genres=27
Here's my api call
#GET("3/search/movie")
Call<itemList_model> test(#Query("api_key") String key,#Query("with_genres") int query);
Code
Invalid parameters: Your request parameters are incorrect.
Retrofit call
public void getListViewItems() {
String url = "https://api.themoviedb.org/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build();
apiCall api = retrofit.create(apiCall.class);
Call<itemList_model> call = api.test("API_KEY",27); <- 27 stand's for horror genres.
call.enqueue(new Callback<itemList_model>() {
#Override
public void onResponse(Call<itemList_model> call, Response<itemList_model> response) {
if (!response.isSuccessful()) {
Log.i(TAG, "onResponse: " + response.code());
}
Log.i(TAG, "onResponse: "+response.code());
}
#Override
public void onFailure(Call<itemList_model> call, Throwable t) {
Log.i(TAG, "onFailure: " + t.getMessage());
}
});
}
Simple typo. Should be:
https://api.themoviedb.org/3/discover/movie?api_key=[API_KEY]&with_genres=27
But:
https://api.themoviedb.org/3/search/movie?api_key=[API_KEY]&with_genres=27
Working code
package test;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface RetrofitProxy {
#GET("3/discover/movie")
Call<Object> test(#Query("api_key") String apiKey, #Query("with_genres") int genreCode);
}
package test;
import retrofit2.Call;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import java.io.IOException;
public class RetrofitTest {
public static void main(String[] args) throws IOException {
String url = "https://api.themoviedb.org/";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build();
RetrofitProxy retrofitProxy = retrofit.create(RetrofitProxy.class);
Call<Object> call = retrofitProxy.test("API_KEY", 27);
Response<Object> execute = call.execute();
System.out.println(execute.raw());
System.out.println(execute.isSuccessful());
System.out.println(execute.body());
}
}
I'm adding some auth logic into cloud api gateway. I've added GatewayFilter:
import java.util.List;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.HttpStatus;
import org.springframework.util.CollectionUtils;
import org.springframework.util.PatternMatchUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
public class AuthorizationFilter implements GatewayFilter {
#Override
public Mono<Void> filter(
ServerWebExchange exchange, GatewayFilterChain chain) {
List<String> authorization = exchange.getRequest().getHeaders().get("Authorization");
if (CollectionUtils.isEmpty(authorization) &&
!PatternMatchUtils.simpleMatch(URL_WITHOUT_AUTH, exchange.getRequest().getURI().toString())) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
//Add some custom data in body of the response
return exchange.getResponse().setComplete();
}
String token = authorization.get(0).split(" ")[1];
// token validation
return chain.filter(exchange);
}
}
but I can't add some data into the body of response. Can you please help me to find out how it works and how I can customize that?
P.S.
I'm trying to add some data in response using flux but it doesn't work:
DataBuffer b = exchange.getResponse().bufferFactory().allocateBuffer(256);
b.write("12345".getBytes());
return exchange.getResponse().writeWith(s -> Flux.just(b));
What I'm doing wrong?
After some help from spring guys, I was able to make it work. So instead of writing directly to response I had to throw my custom exception and handle it properly:
#Bean
public ErrorWebExceptionHandler myExceptionHandler() {
return new MyWebExceptionHandler();
}
public class MyWebExceptionHandler implements ErrorWebExceptionHandler {
#Override
public Mono<Void> handle(
ServerWebExchange exchange, Throwable ex) {
byte[] bytes = "Some text".getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
return exchange.getResponse().writeWith(Flux.just(buffer));
}
}
Here's a working solution
import java.util.List;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public class AuthorizationFilter implements GatewayFilter {
#Override
public Mono<Void> filter(
ServerWebExchange exchange, GatewayFilterChain chain) {
if (isAuthorizationTokenValid(exchange.getRequest().getHeaders().get("Authorization")))
return chain.filter(exchange);
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
//Add some custom data in body of the response,
//Returning "Unauthorized" in the body here
return exchange.getResponse().writeWith(Flux.just(new DefaultDataBufferFactory().wrap("Unauthorized".getBytes())));
}
private boolean isAuthorizationTokenValid(List<String> authorizationTokens){
//Your logic here
return true;
}
}
You should use ServerHttpResponseDecorator to modify the response.
Your code should be like:
import java.util.List;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.HttpStatus;
import org.springframework.util.CollectionUtils;
import org.springframework.util.PatternMatchUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
public class AuthorizationFilter implements GatewayFilter {
#Override
public Mono<Void> filter(
ServerWebExchange exchange, GatewayFilterChain chain) {
List<String> authorization = exchange.getRequest().getHeaders().get("Authorization");
if (CollectionUtils.isEmpty(authorization) &&
!PatternMatchUtils.simpleMatch(URL_WITHOUT_AUTH, exchange.getRequest().getURI().toString())) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
ServerHttpResponse originalResponse = exchange.getResponse();
DataBufferFactory bufferFactory = originalResponse.bufferFactory();
ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
#Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
if (body instanceof Flux) {
Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
return super.writeWith(fluxBody.map(dataBuffer -> {
// probably should reuse buffers
byte[] content = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(content);
byte[] uppedContent = new String(content, Charset.forName("UTF-8")).toUpperCase().getBytes();
return bufferFactory.wrap(uppedContent);
}));
}
return super.writeWith(body); // if body is not a flux. never got there.
}
};
return chain.filter(exchange.mutate().response(decoratedResponse).build()); // replace response with decorator
}
String token = authorization.get(0).split(" ")[1];
// token validation
return chain.filter(exchange);
}
}
You can find a complete example here.
I know that with application.yml I can modify the url that call a microservice but my doubt is how can I implement zuul with hystrix circuit braker?, I have a class that extends ZuulFilter and in my run method I'm trying to execute the hystrixCommand like this:
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
HystrixCommand<String> hystrixCommand = new HystrixCommand<String>(HystrixCommandGroupKey.Factory.asKey(request.getRequestURL().toString())) {
#Override
protected String run() throws Exception {
RestTemplate restTemplate = new RestTemplate();
String responseBody = restTemplate.getForObject(request.getRequestURL().toString(), String.class);
return responseBody;
}
#Override
protected String getFallback() {
return "No response from server";
}
};
String response = hystrixCommand.execute();
RequestContext.getCurrentContext().setResponseBody(response);
return null;
}
But how can I tell hystrixCommand to use the getFallback method if the actual URL failed?, I thought to call the same URL but I think if I do that it will do an infinite cycle or am I not understanding?
Thanks in advance.
UPDATE
This is my whole filter class
package com.filter;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URI;
import javax.servlet.http.HttpServletRequest;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.springframework.web.client.RestTemplate;
public class ZuulHttpFilter extends ZuulFilter{
#Override
public String filterType() {
return "pre";
}
#Override
public int filterOrder() {
return 10000;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
HystrixCommand<String> hystrixCommand = new HystrixCommand<String>(HystrixCommandGroupKey.Factory.asKey(request.getRequestURL().toString())) {
#Override
protected String run() throws Exception {
RestTemplate restTemplate = new RestTemplate();
String responseBody = restTemplate.getForObject(request.getRequestURL().toString(), String.class);
return responseBody;
}
#Override
protected String getFallback() {
return "No response from server";
}
};
String response = hystrixCommand.execute();
RequestContext.getCurrentContext().setResponseBody(response);
return null;
}
}
Did you see this question? In fact, the Hystrix javadoc says that it is supposed to execute the fallback automatically:
Returns: R Result of run() execution or a fallback from getFallback()
if the command fails for any reason.
I have restyGWT+GXT project, that send request to server project (Spring Boot), so, my restyGWT+GXT part:
buiid.gradle:
...
compile 'org.fusesource.restygwt:restygwt:2.0.3'
compile 'javax.ws.rs:jsr311-api:1.1.1'
my rest service in restyGWT+GXT part:
import org.fusesource.restygwt.client.MethodCallback;
import org.fusesource.restygwt.client.RestService;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.List;
public interface LoadHelloService extends RestService {
#GET
#Path("/rest/loadHelloService")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public void loadHelloService(MethodCallback<List<Hello>> callback);
}
bean Hello.java:
public class Hello {
private final String id;
private final String name;
#JsonCreator
public Hello(#JsonProperty("id") String id, #JsonProperty("name") String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
}
in MainMenuPage (implements IsWidget):
on click menuButton1 send request to server project (Spring Boot):
#UiHandler("menuButton1")
void selectOnMenu1(SelectEvent event) {
...
restServerLoader.loadHelloListFromServer();
}
so, RestServerLoader class with method loadHelloListFromServer:
import com.google.gwt.core.client.GWT;
import com.sencha.gxt.widget.core.client.box.MessageBox;
import org.fusesource.restygwt.client.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class RestServerLoader {
public void loadHelloListFromServer() {
String pageBaseUrl = "http://127.0.0.1:8080/";
Defaults.setServiceRoot(pageBaseUrl);
Map<String, String> headers = new HashMap<>();
headers.put("Access-Control-Allow-Methods", "GET");
Resource resource = new Resource(Defaults.getServiceRoot(), headers);
LoadHelloService service = GWT.create(LoadHelloService.class);
((RestServiceProxy)service).setResource(resource);
service.loadHelloService(new MethodCallback<List<Hello>>() {
public void onSuccess(Method method, List<Hello> response) {
MessageBox messageBox = new MessageBox("response (list) = " + response.toString());
messageBox.show();
//code your stuff here
}
public void onFailure(Method method, Throwable exception) {
MessageBox messageBox = new MessageBox("exception = " + exception);
messageBox.show();
//code your stuff here
}
});
}
}
So, and when I send request loadHelloService I have: org.fusesource.restygwt.client.FailedStatusCodeException: status code 0.
:(((((((
my server part (Spring Boot) rest:
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.ArrayList;
import java.util.List;
#Path("/")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public class HelloResource {
#Context
private HttpServletResponse response;
#OPTIONS
#Path("loadHelloService") //The response for the preflight request made implicitly by the bowser
public Response loadHelloPreflight() {
Response response = Response.ok()
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "POST, GET, UPDATE, OPTIONS")
.header("Access-Control-Allow-Headers", "*")
.header("Access-Control-Max-Age", "18000").build();
return response;
}
#GET
#Path("loadHelloService")
public List<Hello> loadHelloList() {
response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Allow-Methods", "POST, GET, UPDATE, OPTIONS");
response.addHeader("Access-Control-Allow-Headers", "*");
List<Hello> list = new ArrayList<>();
list.add(new Hello("1", "ronan"));
list.add(new Hello("2", "john"));
return list;
}
}
so, When I send request, I input method loadHelloPreflight, but when send request to loadHelloList I have: org.fusesource.restygwt.client.FailedStatusCodeException: status code 0., Why??? :((, When I send request to server part (Spring Boot) from browser Postman Client - all good!, I get list of hellos, but I want do it from restyGWT+GXT part :((( Help me, please.
Did you forget some cors headers like below ?
.header("Access-Control-Allow-Headers", "x-http-method-override");