How can I avoid escaping a WebTarget query parameter? - java

I want to turn off URL encoding for Jersey requests, or for certain parameters.
The server I'm talking to requires the format of example.com/path?query=foo:bar
With Jackson WebTarget,
final WebTarget target = ClientBuilder.newClient()
.target(url)
.queryParam("query", "{queryVal}")
.resolveTemplate("queryVal", "foo:bar");
Sadly this produces example.com/path?query=foo bar which is not accepted by the server.
I've searched a lot for this and the only promising avenue seems to be something to do with javax.ws.rs.core.Configuration, but I haven't gotten far with that yet.

I figured it out: The solution is to use a request filter as documented in Advanced Features of the Client API
final WebTarget target = ClientBuilder.newClient()
.target(url)
.queryParam("query", "{queryVal}")
.resolveTemplate("queryVal", "foo:bar")
.register(Filter.class)
and then we have
#Provider
public class Filter implements ClientRequestFilter {
private static final Logger logger = LoggerFactory.getLogger(Filter.class);
#Override
public void filter(ClientRequestContext ctx) throws IOException {
try {
logger.debug("Before: {}", ctx.getUri());
//this is gonna get ugly
ctx.setUri(new URI(
URLDecoder.decode(ctx.getUri().toString(), StandardCharsets.UTF_8.toString())));
logger.debug("After: {}", ctx.getUri());
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
}
Output:
com.example.client.Filter : Before: http://example.com/path?query=foo%3Abar
com.example.client.Filter : After: http://example.com/path?query=foo:bar

Related

Redirect using Java Jersey rest filters

I am trying to request a new call from the request pre-matching filter. I need to initiate the new request from the start so that this new requested will again go through the filters and route to a different process.
#Provider
#PreMatching
public class SSOFilter implements ContainerRequestFilter
{
#Override
public void filter(ContainerRequestContext requestContext) throws IOException
{
URI ssoUri = null;
try
{
String requestedQueryString = requestContext.getUriInfo().getRequestUri().getQuery();
ssoUri = new URI(requestContext.getUriInfo().getBaseUri()+"/saml/SSO?"+ requestedQueryString);
}
catch (URISyntaxException e)
{
Debug.println(Debug.ERROR, "Error encountered in generating SSO URI " + e.getMessage());
}
Response.status(Status.TEMPORARY_REDIRECT).location(ssoUri).build();
}
}
I want this call to happen only on the server so that browser shouldn't be aware of what's happening here, Also the browser URL shouldn't update.

Is it possible to response with http error, that code are variable in java spring?

I have an Microservice and it gets an response from another. And based on the response I get I need to respond accordingly. I have no complete List of Error code I can receive, so the question is - can I generate error codes on the fly for my own response? From what I saw in spring the responses are predefined in code. I need to be flexible.
For example:
I receive a 409 I will respond with 409
I receive a 400 I will respond with 400
I receive a XXX code I will respond with XXX.
Try this code: (Sample code)
#RequestMapping(value = "/validate", method = RequestMethod.GET, produces = "application/json")
public ResponseEntity<ErrorBean> validateUser(#QueryParam("jsonInput") final String jsonInput) {
int numberHTTPDesired = 400;
ErrorBean responseBean = new ErrorBean();
responseBean.setError("ERROR");
responseBean.setMessage("Error in validation!");
return new ResponseEntity<ErrorBean>(responseBean, HttpStatus.valueOf(numberHTTPDesired));
}
I have worked on such a use case using the following concept. Try to create a generic exception across micro services. Take 2 params in the exception as error message and another one as error code. Throw the exception from the service being called and catch the same exception in the calling service in the rest template or feign client call.
public class MyException extends Exception {
private String errorCode;
public MyException() {
super();
}
public MyException(String message, String errorCode) {
super(message);
this.errorCode = errorCode;
}
}
--
try {
return myApiService.getUser();//call to myApiService microservice
} catch (MyException e) {
logger.error("Error: {}", e.getMessage());
throw new MyException(e.getMessage(), e.getCode());
}

How to control the HTTP response code from a Restlets resource?

I am currently working with the Restlets framework, and I cannot find a way to manually set the HTTP response code within a service method. Consider the following snippet of code:
public class MyResource extends ServerResource {
#Post("json")
public Representation doSomething(Representation entity) throws IOException {
int status = 200;
try {
// do something which might throw an exception
}
catch (Exception e) {
// log the exception
// *** I would like to assign HTTP status 500 here ***
status = 500;
}
JSONObject responseJSON = new JSONObject();
responseJSON.put("result", "some data");
Representation rep = new JsonRepresentation(responseJSON.toJSONString());
return rep;
}
}
I have the ability to catch and log an exception, should one occur, but it is not clear how I can change the HTTP response code. As far as I know, returning from doSomething will automatically be handled by Restlets with an 200 HTTP response code.
I know how to assign the status code directly from a filter or servlet, but is it possible to do this within Restlets, without going down the servlet layer?
As far as I know, there is an object called ResponseEntity which you can use to operate with microservices and a request-response programming model, which allows you to specify the returning HTTP return code. However, you need entities for this, and I think this goes below your abstraction level of Servlets.
You can change them to some predefined values such as HTTP.INTERNAL_SERVER_ERROR and such, which translate to a value in the end, which you can Google in the end.
I hope this was of some help
EDIT:
Import the necessary resource for a ResponseEntity object. In STS, it is
import org.springframework.http.ReponseEntity;
import org.springframework.http.HttpStatus;
public class MyResource extends ServerResource {
#Post("json")
public ResponseEntity<Representation> doSomething(Representation entity) throws IOException {
int status = 200;
try {
// do something which might throw an exception
}
catch (Exception e) {
ResponseEntity<Representation> response = null;
response = new ResponseEntity<Representation>(HttpStatus.INTERNAL_SERVER_ERROR);
return response;
}
JSONObject responseJSON = new JSONObject();
responseJSON.put("result", "some data");
Representation rep = new JsonRepresentation(responseJSON.toJSONString());
return rep;
}
And sorry for the delay. I am new to Stack Overflow

Handling custom error response in JAX-RS 2.0 client library

I am starting to use the new client API library in JAX-RS and really loving it so far. I have found one thing I cannot figure out however. The API I am using has a custom error message format that looks like this for example:
{
"code": 400,
"message": "This is a message which describes why there was a code 400."
}
It returns 400 as the status code but also includes a descriptive error message to tell you what you did wrong.
However the JAX-RS 2.0 client is re-mapping the 400 status into something generic and I lose the good error message. It correctly maps it to a BadRequestException, but with a generic "HTTP 400 Bad Request" message.
javax.ws.rs.BadRequestException: HTTP 400 Bad Request
at org.glassfish.jersey.client.JerseyInvocation.convertToException(JerseyInvocation.java:908)
at org.glassfish.jersey.client.JerseyInvocation.translate(JerseyInvocation.java:770)
at org.glassfish.jersey.client.JerseyInvocation.access$500(JerseyInvocation.java:90)
at org.glassfish.jersey.client.JerseyInvocation$2.call(JerseyInvocation.java:671)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:424)
at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:667)
at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:396)
at org.glassfish.jersey.client.JerseyInvocation$Builder.get(JerseyInvocation.java:296)
Is there some sort of interceptor or custom error handler that can be injected so that I get access to the real error message. I've been looking through documentation but can't see any way of doing it.
I am using Jersey right now, but I tried this using CXF and got the same result. Here is what the code looks like.
Client client = ClientBuilder.newClient().register(JacksonFeature.class).register(GzipInterceptor.class);
WebTarget target = client.target("https://somesite.com").path("/api/test");
Invocation.Builder builder = target.request()
.header("some_header", value)
.accept(MediaType.APPLICATION_JSON_TYPE)
.acceptEncoding("gzip");
MyEntity entity = builder.get(MyEntity.class);
UPDATE:
I implemented the solution listed in the comment below. It is slightly different since the classes have changed a bit in the JAX-RS 2.0 client API. I still think it is wrong that the default behavior is to give a generic error message and discard the real one. I understand why it wouldn't parse my error object, but the un-parsed version should have been returned. I end up having the replicate exception mapping that the library already does.
Thanks for the help.
Here is my filter class:
#Provider
public class ErrorResponseFilter implements ClientResponseFilter {
private static ObjectMapper _MAPPER = new ObjectMapper();
#Override
public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
// for non-200 response, deal with the custom error messages
if (responseContext.getStatus() != Response.Status.OK.getStatusCode()) {
if (responseContext.hasEntity()) {
// get the "real" error message
ErrorResponse error = _MAPPER.readValue(responseContext.getEntityStream(), ErrorResponse.class);
String message = error.getMessage();
Response.Status status = Response.Status.fromStatusCode(responseContext.getStatus());
WebApplicationException webAppException;
switch (status) {
case BAD_REQUEST:
webAppException = new BadRequestException(message);
break;
case UNAUTHORIZED:
webAppException = new NotAuthorizedException(message);
break;
case FORBIDDEN:
webAppException = new ForbiddenException(message);
break;
case NOT_FOUND:
webAppException = new NotFoundException(message);
break;
case METHOD_NOT_ALLOWED:
webAppException = new NotAllowedException(message);
break;
case NOT_ACCEPTABLE:
webAppException = new NotAcceptableException(message);
break;
case UNSUPPORTED_MEDIA_TYPE:
webAppException = new NotSupportedException(message);
break;
case INTERNAL_SERVER_ERROR:
webAppException = new InternalServerErrorException(message);
break;
case SERVICE_UNAVAILABLE:
webAppException = new ServiceUnavailableException(message);
break;
default:
webAppException = new WebApplicationException(message);
}
throw webAppException;
}
}
}
}
I believe you want to do something like this:
Response response = builder.get( Response.class );
if ( response.getStatusCode() != Response.Status.OK.getStatusCode() ) {
System.out.println( response.getStatusType() );
return null;
}
return response.readEntity( MyEntity.class );
Another thing you can try (since I don't know where this API puts stuff -- i.e. in the header or entity or what) is:
Response response = builder.get( Response.class );
if ( response.getStatusCode() != Response.Status.OK.getStatusCode() ) {
// if they put the custom error stuff in the entity
System.out.println( response.readEntity( String.class ) );
return null;
}
return response.readEntity( MyEntity.class );
If you would like to generally map REST response codes to Java exception you can add a client filter to do that:
class ClientResponseLoggingFilter implements ClientResponseFilter {
#Override
public void filter(final ClientRequestContext reqCtx,
final ClientResponseContext resCtx) throws IOException {
if ( resCtx.getStatus() == Response.Status.BAD_REQUEST.getStatusCode() ) {
throw new MyClientException( resCtx.getStatusInfo() );
}
...
In the above filter you can create specific exceptions for each code or create one generic exception type that wraps the Response code and entity.
There are other ways to getting a custom error message to the Jersey client besides writing a custom filter. (although the filter is an excellent solution)
1) Pass error message in an HTTP header field.
The detail error message could be in the JSON response and in an additional header field, such as "x-error-message".
The Server adds the HTTP error header.
ResponseBuilder rb = Response.status(respCode.getCode()).entity(resp);
if (!StringUtils.isEmpty(errMsg)){
rb.header("x-error-message", errMsg);
}
return rb.build();
The Client catches the exception, NotFoundException in my case, and reads the response header.
try {
Integer accountId = 2222;
Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target("http://localhost:8080/rest-jersey/rest");
webTarget = webTarget.path("/accounts/"+ accountId);
Invocation.Builder ib = webTarget.request(MediaType.APPLICATION_JSON);
Account resp = ib.get(new GenericType<Account>() {
});
} catch (NotFoundException e) {
String errorMsg = e.getResponse().getHeaderString("x-error-message");
// do whatever ...
return;
}
2) Another solution is to catch the exception and read the response content.
try {
// same as above ...
} catch (NotFoundException e) {
String respString = e.getResponse().readEntity(String.class);
// you can convert to JSON or search for error message in String ...
return;
}
The class WebApplicationException was designed for that but for some reason it ignores and overwrites what you specify as parameter for the message.
For that reason I created my own extension WebAppException that honors the parameters. It is a single class and it doesn't require any response filter or a mapper.
I prefer exceptions than creating a Response as it can be thrown from anywhere while processing.
Simple usage:
throw new WebAppException(Status.BAD_REQUEST, "Field 'name' is missing.");
The class:
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.Response.Status.Family;
import javax.ws.rs.core.Response.StatusType;
public class WebAppException extends WebApplicationException {
private static final long serialVersionUID = -9079411854450419091L;
public static class MyStatus implements StatusType {
final int statusCode;
final String reasonPhrase;
public MyStatus(int statusCode, String reasonPhrase) {
this.statusCode = statusCode;
this.reasonPhrase = reasonPhrase;
}
#Override
public int getStatusCode() {
return statusCode;
}
#Override
public Family getFamily() {
return Family.familyOf(statusCode);
}
#Override
public String getReasonPhrase() {
return reasonPhrase;
}
}
public WebAppException() {
}
public WebAppException(int status) {
super(status);
}
public WebAppException(Response response) {
super(response);
}
public WebAppException(Status status) {
super(status);
}
public WebAppException(String message, Response response) {
super(message, response);
}
public WebAppException(int status, String message) {
super(message, Response.status(new MyStatus(status, message)). build());
}
public WebAppException(Status status, String message) {
this(status.getStatusCode(), message);
}
public WebAppException(String message) {
this(500, message);
}
}
A much more concise solution for anyone stumbling on this:
Calling .get(Class<T> responseType) or any of the other methods that take the result type as an argument Invocation.Builder will return a value of the desired type instead of a Response. As a side effect, these methods will check if the received status code is in the 2xx range and throw an appropriate WebApplicationException otherwise.
From the documentation:
Throws: WebApplicationException in case the response status code of
the response returned by the server is not successful and the
specified response type is not Response.
This allows to catch the WebApplicationException, retrieve the actual Response, process the contained entity as exception details (ApiExceptionInfo) and throw an appropriate exception (ApiException).
public <Result> Result get(String path, Class<Result> resultType) {
return perform("GET", path, null, resultType);
}
public <Result> Result post(String path, Object content, Class<Result> resultType) {
return perform("POST", path, content, resultType);
}
private <Result> Result perform(String method, String path, Object content, Class<Result> resultType) {
try {
Entity<Object> entity = null == content ? null : Entity.entity(content, MediaType.APPLICATION_JSON);
return client.target(uri).path(path).request(MediaType.APPLICATION_JSON).method(method, entity, resultType);
} catch (WebApplicationException webApplicationException) {
Response response = webApplicationException.getResponse();
if (response.getMediaType().equals(MediaType.APPLICATION_JSON_TYPE)) {
throw new ApiException(response.readEntity(ApiExceptionInfo.class), webApplicationException);
} else {
throw webApplicationException;
}
}
}
ApiExceptionInfo is custom data type in my application:
import lombok.Data;
#Data
public class ApiExceptionInfo {
private int code;
private String message;
}
ApiException is custom exception type in my application:
import lombok.Getter;
public class ApiException extends RuntimeException {
#Getter
private final ApiExceptionInfo info;
public ApiException(ApiExceptionInfo info, Exception cause) {
super(info.toString(), cause);
this.info = info;
}
}
[At least with Resteasy] there is one big disadvantage with the solution offered by #Chuck M and based on ClientResponseFilter.
When you use it based on ClientResponseFilter, your BadRequestException, NotAuthorizedException, ... exceptions are wrapped by javax.ws.rs.ProcessingException.
Clients of your proxy must not be forced to catch this javax.ws.rs.ResponseProcessingException exception.
Without filter, we get an original rest exception. If we catch and handle by default, it does not give us much:
catch (WebApplicationException e) {
//does not return response body:
e.toString();
// returns null:
e.getCause();
}
The problem can be solved on another level, when you extract a description from the error. WebApplicationException exception, which is a parent for all rest exceptions, contains javax.ws.rs.core.Response. Just write a helper method, that in case the exception is of WebApplicationException type, it will also check the response body. Here is a code in Scala, but the idea should be clear. The methord returns a clear description of the rest exception:
private def descriptiveWebException2String(t: WebApplicationException): String = {
if (t.getResponse.hasEntity)
s"${t.toString}. Response: ${t.getResponse.readEntity(classOf[String])}"
else t.toString
}
Now we move a responsibility to show exact error, on the client. Just use a shared exception handler to minimize effort for clients.
The following works for me
Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage()).build();

