Retrofit 2 query (client_id) and parameter (song title) - interface

How to write a retrofit interface that accepts a key (String) and then "&q=" and my SONG (String)? That's what I did:
public interface API {
#GET("/tracks")
Call<List<Song>> getSongs(#Query("client_id") String id,
#Path("query") String query);
}
and in the activity:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Constants.BASE_URL)
.build();
API service = retrofit.create(API.class);
Call<List<Song>> call = service.getSongs("nofx");
call.enqueue(new Callback<List<Song>>() {
#Override
public void onResponse(Call<List<Song>> call, Response<List<Song>> response) {
List<Song> songList = response.body();
}
#Override
public void onFailure(Call<List<Song>> call, Throwable t) {
}
});
URI:
http://api.soundcloud.com/tracks?client_id=MY_KEY&q=SONG

The client_id and q params in the URL that you posted are both path params. So your interface method should look something like this:
public interface API {
#GET("tracks")
Call<List<Song>> getSongs(#Path("client_id") String id,
#Path("q") String query);
}
I also recommend moving the prepended forward slash on "/tracks" to the end of your Constants.BASE_URL so that you do not have to add the forward slash to all of your API methods.

Don't forget to add
<uses-permission android:name="android.permission.INTERNET"/>
into manifest file.

Related

Micronaut: Test POST request

In my Micronaut app I have a simple REST controller:
public class Response {
private String code;
public Response(String code) {
this.code = code;
}
}
#Controller("/api/test")
public class TestController {
#Post("/")
public Response index() {
return new Response("OK");
}
}
How can I tests this edpoint? I tried using
#MicronautTest
public class TestControllerTest {
#Inject
EmbeddedServer server;
#Inject
#Client("/")
HttpClient client;
#Test
void testResponse() {
String response = client.toBlocking()
.retrieve(HttpRequest.POST("/api/test/")); // FIXME `HttpRequest.POST` requires body
assertEquals("{\"code\": \"OK\"}", response);
}
but HttpRequest.POST requires an additional body argument to be specified. In my case there is no body to be sent. (In the real code it is a request to initialize a new object and thus it has to be POST).
Usually, when you implement a POST action, you expect that there is a body sent with the request. In your example, you don't accept any POST body, but you still need to pass anything in the unit test.
You can instantiate the HttpRequest object in the following way:
HttpRequest.POST("/api/test/", "");
You can't pass null, it has to be some non-null value (like an empty string.)

How to make the #RestController do not response data as restful? [duplicate]

I have a REST endpoint implemented with Spring MVC #RestController. Sometime, depends on input parameters in my controller I need to send http redirect on client.
Is it possible with Spring MVC #RestController and if so, could you please show an example ?
Add an HttpServletResponse parameter to your Handler Method then call response.sendRedirect("some-url");
Something like:
#RestController
public class FooController {
#RequestMapping("/foo")
void handleFoo(HttpServletResponse response) throws IOException {
response.sendRedirect("some-url");
}
}
To avoid any direct dependency on HttpServletRequest or HttpServletResponse I suggest a "pure Spring" implementation returning a ResponseEntity like this:
HttpHeaders headers = new HttpHeaders();
headers.setLocation(URI.create(newUrl));
return new ResponseEntity<>(headers, HttpStatus.MOVED_PERMANENTLY);
If your method always returns a redirect, use ResponseEntity<Void>, otherwise whatever is returned normally as generic type.
Came across this question and was surprised that no-one mentioned RedirectView. I have just tested it, and you can solve this in a clean 100% spring way with:
#RestController
public class FooController {
#RequestMapping("/foo")
public RedirectView handleFoo() {
return new RedirectView("some-url");
}
}
redirect means http code 302, which means Found in springMVC.
Here is an util method, which could be placed in some kind of BaseController:
protected ResponseEntity found(HttpServletResponse response, String url) throws IOException { // 302, found, redirect,
response.sendRedirect(url);
return null;
}
But sometimes might want to return http code 301 instead, which means moved permanently.
In that case, here is the util method:
protected ResponseEntity movedPermanently(HttpServletResponse response, String url) { // 301, moved permanently,
return ResponseEntity.status(HttpStatus.MOVED_PERMANENTLY).header(HttpHeaders.LOCATION, url).build();
}
As the redirections are usually needed in a not-straightforward path, I think throwing an exception and handling it later is my favourite solution.
Using a ControllerAdvice
#ControllerAdvice
public class RestResponseEntityExceptionHandler
extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = {
NotLoggedInException.class
})
protected ResponseEntity<Object> handleNotLoggedIn(
final NotLoggedInException ex, final WebRequest request
) {
final String bodyOfResponse = ex.getMessage();
final HttpHeaders headers = new HttpHeaders();
headers.add("Location", ex.getRedirectUri());
return handleExceptionInternal(
ex, bodyOfResponse,
headers, HttpStatus.FOUND, request
);
}
}
The exception class in my case:
#Getter
public class NotLoggedInException extends RuntimeException {
private static final long serialVersionUID = -4900004519786666447L;
String redirectUri;
public NotLoggedInException(final String message, final String uri) {
super(message);
redirectUri = uri;
}
}
And I trigger it like this:
if (null == remoteUser)
throw new NotLoggedInException("please log in", LOGIN_URL);
if you #RestController returns an String you can use something like this
return "redirect:/other/controller/";
and this kind of redirect is only for GET request, if you want to use other type of request use HttpServletResponse

