I have been trying to use this cordova plugin, which uses NanoHttpd to handle requests.
By default, Nanohttpd handles some of the HTTP methods, like GET, POST, CONNECT, PROPFIND, PATCH, etc.
I have been trying to figure out how to implement a custom handler so that nanohttpd can handled more HTTP methods like: NOTIFY and SUBSCRIBE
#Override
public Response serve(IHTTPSession session) {
Log.d(this.getClass().getName(), "New request is incoming!");
String requestUUID = UUID.randomUUID().toString();
PluginResult pluginResult = null;
try {
pluginResult = new PluginResult(
PluginResult.Status.OK, this.createJSONRequest(requestUUID, session));
} catch (JSONException e) {
e.printStackTrace();
}
pluginResult.setKeepCallback(true);
this.webserver.onRequestCallbackContext.sendPluginResult(pluginResult);
while (!this.webserver.responses.containsKey(requestUUID)) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
JSONObject responseObject = (JSONObject) this.webserver.responses.get(requestUUID);
Log.d(this.getClass().getName(), "responseObject: " + responseObject.toString());
Response response = null;
try {
response = newFixedLengthResponse(
Response.Status.lookup(responseObject.getInt("status")),
getContentType(responseObject),
responseObject.getString("body")
);
Iterator<?> keys = responseObject.getJSONObject("headers").keys();
while (keys.hasNext()) {
String key = (String) keys.next();
response.addHeader(
key,
responseObject.getJSONObject("headers").getString(key)
);
}
} catch (JSONException e) {
e.printStackTrace();
}
return response;
}
I added a simple notify Response to handle any incoming request, referring from here - https://stackoverflow.com/a/27645191/2096740
public Response notify(IHTTPSession session) {
StringBuilder text = new StringBuilder("<html><body>");
text.append("<h1>Url: ");
text.append(session.getUri());
text.append("</h1><br>");
Map<String, String> queryParams = session.getParms();
if (queryParams.size() > 0) {
for (Map.Entry<String, String> entry : queryParams.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
text.append("<p>Param '");
text.append(key);
text.append("' = ");
text.append(value);
text.append("</p>");
}
} else {
text.append("<p>no params in url</p><br>");
}
return newFixedLengthResponse(text.toString());
}
But this returnsBAD REQUEST: Syntax error. HTTP verb NOTIFY unhandled.
Documentation is not clear and there is not much info circulating on extending Nanohttpd behavior on SO or via web results.
What is the correct way to do this? How can I extend it ?
The check for Method is actually locked in an enum. It is hardcoded and there is no other method to expand.
The getMethod instance itself is a enum type of Method.
Since, I couldn't find any other solution, I therefore conclude it is not possible to do this stuff in Nanohttpd. All its versions in Maven dont support this.
The reason they have
Some built-in support for HEAD, POST and DELETE requests. You can
easily implement/customize any HTTP method, though.
mentioned in their feature list is because the original version had method as a String. It has changed since.
Feature list not been updated to reflect this change.
Related
#PostMapping()
public ResponseEntity<?> getCall(#Valid #RequestBody Request request) {
String requestJson = null;
try {
requestJson = ObjectMapperUtil.writeValueAsString(request);
log.info(requestJson) // will this introduce latency in my api.
return ResponseEntity.ok(service.getData(request));
} catch (Exception e) {
log.error(requestJson);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Request.builder().errors(INTERNAL_SERVER_ERROR)).build());
}
}
Just want to know that if we print the request body in json format after converting using ObjectMapper, what will be the impact on the latency on the api. Should we go ahead with #toString logging only. What's the good trade-off here.
If you're worried about latency, add an if statement around that code (most logging frameworks have such check methods):
String requestJson = null;
try {
if (log.isInfoEnabled()) {
requestJson = ObjectMapperUtil.writeValueAsString(request);
log.info(requestJson);
}
return ResponseEntity.ok(service.getData(request));
} catch (Exception e) {
if (requestJson != null) {
log.error(requestJson, e);
} else {
log.error("Failed to convert '{}' to JSON", request, e);
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Request.builder().errors(INTERNAL_SERVER_ERROR)).build());
}
Note that if the conversion of the object to JSON fails, requestJson will remain null, and there's no point in logging it. I also didn't add a check for log.isErrorEnabled() because a) that's almost always the case, and b) there's no logic involved in the error logging; any filtering will be done by the logger itself. Also note that I included the exception in the logging as well - you really want to know why the failure occurred.
There will still be latency, but only if needed. You can also consider moving the conversion into the catch (which needs its own try-catch). That way, the request JSON will only be logged if there's an error.
I have implement insert rating method in my app . I am searching more than 1 days to get the response . In this page i have check that insert like method and json is giving response . Look .
But i don't find any way to get the response in my app . How can i solve this ?
#SuppressLint("StaticFieldLeak")
class Insert extends AsyncTask<Object,Object, Object> {
#Override
protected Object doInBackground(Object... objects) {
if (Email!=null){
mCredential = GoogleAccountCredential.usingOAuth2(
getApplicationContext(), Arrays.asList(YouTubeScopes.YOUTUBE))
.setBackOff(new ExponentialBackOff());
mCredential.setSelectedAccountName(Email);
HttpTransport transport = AndroidHttp.newCompatibleTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
YouTube youtubeService = new YouTube.Builder(
transport, jsonFactory, mCredential)
.setApplicationName(LikeInsertActivity.this.getResources().getString(R.string.app_name))
.build();
// Define and execute the API request
try {
YouTube.Videos.Rate request = youtubeService.videos()
.rate(VID, "like");
request.execute();
runOnUiThread(new Runnable() {
public void run() {
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}
As #mavriksc mentions Rate.execute() returns Void. This is due to the fact that all these object are based on the superclass
com.google.api.services.youtube.YouTubeRequest<java.lang.Void>
However instead of the execute method you can use other methods defined by AbstractGoogleClientRequest which is a super class of YouTubeRequest.
For example executeUnparsed returns a com.google.api.client.HttpResponse object.
So obtaining that HttpResponse object and checking the StatusCode vs 204 seems to be the solution you want to have.
Example:
try {
final YouTube.Videos.Rate request = youtubeService.videos().rate(VID, "like");
runOnUiThread(new Runnable() {
public void run() {
HttpResponse response = request.executeUnparsed();
// There should be a matching constant for 204 defined somewhere, I haven't found it yet
if (response.getStatusCode() == 204) {
// request successfull
}
}
});
} catch (IOException e) {
e.printStackTrace();
}
Note:
Android forces developers to do certain (time consuming) things (like NetworkCommunication) in a background task to prevent the UI from blocking.
The return type of Rate.execute() is Void. looking at the HTTP it seems like you get a 204 no content on a good response and an exception otherwise.
https://developers.google.com/youtube/v3/docs/videos/rate
I wonder if it is as hard as I currently try to achieve it. I use Some Interceptor for security in my RESTEasy application. The interceptor implements javax.ws.rs.container.ContainerRequestFilter.
I use such a code to access request data:
if (((PostMatchContainerRequestContext) requestContext).getHttpRequest().getHttpMethod().equals("GET")) {
requestedId = Long.parseLong(requestContext.getUriInfo().getQueryParameters().get("id").get(0));
} else {
postDataMap = getPostData(requestContext);
}
and
private LinkedHashMap getPostData(ContainerRequestContext requestContext) {
Object obj = null;
try {
String result = IOUtils.toString(requestContext.getEntityStream());
ObjectMapper mapper = new ObjectMapper();
obj = mapper.readValue(result, Object.class);
System.out.println(obj);
} catch (IOException e) {
e.printStackTrace();
}
return (LinkedHashMap) obj;
}
But it seams ridiculous to access request data in such a way. Now I wanted to access DELETE-Request data but couldn't find any solution. Is there a much proper way to achieve what I am currently doing?
By intercepting you mean to check data before it goes to the rest layer? I have achieved this with implementing Filter interface (https://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/Filter.html).
Hope it helps.
Br,
Dusan
you can set security info in header,get value like this:
String logintoken = crc.getHeaderString("token");
I have a SOAP web service implementation on Jboss 4.2.3. I want to add a version number check for the service. Whenever a client makes a call, I will pass the client version number. I will write an interceptor at the server that would check the client version number. If it is a client with a different version number, I would not process the request.
What I want to know is if there is a way to pass the version number from the client in some context parameter other than adding it in the web service method signature?
In general, if I want to pass some custom META-DATA from client to server, how do I do it ?
In general, if I want to pass some custom META-DATA from client to
server, how do I do it ?
This can be achieved through SOAP Message Handlers both side (Client and Server ) in Jax-WS .
Client Side:
The custom-meta-data , like version number, UUID , Signature information can be added via SOAP Headers.
1..Write a VersionNumberHandler as shown below.
public class VersionNumberHandler implements SOAPHandler<SOAPMessageContext> {
private static final String LoggerName = "ClientSideLogger";
private Logger logger;
private final boolean log_p = true; // set to false to turn off
public VersionNumberHandler() {
logger = Logger.getLogger(LoggerName);
}
public boolean handleMessage(SOAPMessageContext ctx) {
if (log_p)
logger.info("handleMessage");
// Is this an outbound message, i.e., a request?
Boolean request_p = (Boolean) ctx
.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
// Manipulate the SOAP only if it's a request
if (request_p) {
// Get the Version Number from some property file ,
// to place in the message header.
String versionNumber = "v1.0";
try {
SOAPMessage msg = ctx.getMessage();
SOAPEnvelope env = msg.getSOAPPart().getEnvelope();
SOAPHeader hdr = env.getHeader();
// Ensure that the SOAP message has a header.
if (hdr == null)
hdr = env.addHeader();
QName qname = new QName("http://ticket.example.com/",
"versionnumber");
SOAPHeaderElement helem = hdr.addHeaderElement(qname);
// In SOAP 1.2, setting the actor is equivalent to
// setting the role.
helem.setActor(SOAPConstants.URI_SOAP_ACTOR_NEXT);
helem.setMustUnderstand(true);
helem.addTextNode(versionNumber);
msg.saveChanges();
// For tracking, write to standard output.
msg.writeTo(System.out);
} catch (SOAPException e) {
System.err.println(e);
} catch (IOException e) {
System.err.println(e);
}
}
return true; // continue down the chain
}
public boolean handleFault(SOAPMessageContext ctx) {
if (log_p)
logger.info("handleFault");
try {
ctx.getMessage().writeTo(System.out);
} catch (SOAPException e) {
System.err.println(e);
} catch (IOException e) {
System.err.println(e);
}
return true;
}
public Set<QName> getHeaders() {
if (log_p)
logger.info("getHeaders");
return null;
}
public void close(MessageContext messageContext) {
if (log_p)
logger.info("close");
}
2..Mention this class in the Handler-Chain.xml.
<javaee:handler>
<javaee:handler-class>
com.example.client.handler.VersionNumberHandler
</javaee:handler-class>
</javaee:handler>
3..Add the handler-chain in the client (Stub) also.
#WebServiceClient(name = "TicketWSImplService", targetNamespace = "http://ticket.example.com/", wsdlLocation = "http://localhost:8080/ticket?wsdl")
#HandlerChain(file = "handler-chain.xml")
public class TicketWSImplService extends Service {
#WebMethod
public void method(){
}
Here, we are adding a new header element "versionnumber" and mustunderstand=true, which means the server/intermediaries has to process this element, otherwise Jax-WS-Runtime will throw SOAP Fault exception to the client. Now we need to write a Validator(SOAP Handler) at the server side to validate this version number which is being passed by the clients.
Server Side:
1..Write a VersionNumberValidator as shown below.
public class VersionNumberValidator implements SOAPHandler<SOAPMessageContext> {
#SuppressWarnings("unused")
#Override
public boolean handleMessage(SOAPMessageContext ctx) {
// Is this an inbound message, i.e., a request?
Boolean response_p = (Boolean) ctx
.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
// Manipulate the SOAP only if it's incoming.
if (!response_p) {
try {
SOAPMessage msg = ctx.getMessage();
SOAPEnvelope env = msg.getSOAPPart().getEnvelope();
SOAPHeader hdr = env.getHeader();
// Ensure that the SOAP message has a header.
if (hdr == null) {
generateSOAPFault(msg, "No message header.");
return true;
}
Iterator mustUnderstandHeaders = msg.getSOAPHeader()
.examineMustUnderstandHeaderElements(
"http://schemas.xmlsoap.org/soap/actor/next");
String value = null;
while (mustUnderstandHeaders.hasNext()) {
Node next = (Node) mustUnderstandHeaders.next();
System.out.println("mustUnderstandHeaders name:"
+ next.getValue());
if (next.getNodeName().equalsIgnoreCase("versionnumber"))
value = next.getValue();
if (value != null && !value.equalsIgnoreCase("v1.0")) {
generateSOAPFault(msg, "Version Number Mismatch");
}
}
// For tracking, write to standard output.
msg.writeTo(System.out);
} catch (SOAPException e) {
System.err.println(e);
} catch (IOException e) {
System.err.println(e);
}
}
return true; // continue down the chain
}
#Override
public boolean handleFault(SOAPMessageContext ctx) {
return true; // do continue down the chain
}
// For now, no-ops.
#Override
public Set<QName> getHeaders() {
Set<QName> headers = new HashSet<QName>();
QName qName = new QName("http://ticket.example.com/", "versionnumber");
headers.add(qName);
return headers;
}
#Override
public void close(MessageContext messageContext) {
}
private void generateSOAPFault(SOAPMessage msg, String reason) {
try {
SOAPBody body = msg.getSOAPBody();
SOAPFault fault = body.addFault();
QName fault_name = new QName(
SOAPConstants.URI_NS_SOAP_1_2_ENVELOPE, "UltimateReceiver");
fault.setFaultCode(fault_name);
fault.setFaultRole("http://ticket.example.com/versionNumber_validator");
fault.addFaultReasonText(reason, Locale.US);
} catch (SOAPException e) {
}
}
2..Mention this class in the Handler-Chain-server.xml.
<javaee:handler>
<javaee:handler-class>
com.example.client.handler.VersionNumberValidator
</javaee:handler-class>
</javaee:handler>
3..Publish the webservices.
Now, the every client request will be having "version number =v1.0", At the server side , you will be validating this value is correct or not. If it is not correct, SOAPFaultException will be thrown.
You could add it to the http-headers but that would mean your client would need to do this which also means they can change it and give you wrong numbers causing issues on the server. It's only as reliable as the messages being sent in.
Either way, this isn't the right way to restrict access to your Web Service, you should use http basic authentication or if it's version differences then you should create multiple version endpoints giving clients access to the versions they need.
Also, JBoss 4.2.3 is so old it might not even work. See [1]
Mus
[1] https://community.jboss.org/message/534711
It's a bad idea to try to add out-of-band metadata to a web service. Just pick a new URL for each version if the data structures are incompatible. If they are compatible, put the version number inside the request.
This way you can still support interoperation with all different libraries and not require your clients to find a new hoop to jump through for each toolkit.
I need to log the full http request and response in a JAX-WS WebService call. For the request I need the request headers and the body and for the response, response headers and body.
After some researching, I've found that I can get this information with the property:
-Dcom.sun.xml.ws.transport.http.client.HttpTransportPipe.dump=true
and show the information that I need but it dumps it to the console and I need to store it in the database with an internal request id.
I've tried to implement a handler:
public class LoggingHandler implements SOAPHandler<SOAPMessageContext> {
#Override
public boolean handleMessage(SOAPMessageContext context) {
Boolean outbound = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outbound) {
System.out.println("SOAP outbound!!!!!");
Map<String, List<String>> responseHeaders = (Map<String, List<String>>) context
.get(SOAPMessageContext.HTTP_RESPONSE_HEADERS);
try {
String headers = getHeaders(responseHeaders);
System.out.println(headers);
String body = getBody(context.getMessage());
System.out.println(body);
} catch (Exception ex) {
// TODO: What do I have to do in this case?
}
} else {
System.out.println("SOAP inbound!!!!!");
Map<String, List<String>> requestHeaders = (Map<String, List<String>>) context
.get(SOAPMessageContext.HTTP_REQUEST_HEADERS);
try {
String headers = getHeaders(requestHeaders);
System.out.println(headers);
String body = getBody(context.getMessage());
System.out.println(body);
} catch (Exception ex) {
// TODO: What do I have to do in this case?
}
}
return true;
}
private String getBody(SOAPMessage message) throws SOAPException, IOException {
OutputStream stream = new ByteArrayOutputStream();
message.writeTo(stream);
return stream.toString();
}
public String getFullHttpRequest(HttpServletRequest request) throws IOException {
InputStream in = request.getInputStream();
String encoding = request.getCharacterEncoding();
encoding = encoding == null ? "UTF-8" : encoding;
String body = IOUtils.toString(in, encoding);
return body;
}
private String getHeaders(Map<String, List<String>> headers) throws IOException {
StringBuffer result = new StringBuffer();
if (headers != null) {
for (Entry<String, List<String>> header : headers.entrySet()) {
if (header.getValue().isEmpty()) {
// I don't think this is legal, but let's just dump it,
// as the point of the dump is to uncover problems.
result.append(header.getValue());
} else {
for (String value : header.getValue()) {
result.append(header.getKey() + ": " + value);
}
}
result.append("\n");
}
}
return result.toString();
}
}
but in this case, I can get the http request headers and body but in the response, I only get the body, http response headers are always empty.
Any idea on how to archieve this? The objective is to be able to store the full http request and response in a database.
Thanks!!
You could also try
-Dcom.sun.xml.ws.transport.http.HttpAdapter.dump=true
I'm assuming you're providing your web service from within a Java EE application server of some sort (and not from a standalone client). You cannot have access to Java EE infrastructure like HttpServletRequest and HttpServletResponse outside of the context of a web/Java EE container.
You could try to get your hands on the actual servlet response object (within a web context) with
HttpServletResponse response = (HttpServletResponse) messageContext.get(SOAPMessageContext.SERVLET_RESPONSE); //messageContext is the SOAPMessageContext
List<String> responseHeaderNames = (List<String>)response.getHeaderNames();
for(String headerName : responseHeaderNames){
//Do whatever you want with it.
}
I seriously doubt that you'll be able to get your hands on the full response headers within a handler though. Your question really intrigued me and I've spent quite some time researching that part. In all the code samples I've seen, Not even the example on the metro site attempt to implement this functionality and I think the reason is simple. As at the point where a handler is invoked, the container may not have enough definitive information to stamp an http header on the outbound message. You might be able to add stuff but that's doubtful as well.