GET request using GWT to retrieve XML data?

Oh hello there, fellow SO members,
I have a web service that returns XML data using a simple get request that goes like this :
http://my-service:8082/qc/getData?paramX=0169&paramY=2
the service returns raw xml in the page according to the parameters' values.
I am trying to retrieve this data from a GET request in GWT using RequestBuilder, Request, etc.
However, the response gives me empty text, a Status code of ZERO (which doesn't mean anything and isn't supposed to happen), and so on.
Here's the simplified code that doesn't work.
public class SimpleXML implements EntryPoint {
public void onModuleLoad() {
this.doGet("http://my-service:8082/qc/getData", "0169", "2");
}
public void doGet(String serviceURL, String paramX, String paramY) {
final String getUrl = serviceURL + "?paramX=" + paramX + "&idTarification=" + paramY;
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, getUrl);
try {
Request response = builder.sendRequest(null, new RequestCallback() {
#Override
public void onResponseReceived(Request request, Response response) {
response.getStatusCode(); // Gives me 0 (zero) :(
}
#Override
public void onError(Request request, Throwable exception) {
// ... doesn't matter for this example
}
});
} catch (RequestException e) {
// ... doesn't matter for this example
}
}
}
I don't get why this wouldn't work, since this is REALLY simple, I've seen tutorials and they all show me this way of doing things..
Thanks in advance
The reason is, that browsers do not allow cross-site requests with AJAX (see Same Origin Policy).
This means, that you can only call a service on the same server, same port (using the same protocol) as your HTML page. If you want to perform cross-site requests, you can use JSONP, as explained in http://code.google.com/webtoolkit/doc/latest/tutorial/Xsite.html.

Categories

Resources