How to POST InputStream as the body of a request in Retrofit?

I'm attempting to do a POST with the body being an InputStream with something like this:
#POST("/build")
#Headers("Content-Type: application/tar")
Response build(#Query("t") String tag,
#Query("q") boolean quiet,
#Query("nocache") boolean nocache,
#Body TypedInput inputStream);
In this case the InputStream is from a compressed tar file.
What's the proper way to POST an InputStream?
You can upload inputStream using Multipart.
#Multipart
#POST("pictures")
suspend fun uploadPicture(
#Part part: MultipartBody.Part
): NetworkPicture
Then in perhaps your repository class:
suspend fun upload(inputStream: InputStream) {
val part = MultipartBody.Part.createFormData(
"pic", "myPic", RequestBody.create(
MediaType.parse("image/*"),
inputStream.readBytes()
)
)
uploadPicture(part)
}
If you want to find out how to get an image Uri, check this answer: https://stackoverflow.com/a/61592000/10030693
TypedInput is a wrapper around an InputStream that has metadata such as length and content type which is used in making the request. All you need to do is provide a class that implements TypedInput which passed your input stream.
class TarFileInput implements TypedInput {
#Override public InputStream in() {
return /*your input stream here*/;
}
// other methods...
}
Be sure you pass the appropriate return values for length() and mimeType() based on the type of file from which you are streaming content.
You can also optionally pass it as an anonymous implementation when you are calling your build method.
The only solution I came up with here was to use the TypeFile class:
TypedFile tarTypeFile = new TypedFile("application/tar", myFile);
and the interface (without explicitly setting the Content-Type header this time):
#POST("/build")
Response build(#Query("t") String tag,
#Query("q") boolean quiet,
#Query("nocache") boolean nocache,
#Body TypedInput inputStream);
Using my own implementation of TypedInput resulted in a vague EOF exception even while I provided the length().
public class TarArchive implements TypedInput {
private File file;
public TarArchive(File file) {
this.file = file;
}
public String mimeType() {
return "application/tar";
}
public long length() {
return this.file.length();
}
public InputStream in() throws IOException {
return new FileInputStream(this.file);
}
}
Also, while troubleshooting this issue I tried using the latest Apache Http client instead of OkHttp which resulted in a "Content-Length header already present" error even though I wasn't explicitly setting that header.
According to the Multipart section of http://square.github.io/retrofit/ you'll want to use TypedOutput instead of TypedInput. Following their examples for multipart uploads worked fine for me once I had implemented a TypedOutput class.
My solution was to implement TypedOutput
public class TypedStream implements TypedOutput{
private Uri uri;
public TypedStream(Uri uri){
this.uri = uri;
}
#Override
public String fileName() {
return null;
}
#Override
public String mimeType() {
return getContentResolver().getType(uri);
}
#Override
public long length() {
return -1;
}
#Override
public void writeTo(OutputStream out) throws IOException {
Utils.copyStream(getContentResolver().openInputStream(uri), out);
}
}

Pass a parameter to REST web service via URL

I'm creating a small REST web service using Netbeans. This is my code:
private UriInfo context;
private String name;
public GenericResource() {
}
#GET
#Produces("text/html")
public String getHtml() {
//TODO return proper representation object
return "Hello "+ name;
}
#PUT
#Consumes("text/html")
public void putHtml(String name) {
this.name = name;
}
I'm calling the get method ok since when I call http://localhost:8080/RestWebApp/resources/greeting I get "Hello null" but I'm trying to pass a parameter using http://localhost:8080/RestWebApp/resources/greeting?name=Krt_Malta but the PUT method is not being called... Is this the correct way to pass a parameter or am I missing something?
I'm a newbie to Rest bdw, so sry if it's a simple question.
Thanks! :)
Krt_Malta
The second URL is a plain GET request. To pass data to a PUT request you have to pass it using a form. The URL is reserved for GET as far as I know.
If you build the HTTP-header yourself, you must use POST instead of GET:
GET /RestWebApp/resources/greeting?name=Krt_Malta HTTP/1.0
versus
POST /RestWebApp/resources/greeting?name=Krt_Malta HTTP/1.0
If you use a HTML-form, you must set the method-attribute to "PUT":
<form action="/RestWebApp/resources/greeting" method="PUT">
For JAX-RS to mactch a method annotated with #PUT, you need to submit a PUT request. Normal browsers don't do this but cURL or a HTTP client library can be used.
To map a query parameter to a method argument, JAX-RS provides the #QueryParam annotation.
public void putWithQueryParam(#QueryParam("name") String name) {
// do something
}
You can set:
#PUT
#path{/putHtm}
#Consumes("text/html")
public void putHtml(String name) {
this.name = name;
}
and if you use something like google`s Volley library you can do.
GsonRequest<String> asdf = new GsonRequest<String>(ConnectionProperties.happyhourURL + "/putHtm", String.class, yourString!!, true,
new Response.Listener<Chain>() {
#Override
public void onResponse(Chain response) {
}
}, new CustomErrorListener(this));
MyApplication.getInstance().addToRequestQueue(asdf);
and GsonRequest will look like:
public GsonRequest(String url, Class<T> _clazz, T object, boolean needLogin, Listener<T> successListener, Response.ErrorListener errorlistener) {
super(Method.PUT, url, errorlistener);
_headers = new HashMap<String, String>();
this._clazz = _clazz;
this.successListener = successListener;
this.needsLogin = needLogin;
_object = object;
setTimeout();
}

How to translate,use JSON in GWT?

I'm new in gwt. and need to know how to use JSON in gwt so i try this simple data loader but i'm still confuse.
I create a project named 'tesdb3' in eclipse. I create the PHP side to access the database, and made the output as JSON.. I create the userdata.php in folder war. then I compile tesdb3 project. Folder tesdb3 and the userdata.php in war moved in local server(I use WAMP). I put the PHP in folder tesdb3. This is the result from my localhost/phpmyadmin/tesdb3/userdata.php
[{"kode":"002","nama":"bambang gentolet"}{"kode":"012","nama":"Algiz"}]
From that result I think the PHP side was working good.Then I create UserData.java as JSNI overlay like this:
package com.tesdb3.client;
import com.google.gwt.core.client.JavaScriptObject;
class UserData extends JavaScriptObject{
protected UserData() {}
public final native String getKode() /*-{ return this.kode; }-*/;
public final native String getNama() /*-{ return this.nama; }-*/;
public final String getFullData() {
return getKode() + ":" + getNama();
}
}
Then Finally in the tesdb3.java:
public class Tesdb3 implements EntryPoint {
String url= "http://localhost/phpmyadmin/tesdb3/datauser.php";
private native JsArray<UserData> getuserdata(String json)
/*-{
return eval(json);
}-*/;
public void LoadData() throws RequestException{
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, URL.encode(url));
builder.sendRequest(null, new RequestCallback(){
#Override
public void onError(Request request, Throwable exception) {
Window.alert("error " + exception);
}
public void onResponseReceived(Request request,
Response response) {
//1
//data(getuserdata(response.getText()));
//2
JsArray<UserData> uda = JsonUtils.unsafeEval(response.getText())
data(uda);
}
});
}
public void data(JsArray<UserData> data){
for (int i = 0; i < data.length(); i++) {
String lkode =data.get(i).getKode();
String lname =data.get(i).getNama();
Label l = new Label(lkode+" "+lname);
tb.setWidget(i, 0, l);
}
RootPanel.get().add(new HTML("my data"));
RootPanel.get().add(tb);
}
public void onModuleLoad() {
try {
LoadData();
} catch (RequestException e) {
e.printStackTrace();
}
}
}
The result from both method i use in the onResponseReceived is the same. Just showing string "my data". but the method 2 create eror like this:
14:41:59.875 [ERROR] [tesdb3] Uncaught exception escaped
com.google.gwt.core.client.JavaScriptException: (SyntaxError): syntax error
Did I miss use the 2nd method? Why method 1 didn't have any eror but can't show the data?
The problem is that your JSON has incorrect syntax, you are missing a comma after the first item of the table, it should be (whitespace added for readability):
[
{
"kode": "002",
"nama": "bambang gentolet"
},
{
"kode": "012",
"nama": "Algiz"
}
]
Since JSON is a part of JavaScript this might be the syntax error exception you are getting.
PS: I'd recommend using some PHP framework to create JSON for you (Zend Framework is my usual choice). Also, JSON validators like JSONLint are very useful for debugging JSON.
It looks like a typo in your code, which brings me to naming conventions: for variables and methods use camel case, starting with a lower case character. Thus UserData UD should be UserData ud.
In your getuserdata method (which should be getUserData) you use the parameter name Json with capital J and in the native code json with the lower j. This explains the error.
Regarding the getUserData method. There is a GWT method: JsonUtils.unsafeEval(json) which you should use.
Furthermore, the code in the onResponseReceived seems incomplete, it might not be of importance as it might be incorrectly be put in this example, but just to be complete, here is what it should look like:
JsArray<UserData> uda = JsonUtils.unsafeEval(response.getText());
for (int i = 0; i < uda.length(); i++) {
UserData ud = uda.get(i);
String lKode = ud.getKode();
String lName = ud.getNama();
Label l = new Label(lKode + " " +lName);
RootPanel.get().add(l);
}