JAX-RS: retrieve alternate format based on URL pattern - java

I have a simple JAX-RS service running in a Tomcat 6 container. Is it possible to display not only text but XML in the browser, in a comparable manner as in Rails by appending .xml or .json?
The service I have is based on this tutorial.

Short answer is no, JAX-RS does not allow you to switch the response content type simply by appending 'xml' or 'json' to the URL.
The specification explicitly defines how content negotiation happens - via the Accept HTTP header from the client side, matching with the #Produces annotation on the server side. So going by the spec, if you want to get a different content type then you would specify as such in an Accept header, and that content type would be returned (as long as the server supports it).
Having said that, JAX-RS implementations are free to implement custom (non-portable) extensions to support behavior similar to what you describe. An example would be RESTEasy, which allows you to define the desired content type as a query parameter Section 17.2 - Query String Parameter-based negotiation.
You could also design your resource classes to easily support the desired behavior - some examples can be seen in the Apache CXF Content Negotiation guide.

Related

Is it possible to programmatically resolve producible content-type given a request in spring-webflux?

My application uses spring-webflux, it still uses classic #Controllers with #RequestMapping-annotated handler methods.
Some methods produce application/json, while others produce text/event-stream.
When a request hits a controller, there is no problem: each mapping has produces with the corresponding media type defined.
The application also uses spring-security (the reactive flavor). If an unauthenticated request arrives, we must build a error response using correct format: JSON for application/json endpoints and a Server-Sent-Event for text/event-stream endpoints.
The problem is that security checks are made before the request handler is resolved, so Spring has no clue about the correct response media type at this point.
If a client sends Accept header, this solves the problem: we just parse it and decide what content type to use, with something like the following:
request.getHeaders().getAccept().contains(MediaType.TEXT_EVENT_STREAM)
(the algorithm is oversimplified, but you get the idea).
But some clients do not send Accept header at all. Strictly speaking, we have all the information we need: we have request, and, somewhere in spring-webflux beans information about all the mappings is stored.
So the question is: how (only having ServerWebExchange instance and access to Spring context) do you make use of this mapping information to find out what media types are supported by a handler corresponding to the current request?
P.S. What I tried/thought of so far:
Manually maintain list of all streaming endpoints... yuck!
Use a bean post-processor to collect information about all mappings and then try to emulate spring-webflux behavior... cumbersome and probably fragile.

Is any additional attributes required to force the java type in spring integration

We are using spring-integation (xml based configuration), In which we are performing below steps
Convert the payload (java-object) to json
Make the rest api call
Convert back to java-object
<int:object-to-json-transformer content-type="test.Request" input-channel="objectToJsonChannel" output-channel="apiChannel"/>
<int-http:outbound-gateway id="apiChannel"
request-channel="apiChannel"
reply-channel="jsonToObjectChannel"
....
/>
<int:json-to-object-transformer type="test.Response" input-channel="jsonToObjectChannel" output-channel="responseChannel"/>
Above code works till spring-integration version 5.1. When I upgrade to 5.2. It starts to throw the exception as
org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [test.Request] to type [test.Response].
I have noticed that object-to-json-transformer add class type on the header with key json__TypeId__. Then it uses that class type for json-to-object-transformer.
But it is expected that type attribute mentioned on json-to-object-transformer should be used if mentioned.
Please suggest on fixing this issue or Is it really bug on spring integration (5.2).
Consider to add a <header-filter header-names="json_*" /> before calling your REST service. The <int:object-to-json-transformer> populates JsonHeaders to let downstream to know what the real type of JSON we curry in the payload.
A <int:json-to-object-transformer> prefers those headers instead of static type option.
But since the payload is already a different representation than those request headers it does a wrong thing.
I would suggest an option on the <int:json-to-object-transformer> to make a preference, but that would not be fully logical. Since we have changed a payload, it would be better to change its respective headers. Otherwise we just lying to ourselves.
On the other hand a HTTP Outbound Gateway can take care for your to convert request into a JSON for network and back from JSON response to some POJO type.
See https://docs.spring.io/spring-integration/docs/5.2.3.RELEASE/reference/html/http.html#http-outbound and its expected-response-type. As long as a contentType header is an application/json, you are good to avoid those <int:object-to-json-transformer> & <int:json-to-object-transformer>.

Respond to html, json and xml with Spring MVC content negotiation

