I'm using Retrofit 1.9.0. I have the following interface:
import retrofit.http.PUT;
public interface ShovelsApi {
#PUT("/api/parameters/shovel/{vhost}/{name}")
RetrievedShovel putShovel(#Path("vhost") final String vhost, #Path("name") final String name, #Body final Component<Shovel> shovel);
}
However, when this fails, all I get back is a null RetrievedShovel :
final Shovel shovel = new Shovel();
shovel.setSrcDeleteAfter(1);
shovel.setAckMode("on-confirm");
shovel.setSrcQueue("srcq");
shovel.setSrcUri("amqp://");
shovel.setDestQueue("destq");
shovel.setDestUri("amqp://");
final RetrievedShovel retrievedShovel = shovelsApi.getApi().putShovel(
"/", "myshovel", new Component<>("shovel", "myshovel", shovel));
System.out.println(retrievedShovel); // null
How do I get back the HTTP response, to inspect the status code and message etc?
Thanks!
Related
I have an application A that has an endpoint which signs the response and puts the signature in a header. The header looks like:
X-Algorithm: SHA256withRSA
X-Signature: Zca8Myv4......PkH1E25hA=
When I call the application directly I see the headers.
I build application B and it's calling A via a proxy P.
Application B has an OkHttp client which sends the request and reads the response. I have a custom interceptor:
#Slf4j
public class SignatureValidatorInterceptor implements Interceptor {
private final Signature signer;
public SignatureValidatorInterceptor(final PublicKey publicKey) {
this.signer = getSigner(publicKey);
}
private static final String ALGORITHM_HEADER = "x-algorithm";
private static final String SIGNATURE_HEADER = "x-signature";
private static final String SIGNATURE_ALGORITHM = "SHA256withRSA";
/**
* This interceptor verifies the signature of the response
*/
#Override
public Response intercept(final Interceptor.Chain chain) throws IOException {
final Response response = chain.proceed(chain.request());
final Headers headers = response.headers();
log.info("Received response for url={}} with headers \n{}", response.request().url(), headers);
final byte[] responseBodyBytes = response.peekBody(1000).bytes();
final String algorithmHeader = headers.get(ALGORITHM_HEADER);
final String signatureHeader = headers.get(SIGNATURE_HEADER);
if (StringUtils.isBlank(signatureHeader) || StringUtils.isBlank(algorithmHeader)) {
throw new Exception("No signature or algorithm header on response");
}
this.verifySignature(responseBodyBytes, algorithmHeader, signatureHeader);
return response;
}
private void verifySignature(final byte[] responseBodyBytes, final String algorithmHeader, final String signatureHeader) {
//code to validate signature
}
private Signature getSigner(final PublicKey publicKey) {
//code to create signer
}
}
The debug line logs the headers it receives. I see multiple standard http headers in my logs but I'm missing my custom headers!
I have no clue why. I first thought it was a network thing. But on doing a curl from the machine of B to application A, I see the headers are there. Also I had the proxy log the headers for me, and I can see they passed.
All applications are standard spring-boot java applications. Running on linux VM's.
What am I missing?
Thanks,
Rick
I'm attempting to use Retrofit to call the GitHub API to update the contents of an existing file, but am getting 404s in my responses. For this question, I'm interested in updating this file. Here is the main code I wrote to try and achieve this:
GitHubUpdateFileRequest
public class GitHubUpdateFileRequest {
public String message = "Some commit message";
public String content = "Hello World!!";
public String sha = "shaRetrievedFromSuccessfulGETOperation";
public final Committer committer = new Committer();
private class Committer {
Author author = new Author();
private class Author {
final String name = "blakewilliams1";
final String email = "blake#blakewilliams.org";
}
}
}
**GitHubUpdateFileResponse **
public class GitHubUpdateFileResponse {
public GitHubUpdateFileResponse() {}
}
GitHubClient
public interface GitHubClient {
// Docs: https://docs.github.com/en/rest/reference/repos#get-repository-content
// WORKS FINE
#GET("/repos/blakewilliams1/blakewilliams1.github.io/contents/qr_config.json")
Call<GitHubFile> getConfigFile();
// https://docs.github.com/en/rest/reference/repos#create-or-update-file-contents
// DOES NOT WORK
#PUT("/repos/blakewilliams1/blakewilliams1.github.io/contents/qr_config.json")
Call<GitHubUpdateFileResponse> updateConfigFile(#Body GitHubUpdateFileRequest request);
}
Main Logic
// Set up the Retrofit client and add an authorization interceptor
UserAuthInterceptor interceptor =
new UserAuthInterceptor("blake#blakewilliams.org", "myActualGitHubPassword");
OkHttpClient.Builder httpClient =
new OkHttpClient.Builder().addInterceptor(interceptor);
Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit = builder.client(httpClient.build()).build();
client = retrofit.create(GitHubClient.class);
// Now make the request and process the response
GitHubUpdateFileRequest request = new GitHubUpdateFileRequest();
client.updateConfigFile(request).enqueue(new Callback<GitHubUpdateFileResponse>() {
#Override
public void onResponse(Call<GitHubUpdateFileResponse> call, Response<GitHubUpdateFileResponse> response) {
int responseCode = response.code();
// More code on successful update
}
#Override
public void onFailure(Call<GitHubUpdateFileResponse> call, Throwable t) {
Log.e("MainActivity", "Unable to update file" + t.getLocalizedMessage());
}
});
What currently happens:
Currently, the success callback is triggered, but with a response code of 404 like so:
Response{protocol=http/1.1, code=404, message=Not Found, url=https://api.github.com/repos/blakewilliams1/blakewilliams1.github.io/contents/qr_config.json}
Has anyone else encountered this? I first thought it was a problem with including '/content/' in the URL but I do the same thing for reading the file contents request and it works fine (also uses same URL just a GET instead of PUT).
For anyone interested in doing this in the future, I figured out the solution.
I needed to revise the request object structure
Rather than using an authentication interceptor, I instead added an access token to the header. Here is where you can create access tokens for Github, you only need to grant it permissions to the 'repos' options for this use case to work.
This is what my updated request object looks like:
public class GitHubUpdateFileRequest {
public String message;
public String content;
public String sha;
public final Committer committer = new Committer();
public GitHubUpdateFileRequest(String unencodedContent, String message, String sha) {
this.message = message;
this.content = Base64.getEncoder().encodeToString(unencodedContent.getBytes());
this.sha = sha;
}
private static class Committer {
final String name = "yourGithubUsername";
final String email = "email#yourEmailAddressForTheUsername.com";
}
}
Then from my code, I would just say:
GitHubUpdateFileRequest updateRequest = new GitHubUpdateFileRequest("Hello World File Contents", "This is the title of the commit", shaOfExistingFile);
For using this reqest, I updated the Retrofit client implementation like so:
// https://docs.github.com/en/rest/reference/repos#create-or-update-file-contents
#Headers({"Content-Type: application/vnd.github.v3+json"})
#PUT("/repos/yourUserName/yourRepository/subfolder/path/to/specific/file/theFile.txt")
Call<GitHubUpdateFileResponse> updateConfigFile(
#Header("Authorization") String authorization, #Body GitHubUpdateFileRequest request);
And I call that interface like this:
githubClient.updateConfigFile("token yourGeneratedGithubToken", request);
And yes, you do need the prefix "token ". You could hardcode that header into the interface, but I pass it in so that I can store it in locations outside of my version control's reach for security reasons.
I am trying to make API POST Request with WebClient in Spring Boot. But I cannot make a request with JSON body as I want and receive response as JSONObject.
JSON BODY :
{
"workspaces": [
"6eret123",
"b129078v",
"ngy66553",
"erfc1153"
]
}
Service class-
Workspaces workspace = new Workspaces(mw);
Flux<Workspaces> modifiedWorkspace = webClient.post().uri(URIDetails.MODIFIEDWORKSPACE)
.header("Authorization", bearerToken).body(Mono.just(mw), Workspaces.class).retrieve()
.bodyToFlux(Workspaces.class);
modifiedWorkspace.doOnNext(System.out::println).blockLast();
return null;
Workspaces model-
public class Workspaces {
private List<String> workspaces;
}
Main call-
Flux<ScanIDModel> modifiedWorkspaces;
final List<String> mw = new ArrayList<>();
for (Workspace w : modifiedWorkspaces) {
mw.add(w.getId());
}
modifiedWorkspaces = scanRespone(mw);
I need to send a list of JSON body mentioned as a body post request.
Please help me to do the post request
Thanks in advance
try something like this:
import org.springframework.web.reactive.function.client.WebClient;
public class Testclass{
#Autowired
private WebClient.Builder webClientBuilder;//webclient builder instance
public void testMethod(){
Class<Object> cls = Object.class;//object class of the request body
//Object obj is also the object of the request body
Object obj = webClientBuilder.build().post().uri("{{url}}")
.accept(MediaType.APPLICATION_JSON).header("Authorization", "{{authtoken}}")
.bodyValue(user).retrieve().bodyToMono(clazz).block();
}
}
hope something on it might help.
I have a method in class which does a HTTP GET call to get the response object and utilize this object further. The pesudo code is below:
public class ABC{
public method abc1(){
HttpUrl url = HttpUrl.parse("url").newBuilder()
.addPathSegment("path1")
.build();
Request request = new Request.Builder().url(url).build();
try (Response response = client.newCall(request).execute()) {
ResponseBody responseBody = response.body();
String body = responseBody.string();
//other logic
}catch (IOException e) {}
}
}
Now I am writing a unit test to test with different values in the response object (json object). This is as below:
public class ABCTest{
#Mock
private OkHttpClient mockHttpClient;
#Mock
private Call mockCall;
#Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
}
#Test
public void abc1Test(){
ResponseObjectInJson responseObjectInJson = new ResponseObjectInJson(); //this is a object from my POJO class that i create in order to be received as a response
JSONObject jsonObject = new
JSONObject(responseObjectInJson);
ResponseBody body =
ResponseBody.create(MediaType.parse("application/json"),new
Gson().toJson(jsonObject));
Response.Builder builder = new Response.Builder();
builder.code(200);
Response response = builder.body(body).build();
when(mockCall.execute()).thenReturn(response);
when(mockHttpClient.newCall(any(Request.class))).thenReturn(mockCall);
//call the abc1() method here to see the response and behaviour
}
}
The problem is, when i debug, it throws InvocationTargetException when building the response builder.body(body).build();
And shows java.lang.IllegalStateException: request == null. I understand that i need to set request in the Response.Builder because when i evaluate the expression builder.body(body) in debugger, in the result it shows headers and body, but request is null.
i.e., builder.request(//a request here)
My question is:
1. In response why the request is needed?
2. How to set this? because i am unable to mock since its final.
Thanks in advance
So i have this android application that send jsonObject for a jsonObjectRequest (I'm using volley) with POST method. The JSON file was sent but the server (this is where i'm using Spring btw) is not responding. The code pretty much like these.
Android Code:
JSONObject jsonObject = new JSONObject(data);
final JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST, url, jsonObject, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
userToken = response.getString(TAG_TOKEN);
// blablabla
Server Code:
#RestController
#RequestMapping("/auth/")
public class AuthController {
String userEmail;
String userPassword;
#RequestMapping(value = "/login", method = RequestMethod.POST)
public LoginResponse loginResponse(#RequestBody Auth auth){
userEmail = auth.getEmail();
userPassword = auth.getPassword();
// blablabla
Auth Object:
public class Auth {
private String email;
private String password;
// blablabla
It seems like the server didn't receive the JsonObject from the android client correctly. (The server is able to send JSON to android client perfectly in another case, though). I'm using spring-4.16, jackson-core, jackson-annotation, jackson-databind (2.2.2). Thanks in advance!
From the code you supplied first thing that I suspect can be is that JSONObject doesn't have same field's name with Auth on server.
Also it is possible that HttpMessageConverter which is used for mapping objects from request to object in controller is not doing his job correctly.
I would first try to just try to recieve parameters from body, without Auth object. If Spring maps it fine than HttpMessageConverter is the problem, if it does not than the usage of #RequestBody, which I can't be from code you supplied.