I'm trying to call a local rest webservice to test a simple login function
I have tried with a Get function and it works fine now I'm trying with Post function and I have no idea how to pass parameters to my request
import { HttpClient,HttpParams } from '#angular/common/http';
import { Injectable } from '#angular/core';
import {Storage} from "#ionic/storage";
// import { HttpParams } from '#angular/http';
/*
Generated class for the RestProvider provider.
See https://angular.io/guide/dependency-injection for more info on providers
and Angular DI.
*/
#Injectable()
export class RestProvider {
apiUrl = 'http://localhost:8080/CrunchifyTutorials/api/crunchifyService';
user : any;
constructor(public http: HttpClient, public storage: Storage) {
console.log('Hello RestProvider Provider');
//console.log(this.storage.get('user'));
}
// getUsers() {
// return new Promise(resolve => {
// this.http.get(this.apiUrl).subscribe(data => {
// resolve(data);
// this.storage.set('user', data);
// }, err => {
// console.log(err);
// });
// });
// }
//--------------------------------------------
getUsers() {
// let data = new URLSearchParams();
// data.append('user', 'khaled');
// data.append('pass', 'khaled');
//data =Object.assign(urlSearchParams, {search: "person"});
const data = new HttpParams()
.set('user', 'khaled').set('pass', 'khaled');
this.http
.post(this.apiUrl, data, {headers:{'Content-Type': 'application/json'}})
.subscribe(data => {
alert('ok');
}, error => {
console.log(error.json());
});
}
}
And the Java part
#POST
#Path("/crunchifyService")
#Produces("application/json")
#Consumes("application/json")
public Modele add (String user, String pass) throws ClassNotFoundException, SQLException{
Auth_Ctrl auth = new Auth_Ctrl();
int result =auth.connect(user, pass);
return auth.info(result);
}
the problem is that when I debug the server part all my parameters are null and there is no error in the client part
You are using #QueryParam which uses get parameters. These parameters should be included in the url. The http.post method encodes the data as application/json inside the body of the request.
You should change either one of these to match the other.
My advise would be to change the java here to consume applications/json due to the security concerns of putting a password in the url
For whom it might interest i modified my code like that
i added a class "Log" which contains two fields user and pass in my webservice following thislink
and then i modified my method like that
#POST
#Path("/crunchifyService")
#Produces("application/json")
#Consumes("application/json")
public Modele auth (Log user) throws ClassNotFoundException, SQLException{
Auth_Ctrl auth = new Auth_Ctrl();
int result =auth.connect(user.user, user.pass);
return auth.info(result);
}
and i noticed that i need to add jackson-mapper-asl to my classpath
and after that everything works fine
Related
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 want to set the BaseUrl in Retrofit to change dynamically between stage and live because i have an app that has stage and live version. So i made a spinner and the user can select either he wants. But the problem is that after the user select the flavor he wants and then wants to change again it doens't work because the baseUrl is not changing like it should be.
I have this class where is defined the API_URL but it's not working :
#Singleton
class SingleUrlApi {
companion object{
public var API_URL_STAGE = BuildConfig.STAGE
}
}
and then i have another function that uses this API_URL_STAGE
override fun getUrl(shopUrl: ShopUrl, vararg args: String): String {
return when (shopUrl) {
ShopUrl.API_BASE -> if (SingleUrlApi.API_URL_STAGE) {
context.localizedContext(localeManager.getCurrentLocale()).getString(R.string.base_url_stage)
} else {
context.localizedContext(localeManager.getCurrentLocale()).getString(R.string.base_url_live)
}
ShopUrl.WEB_BASE -> if (SingleUrlApi.API_URL_STAGE) {
context.localizedContext(localeManager.getCurrentLocale()).getString(R.string.base_web_url_stage)
} else {
context.localizedContext(localeManager.getCurrentLocale()).getString(R.string.base_web_url_live)
}
You can use OkHttp along with Retrofit.
Then, you can use an OkHttpInterceptor to change the URL of the request
public final class HostSelectionInterceptor implements Interceptor {
#Override public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
String host = //logic to fetch the new URL
if (host != null) {
HttpUrl newUrl = request.url().newBuilder()
.host(host)
.build();
request = request.newBuilder()
.url(newUrl)
.build();
}
return chain.proceed(request);
}
}
An easy and effective way is to use this library: RetrofitUrlManager
How can I configure Lagom framework to work with CORS request (method request 'options').
I have enabled CORS in lagom for one of my projects in this way.
Define a method in service class to handle OPTIONS calls.
ServiceCall<NotUsed, Done> options();
Implement the method in the service-impl class.
#Override
public ServiceCall<NotUsed, Done> options() {
return request -> CompletableFuture.completedFuture(Done.getInstance());
}
Define the options call in the descriptor. As an example, assume that the actual call is,
GET /api/v0.1/user
The service descriptor should look like this:
#Override
default Descriptor descriptor() {
// #formatter:off
return named("notification").withCalls(
restCall(Method.GET, "/api/v0.1/user", this::getUser),
restCall(Method.OPTIONS, "/api/v0.1/user", this::options)
).withAutoAcl(true).withHeaderFilter(new CORSHeaderFilter());
// #formatter:on
}
Note that it has a header filter attached using,
.withHeaderFilter(new CORSHeaderFilter())
CORSHeaderFilter Class should look like this.
import com.lightbend.lagom.javadsl.api.transport.HeaderFilter;
import com.lightbend.lagom.javadsl.api.transport.Method;
import com.lightbend.lagom.javadsl.api.transport.RequestHeader;
import com.lightbend.lagom.javadsl.api.transport.ResponseHeader;
public class CORSHeaderFilter implements HeaderFilter {
#Override
public RequestHeader transformClientRequest(RequestHeader request) {
return request;
}
#Override
public RequestHeader transformServerRequest(RequestHeader request) {
return request;
}
#Override
public ResponseHeader transformServerResponse(ResponseHeader response, RequestHeader request) {
ResponseHeader modifiedResponse = response.withHeader("Access-Control-Allow-Origin", "*");
if (Method.OPTIONS.equals(request.method())) {
modifiedResponse = modifiedResponse.withStatus(204).withHeader("Access-Control-Allow-Headers",
"Authorization,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With" +
",If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range").
withHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PATCH").
withHeader("Access-Control-Max-Age", "1728000");
}
return modifiedResponse;
}
#Override
public ResponseHeader transformClientResponse(ResponseHeader response, RequestHeader request) {
ResponseHeader modifiedResponse = response.withHeader("Access-Control-Allow-Origin", "*");
if (Method.OPTIONS.equals(request.method())) {
modifiedResponse = modifiedResponse.withStatus(204).withHeader("Access-Control-Allow-Headers",
"Authorization,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With" +
",If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range").
withHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PATCH").
withHeader("Access-Control-Max-Age", "1728000");
}
return modifiedResponse;
}
}
Whenever you add a new endpoint, make sure to add the OPTIONS version of it as well.
To allow a Lagom service written in Java to work with CORS, you'll need to implement a CORS filter per Play:
package example.service.impl
import play.filters.cors.CORSFilter;
import play.http.DefaultHttpFilters;
import javax.inject.Inject;
// See https://playframework.com/documentation/2.5.x/CorsFilter
public class MyCORSFilter extends DefaultHttpFilters {
#Inject
public MyCORSFilter(CORSFilter corsFilter) {
super(corsFilter);
}
}
and then in your application.conf, you'll need to add the filter:
play.http.filters = "example.service.impl.MyCORSFilter"
// To properly setup the CORSFilter, please refer to https://playframework.com/documentation/2.5.x/CorsFilter
// This example is only meant to show what's required for Lagom to use CORS.
play.filters.cors {
// review the values of all these settings to fulfill your needs. These values are not meant for production.
pathPrefixes = ["/api"]
allowedOrigins = null
allowedHttpMethods = null
allowedHttpHeaders = null
exposedHeaders = []
supportsCredentials = false
preflightMaxAge = 6 hour
}
For more info, see the example CORS service and the Play docs.
I have a controller for AngularJS Framework. I use a Http Post Request to send an array to a server. How to get this array in a java method?
This is my controller
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, $http) {
var indici = new Array();
indici.push(1);
indici.push(2);
$http.post("http://localhost:8080/SistemiDistribuiti/rest/Point/Trovati", indici, {
headers: { 'Content-Type': 'application/json' }
}).success(function(receivedData, status) {
$scope.someData = receivedData;
});
And this is my java class but i don't know how to get my array.
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.POST;
import javax.ws.rs.Consumes;
#Path("Point")
public class PointService {
#POST
#Path("Trovati")
#Consumes(MediaType.APPLICATION_JSON)
public void RetrieveData() {
//how print my array?
}
You have to use #GET as below:
#GET
#Path("/blabla")
public Response receiveListOfStrings(#QueryParam("list") final List<String> list){
log.info("receieved list of size="+list.size());
return Response.ok().build();
}
And request:
GET http://example.com/services/echo?list=balbla&list=asdasd&list=BYE
Post doesn't support this. Or you can use #PUT with complex type.
Put example:
#PUT
public Response putExample(MyObject obj) {
log.info("receieved list of size="+obj.getList().size());
return Response.ok().build();
}
In this put example you can see I used a custom MyObject Here's its codes:
public class MyObject{
private List<String> list = new ArrayList<String>();
public List<String> getList(){
return list;
}
public void setList( List<String> list ){
this.list = list;
}
#Override
public String toString(){
return "MyObject [list=" + list + "]";
}
}
As you can see there's a list property in your MyObject class. So you can print anything by calling getList as my example above.
I agree with #lex82, you don't send the payload within your request. See the documentation for $http.post: https://docs.angularjs.org/api/ng/service/$http#post. Be sure to understand what promises are and how they are used within the HTTP support of Angular.
Here is a sample:
var dataToSend = {
// some JSON data
};
$http.post("/some-url", dataToSend, {
headers: { 'Content-Type': 'application/json' }
}).success(function(receivedData, status) {
$scope.someData = receivedData;
});
That said, you say that you don't receive the data on the server. The first to do is to check your raw request. This can be done within Chrome using the Chrome Developer Tools (CRTL + SHIFT + i). This will give you access to a Network tab containing every request. Click on the request corresponding to your AJAX request and check if there is a payload in your request. If so, it's a problem on your server side within your JAXRS application.
Do you some exceptions within your JAXRS application?
Hope it helps you,
Thierry
Apache has an http library that can be used to make various http requests.
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.2</version>
</dependency>
You will also need a library that can be used to transform your json objects into Java object. Google has created a library called gson that I will use in my example.
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.2.4</version>
</dependency>
You are also going to have to create a Java object that represents your data. This will be used to map your json object to a java object or "pojo". I'm not sure what your JSON objects look like, but I'm going to use a generic example called Response.
public class Response
{
private List<Example> examples;
private double total;
private String someString;
public QuoteResponse()
{
super();
}
public List<Examples> getExamples() {
return examples;
}
public void setExamples(List<Examples> examples)
{
this.examples = examples;
}
public double getTotal() {
return total;
}
public void setTotal(double total) {
this.total = total;
}
public String getSomeString() {
return someString;
}
public void setPrint_type(String someString) {
this.someString = someString;
}
}
Your java object has to have the same number of fields with the same type and same name as your JSON object.
Next, you will have to write a function that calls your angular api. See an example below:
public Response getJsonData()
{
params = new Params();
String url = "https://www.yoururl.com/controller/function_you_want;
Response response = null;
HttpGet httpGet = new HttpGet(url);
try
{
response = httpClient.execute(httpGet);
//check to make sure that everything is ok
if(response.getStatusLine().getStatusCode() == 200)
{
entity = response.getEntity();
jsonResponse = EntityUtils.toString(entity);
JsonNode root = mapper.readTree(jsonResponse).get("result");
response = gson.fromJson(root.toString(),Response.class);
}
}
catch (IOException e)
{
e.printStackTrace();
}
return response;
}
That's about it. Let me know if you have any questions.
In this example, the URL for a service has the form /projection/projectionId:
#Stateless
#Path("projection")
public class ProjectionManager {
#Inject
private ProjectionDAO projectionDAO;
#Inject
private UserContext userContext;
#GET
#Path("{projectionId}")
#Produces("application/json")
public String places(#PathParam("projectionId") String projectionId) {
return projectionDAO.findById(Long.parseLong(projectionId)).getPlaces().toString();
}}
How can I pass two (or more) query parameters to access the service using this code:
#PUT
#Path("/buy")
public Response buyTicket(#QueryParam("projectionId") String projectionId, #QueryParam("place") String place) {
Projection projection = projectionDAO.findById(Long.parseLong(projectionId));
if(projection != null) {
projectionDAO.buyTicket(projection, userContext.getCurrentUser(), Integer.parseInt(place));
}
return Response.noContent().build();
}
/buy?projectionId=value1&place=value2
Take a look at https://en.wikipedia.org/wiki/Query_string for further information. And since it is HTTP PUT you cannot simply open that URL in your browser, you can write some simple REST client or use browser extension like Postman in Chrome.
Query parameter is the thing after the ? in the URI, while path parameter is the parametrer before the ? in the URI.
If you need two inputs to your method, you can go with any combination of query param and path param => four combinations
It's a good convention that path params should denote some kind of identity of the resource, because it's part of it's address, while query params more some form/shape/filtering of the response.
In your case, I'd encode both params as path parameters, so the code would look like this:
#PUT
#Path("/buy/{projectionId}/place/{place}")
public Response buyTicket(#PathParam("projectionId") String projectionId, #PathParam("place") String place){
Projection projection = projectionDAO.findById(Long.parseLong(projectionId));
if(projection != null){
projectionDAO.buyTicket(projection, userContext.getCurrentUser(), Integer.parseInt(place));
}
return Response.noContent().build();
}
The URL would look like:
${host}/buy/1337/place/42
Thanks for your input guys, I have fixed it.
It looks like I had to add the path parameter to the additional parameters, and pass additional parameters on the request, instead of the path parameter. Code looks as below,
it('should get a customer, searches with a customer name', (done) => {
var pathParams = {};
var body = {};
var additionalParams = {
queryParams: {
name: 'Ominathi'
}
};
//apigClient.invokeApi(pathParams, '/customer', 'GET', queryParams, body)
apigClient.invokeApi(pathParams, '/customer', 'GET', additionalParams, body)
.then(response => {
expect(response.status).toBe(200);
done();
})
.catch(err => {
fail(err);
done();
});
});
Thanks.
Ref: https://www.npmjs.com/package/aws-api-gateway-client