All examples that I have found around internet were about to use content negotiation with json, xml etc. Is there any chance to have only one method producing all kind of contents, like:
#RequestMapping(produces={"text/html","application/json"})
public List<Product> list() {
return productDAO.list();
}
I tried to use the ContentNegotiationManager, but nothing worked for me.
One method can return responses of different content types. Some you can get with default settings, some you have to additionally configure. Take for example a following method, quite similar to yours,
#RequestMapping(value="/response", method=RequestMethod.GET, produces={MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
public #ResponseBody Foo multipleTypes() {
return new Foo();
}
this method is capable of returning both XML and JSON, even more Spring MVC will automatically configure the converters if you have JAXB2 and Jackson libs on the classpath.
When reasoning whether it will return an XML or JSON its where content negotiation comes to play. If the request is suffixed with a path e.g. /response.json or /response.xml the response will be set based on it. The resolution can be based on a parameter as well, so /response?format=xml. Finally, if the request has an Accept header set to XML or JSON a response will be converted to the respective type. This constitutes a PPA strategy (Path, Parameter, Accept).
In other words, if you provide a proper converter implementations and configure them properly (some are available out of the box), you can get a single method that returns different representations, that you can control based on the PPA strategy.
Content Negotiation Using Spring MVC is a great post on Spring's Blog site with working examples.

Apache Camel - http Producer don't use GET-Method with enrich Pattern

I have a RESTful Webservice which I want to use with a content enricher in a Camel-Route.
See this similar Code:
from("direct:in") // Here comes XML
.to("validator:something.xsd") // validate it
.unmarshal("something-jaxb") // put it into a POJO
.setHeader(Exchange.HTTP_URI, simple("http://localhost:12345/restws/${header.foo}")) // Create the dynamic URI with simple
.setHeader(Exchange.HTTP_METHOD, constant("GET")) // set the HTTP-Method to use
.enrich("http://dummy", new MyAggregator()) // fetch some Information from a Restful Webservice
.to("direct:out"); // send the Message to another route
If I run this, I get the following error:
No type converter available to convert from type: de.my.Class to the required type: java.io.InputStream with value de.my.Class#620ee765.
It seems to me, he tries to send the body of the Exchange to the http-Endpoint, although I set the HTTP-Method to GET. I've read the documentation (https://camel.apache.org/http.html) and below Calling using GET or POST it describes that the Algorithm which selects the Method first look at the Header (1. Use method provided in header).
I found some workarounds, which describes how to move the body to a Exchange-property and move it back again after the Webservice-Call, but this can't be it...
EDIT:
Like Claus Ibsen mentioned enrich doesn't support dynamic uris. Fixed this in the example!
Neither enrich nor pollEnrich supports dynamic uris for their endpoints. Instead of using enrich, you can use the recipient list which support dynamic uris, and the aggregation strategy as well.
If it's "full of lists" of resources, sounds like you would want to split the lists and do a get on each resource
What's in the pojo? Your GET parameter should be a resource identifier and query parameters. That requires a custom converter.
http://fusesource.com/docs/esb/4.2/rest/RESTIntro.html
maybe consider using restlet instead... it's easier I think.
http://camel.apache.org/restlet.html

Set Request Attributes with ContentExchange

Is there a way to set request attributes in a ContentExchange object? What I have been doing so far is send information to the server in the Request body by using the setRequestContentSource(InputStream inputstream) method. But what should I do if I want to send information categorized by field names, say like, Content, Title, Author etc.?
The code that I have as of now is pasted below. Thanks for your help!
exchange.setRequestContentSource(new ByteArrayInputStream(
serialized.getBytes("UTF-8")));
exchange.setRequestContentType("text/html");
exchange.setMethod("POST");
exchange.setURL(("http://localhost:8089/"));
client.send(exchange);
To be more precise, by request attributes, I mean something equivalent to request.setAttribute("Name","ABC"). Only in this case, the request is sent is through the ContentExchange object.
Ah, so ServletRequest attributes are not part of the HTTP protocol.
There is no standard way to send those attributes via a HTTP protocol.
In fact, the servlet spec itself limits its use for application specific information (such as passing information from a filter to a servlet) and SSL certificate related information.
However, using standard POST and form data, via exchange.setRequestContentType("multipart/form-data"); and obtain those values using HttpServletRequest.getParameter("Content"), etc.. similar to how a FORM is submitted from a web browser. Benefit here, is that you can even provide a simple HTML FORM to test our your application.
Choice #2 is to send your data in a markup like JSON or XML and have the server parse it.
This has the benefit of allowing for hierarchical data.
If this interests you, I'd recommend you read about REST and maybe dig into a REST library like Jersey, Restlet, and standards like JAX-RS.

Categories