I am running a VertX HTTP Server. It understands requests when content type is HTML/forms, but when I try to post JSON data, it never even enters the request handler.
Is there something I need to do to make Vertx expect JSON? Is this supported?
Here is a java example. Note that the data handler which will be processing json is executed only for post request. Post a request with some json data to this and it will return with the same.
import org.vertx.java.core.Handler;
import org.vertx.java.core.buffer.Buffer;
import org.vertx.java.core.http.HttpServerRequest;
import org.vertx.java.platform.Verticle;
/**
* Simple Http server
*/
public class HttpVerticle extends Verticle {
public void start() {
vertx.createHttpServer()
.requestHandler(new Handler<HttpServerRequest>() {
#Override
public void handle(final HttpServerRequest request) {
container.logger().info(
"Got request for " + request.path());
if (request.method().equalsIgnoreCase("POST")) {
request.dataHandler(new Handler<Buffer>() {
#Override
public void handle(Buffer data) {
request.response().end("got data " + data);
}
});
} else {
request.response().end("got request");
}
}
}).listen(8080);
container.logger().info("HttpVerticle started");
}
}
If you can, Check out the latest Vertx-web They provide a really neat way of handling various requests formats ( multipart-data , url-encoded params, file uploads etc ) using elegant syntax ala NodeJs routing
Though you would need to migrate your code to Vertx 3.0
Related
My Spring Boot application uses OAuth2 for security and token management. I’m querying one of my REST endpoints with an invalid token to test its response using Postman. The endpoint is correctly responding with 401 InvalidTokenException but the response content is HTML when I would like it to respond with JSON. Can this be done via code?
Example response
<InvalidTokenException>
<error>invalid_token</error>
<error_description>Access token expired: … my token… </error_description>
</InvalidTokenException>
To elaborate on zfChaos's answer, which is a good lead but does not provide sufficient information for the response to be a JSON response:
You should also set the content type and character encoding.
Then, write your JSON response (in this example I used a simple String, of course it would be more convenient use a class and an ObjectMapper).
Here is a complete example:
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.oauth2Login(login -> login
.failureHandler((request, response, exception) -> {
response.setContentType("application/json");
response.setStatus(401);
response.setCharacterEncoding("UTF-8");
response.getWriter().write("{ \"msg\": \"foo\" }");
})
);
}
}
Add custom AuthenticationFailureHandler to your security configuration and then prepare response in your custom implementation:
http.oauth2Login()
.failureHandler(customFailureHandler)
Failure handler example:
public class CustomFailureHandler extends SimpleUrlAuthenticationFailureHandler {
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException {
response.sendError(401, "XML HERE");
}
}
This is an attempt to use Loopj for a Synchronous put and post call from a HTTP utility class. The code uses a syncrhonous client as it’s used inside an AsyncTask and some UI interactions depend heavily on the json response so the AsyncTask is managing making the call asynchronously.
All the get calls from the HTTP utility class are working successfully. The post and put do not and they both appear to have exactly the same problem.
The json string is created using Gson. I’ve tested the json output from the application directly in Postman and it posts exactly as expected to the API, so it appears to be well formed and behaves totally as expected without any errors.
Both the put and post calls are constructed without throwing an error. Basic authorization is being added (as shown on the client instance). The SyncHTTPClient put method is called using a null context parameter. I did a bit of research and found a single post where this was being done successfully.
https://github.com/loopj/android-async-http/issues/1139
The put call fires but doesn’t enter either the overridden methods of the handler. It just returns null. I've included a portion of the working class to view:
public void executePutSave(String name, String pass, String jsonBody) {
client.setBasicAuth(name, pass);
executeLoopJPutCall("/api/Save", jsonBody);
}
public void executeLoopJPutCall(String relativeUrl, String jsonBody) {
String url = getAbsoluteUrl(relativeUrl);
StringEntity entity = new StringEntity(jsonBody, "UTF-8");
jsonResponse = null;
client.put(null, url, entity, "application/json", new JsonHttpResponseHandler() {
#Override
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
super.onSuccess(statusCode, headers, response);
jsonResponse = response.toString();
Log.i(TAG, "onSuccess: " + jsonResponse);
}
#Override
public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) {
super.onFailure(statusCode, headers, throwable, errorResponse);
jsonResponse = errorResponse.toString();
Log.e(TAG, "onFailure: " + statusCode + errorResponse );
}
}
);
}
So, apparently the header must be added explicitly when using the above code to Post or Put json to the API. Once I changed the header authentication line from this:
client.setBasicAuth(name, pass);
To this:
String userpass = name + ":" + pass;
String encoded = new String(Base64.encode(userpass.getBytes(),Base64.NO_WRAP));
client.addHeader("Authorization", "Basic "+encoded);
...everything worked as expected.
I found the information on this blog: https://github.com/loopj/android-async-http/issues/113
Passing a null context worked, too.
Currently I am working on a mini project where I am designing a static website hosted in S3. There is an 'Upload' page on which users will enter name, email and mobile no and files and click on 'Upload' button to invoke the API url endpoint. I have created API gateway url endpoint "https://myAPIName.execute-api.ap-region-1.amazonaws.com/Testing/TestinLambda2" on which I am invoking a post request using XMLHTTPRequest and sending it like this -
xhttp.open("POST", "https://myAPIName.execute-api.ap-region-1.amazonaws.com/Testing/TestinLambda2", true);
xhttp.send(JSON.stringify({Name:$('#Name').val(),Email:$('#email').val(),MobileNo:$('#mno').val()}));
I am sending the data as JSON input to aws lambda Java function.
I have not done any body mapping settings in AWS API gateway.
Back in AWS side, I am using AWS Lambda Java function using POJO. Below are my classes which I got from AWS lambda documentation -
My Lambda function
package com.amazonaws.lambda.demo;
import java.util.Map;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
public class FirstLambda implements RequestHandler<InputClass, OutputClass> {
#Override
public OutputClass handleRequest(InputClass input, Context context) {
String greetingString = String.format("Hello %s, %s , %s.", input.getName(), input.getEmail(), input.getMobile());
//String greetingString = String.format("Hello");
return new OutputClass(greetingString);
}
}
My InputClass
package com.amazonaws.lambda.demo;
public class InputClass {
String Name;
String Email;
String MobileNo;
public String getName(){
return this.Name;
}
public String getEmail(){
return this.Email;
}
public String getMobile(){
return this.MobileNo;
}
public void setName(String Name){
this.Name = Name;
}
public void setEmail(String Email){
this.Email = Email;
}
public void setMobile(String Mobile){
this.MobileNo = Mobile;
}
public InputClass(){
}
public InputClass(String Name, String Email, String Mobile){
this.Name = Name;
this.Email = Email;
this.MobileNo = Mobile;
}
}
My OutputClass
public class OutputClass {
String greetings;
public String getStrings()
{
return greetings;
}
public void setString()
{
this.greetings = greetings;
}
public OutputClass(String greetings)
{
this.greetings = greetings;
}
public OutputClass()
{
}
}
When I click on the 'Upload' button I get the value on my screen as -
{"strings":"Hello null, null , null."}
This is the same value I get when I test the POST method in API gateway using 'Test' option.
If someone can point out what I am missing here, I would really appreciate.
Thank you very much!
The reason why Gerards attempt in recreating the issue didn't work is because he tried this using test event rather than through the apigateway.
What is happening here is that when you are invoking the lambda through the gateway the whole json you send is encapsulated inside another object, which has other details such as pathparameters and other related details.
For testing this out you can create a new POJO and have your InputClass as a member in it. Change lambda paramter to new POJO and invoke lambda through the api gateway again.
This will give you the data you need.
I tested this with the classes you provided using Lambda test event's instead of API Gateway for simplicity, the fix should carry over to your JavaScript as well.
It seems that no matter how you name the class variables in your code, the parameters in the JSON must follow the names your getters/setters. Simply correcting the properties in your code should fix it. This makes sense because your class variables are not accessible from outside the package. So you have to follow the names of the mutator methods.
xhttp.open("POST", "https://myAPIName.execute-api.ap-region-1.amazonaws.com/Testing/TestinLambda2", true);
xhttp.send(JSON.stringify({name: $('#Name').val(), email: $('#email').val(), mobile: $('#mno').val()}));
The following payload:
{
"Name": "Gerard",
"Email": "abc#123.com",
"MobileNo": "8675309"
}
Generates
{"strings":"Hello null, null , null."}
While this payload
{
"name": "Gerard",
"email": "abc#123.com",
"mobile": "8675309"
}
Generated the expected response of:
{"strings": "Hello Gerard, abc#123.com , "8675309"."}
If your AWS Lambda is behind an API Gateway, the gateway will transform the incoming payload and pass it to the AWS Lambda in a different format (that you might expect).
It adds some metadata about the request (e.g. headers) and wraps everything in a JSON with the following format:
{
"headers": {}
"body": ""
...
}
This JSON is passed to your Java AWS Lambda function. You can take a look at this by temporarily adjust your RequestHandler with the following signature:
public class FirstLambda implements RequestHandler<Map<String, Object>, OutputClass> {
#Override
public OutputClass handleRequest(Map<String, Object> input, Context context) {
input.forEach((key, value) -> System.out.println(key + ":" + value);
return new OutputClass("");
}
}
Whenever you use the AWS Console and send a test event with a simple payload like
{
"Name": "Duke",
"Email": "mail#whatever.io"
}
it does not reflect the payload you receive when you invoke your Lambda using the API Gateway and e.g. curl or Postman.
So in your case, you should use e.g. Jackson or GSON to serialize the String inside the body field of the payload to your POJO.
If you don't want to work with a generic Map, you can also include
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-events</artifactId>
<version>3.1.0</version>
</dependency>
to your project and use the APIGatewayProxyRequestEvent and APIGatewayProxyResponseEvent wrapper classes and access headers, body, etc.
public class FirstLambda implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
#Override
public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEventinput, Context context) {
System.out.println(input.getBody());
// e.g. use the ObjectMapper from Jackson to parse the String body to your POJO
APIGatewayProxyResponseEvent result = new APIGatewayProxyResponseEvent();
result.setStatusCode(200);
result.setBody(objectMapper.writeValueAsString(new OutputClass(""));
return result;
}
}
Another solution would be to adjust the way the API Gateway passes data to your Lambda function, as explained here
Whats the simplest code i can use to start an asynchronous thread to fetch json data and then parse it on the UI thread in android using retrofit and rxjava?
I have searched this for two days and every solution so far gives me an error.
I basically want to replace all the old async tasks in my app with the newer retrofit and rxjava way of doing things.
import okhttp3.ResponseBody;
import retrofit2.Response;
import rx.Subscriber;
PoloniexService.getInstance().getAllCoins()
.subscribe(new Subscriber<Response<ResponseBody>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Response<ResponseBody> responseBodyResponse) {
}
});
Something like this.. Except this gives me the error:
Cannot resolve method 'subscribe(anonymous rx.Subscriber<retrofit2.Response<okhttp3.ResponseBody>>)'
I created a Remote Procedure Call. The Server-Side connects to Webservices, to get Information, which it hands over to the Client-Side. This is the Client-Side Code.
public void statusFor(GwtLaneServiceAsync laneProxy){
AsyncCallback<LaneInformation> callback = new AsyncCallback<LaneInformation>()
{
#Override
public void onFailure(Throwable caught)
{
}
#Override
public void onSuccess(LaneInformation information)
{
doStatusForSuccess(information);
}
};
for (Lane lane : this.mainmenu.getSlidePanel().getLaneMenu().getProperLanes().values())
{
if (lane.isChecked().booleanValue())
laneProxy.statusFor("admin", "password", true, lane.getId(), callback);
else
laneProxy.statusFor("admin", "password", false, lane.getId(), callback);
this.laneIndex++;
}
}
Now i wanna do the following...
When the Server can't reach the Webservice, a WebServiceException is thrown. If that happens, I wanna type "Offline" on one of my Buttons of the GUI. BUT I need to tell on which button. It can't be hard coded, cause it depends on which "lane" the Webservice failed.
I need to catch the Exceptions
I need to tell the "onFailure"-Part, on which lane, the Service failed.
Can I somehow deliver the statusFor()-Parameters to that part?
There is no of ways to handle such case. you can throw any custom exception from server side while server can't reach the webservice. then it will come onFailure block. or you can return any message string in response variable. Here response variable you are using LaneInformation bean. so take new variable there like result, and set message as per your requirement.
OnFailure it comes only when any exception occurred or any wrong thing happens in RPC call.
Why not wrap your LaneInformation in a generic response object and add the exception/an error code to that response, to signal that something went wrong on the server side, eg.:
public class RemoteResult<T>
{
T payload;
String errorCode;
}
and
public abstract class AbstractAsyncCallBack<T> implements AsyncCallback<RemoteResult<T>>
{
public void onSuccess( RemoteResult<T> rr )
{
if ( rr.getErrrorCode() != null ) { failure( rr.getErrorCode() ); }
else { success( rr.getPayload() ); }
}
public abstract void success( T payload );
public void failure( String errorCode ) { /* Ignore by default */ }
}
To conclude, you shouldn't throw an exception on the server side when the server can't connect to some other service, you should communicate that nicely to the client, and that's not by (re)throwing the exception :-)
The onFailure() method is mostly for when things go wrong in the RPC communication proper.
Cheers,