Setting headers and content length and mediatype in a Quarkus REST-SErvice - rest

I'd like to migrate a JAX-RS-REST-Restservice (running under Tomcat) to Quarkus.
I could solve most of my problems along the way but I still have a problem with one method.
In this function I do a OTA-download (firmware for a device). I set some headers and the MediaType and the content length.
In the original service my code looked as follows:
public HomeAutomationService
{
...
#Context
private HttpServletRequest request;
...
#GET
#Produces(MediaType.APPLICATION_OCTET_STREAM)
#Path("/v1/DownloadFirmware")
public Response getFirmware()
{
...
response.setHeader("X-OTA-SIGNATURE", signatureString);
response.setContentLength((int) file.length());
response.setContentType(MediaType.APPLICATION_OCTET_STREAM);
return Response.ok(file, MediaType.APPLICATION_OCTET_STREAM).build();
}
}
Unfortunately I can't find anything like a HttpServletResponse in Quarkus.
So I now use a ResponseBuilder to create a Response, where I can add headers as needed:
ResponseBuilder responseBuilder;
However, I am not sure how to instantiate the ResponseBuilder. There is a method to set headers for the ResponseBuilder, but I did not find anything on how to the content length and the content type.
I am not sure if I have to set the content-type since I already use a #Produces-annotation - but what about the content length? Is it set automatically? If no (that's what I guess) how can I set it correctly?
Thanks for reading and answering,
Rudi

You don't need to use #Produces in this case and your return should be something like the code below:
return Response.ok(yourFileBytes[])
.type(MediaType.APPLICATION_OCTET_STREAM)
.header(HttpHeaders.CONTENT_LENGTH, <yourFileLength>)
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; ")
.build();
The code above can be written as:
ResponseBuilder resp = Response.ok(yourFileBytes[]);
resp.type(MediaType.APPLICATION_OCTET_STREAM);
resp.header(HttpHeaders.CONTENT_LENGTH, <yourFileLength>);
resp.header(HttpHeaders.CONTENT_DISPOSITION, "inline; ");
return resp.build();

Related

Wiremock: choose body file name based on request parameter

I am using wiremock to mock http server and I am returning responses from json files (using withBodyFile method).
Now I want to choose and return response json file based on request parameter. For the example below, I want to define one stub so that the body file is chosen based on request parameter.
myMockServer.stubFor(
get(urlEqualTo(myEndPoint+ "?key=key1"))
.willReturn(aResponse().withStatus(200)
.withHeader("Content-Type", "application/json")
.withBodyFile("response_key1.json")
myMockServer.stubFor(
get(urlEqualTo(myEndPoint+ "?key=key2"))
.willReturn(aResponse().withStatus(200)
.withHeader("Content-Type", "application/json")
.withBodyFile("response_key2.json")
myMockServer.stubFor(
get(urlEqualTo(myEndPoint+ "?key=key3"))
.willReturn(aResponse().withStatus(200)
.withHeader("Content-Type", "application/json")
.withBodyFile("response_key3.json")
Any idea how this would be possible? I tried defining transformer but it was not possible to get Stream Source Path from Response object in overridden method so can't use that appraoch. Thanks a lot..
Body File name can't be parameterized in wiremock as of now. I had a similar requirement, I needed to return the file based on the request URL. So I implemented a transformer as below:
public class BodyFileNameResponseTransformer extends ResponseDefinitionTransformer {
public ResponseDefinition transform(Request request, ResponseDefinition rd,
FileSource fileSource, Parameters parameters) {
if (rd.getBodyFileName().startsWith("{{")) {
return new ResponseDefinitionBuilder().**withBodyFile**(request.getUrl().substring(1))
.withStatus(rd.getStatus())
.withHeaders(rd.getHeaders())
.withTransformers(
rd.getTransformers().toArray(new String[rd.getTransformers().size()]))
.build();
}
return rd;
}
public String getName() {
return "BodyFileNameTransformer";
}
}
you can use request.queryParameter(key) instead of request.getUrl() and form any
file path. Create the file path based on your need and set it as bodyFile on returned ResponseDefinition.
Don't forget to start wiremock with --extensions: Extension class names
More details at Extending Wiremock
This is possible by using the inbuilt template helpers provided by Handlebar.
myMockServer.stubFor(
get(urlEqualTo(myEndPoint+ "?key=key3"))
.willReturn(aResponse().withStatus(200)
.withHeader("Content-Type", "application/json")
.withBodyFile("response_{{request.query.key}}.json")
Check for the various models available at http://wiremock.org/docs/response-templating/.

Filename encoding is wrong in MultipartFormDataInput with RestEasy and Wildfly 11

My upload rest method works fine unless the filename contains a special character:
#POST
#Consumes({ MediaType.MULTIPART_FORM_DATA })
public Response uploadFile(MultipartFormDataInput input) {
Map<String, List<InputPart>> uploadForm = input.getFormDataMap();
List<InputPart> inputParts = uploadForm.get("file_upload");
// Do Stuff...
}
The filename is Test.png, inputPart.getHeaders() = [Content-Disposition=form-data; name="tws_file"; filename="test.png",Content-Type=image/png]
--> OK
The filename is Döner.png, inputPart.getHeaders() = [Content-Disposition=form-data; name="tws_file"; filename="d��ner.png",Content-Type=image/png]
--> Not OK
As you can see, the "ö" becomes "��".
Things I've tried so far:
Setting the default encoding in my jboss_web.xml to
<default-encoding>UTF-8</default-encoding> (see here)
Changing my #Consumes annotation to #Consumes(MediaType.MULTIPART_FORM_DATA+";charset=UTF-8"); (see here)
Changing the encoding in a container request filter with requestContext.setProperty(InputPart.DEFAULT_CHARSET_PROPERTY, "UTF-8"); (see here)
Nothing seems to work. Any ideas ? I am using Wildfly 11 and Resteasy-multipart-provider 3.0.24.Final
I ended up using the workaround of encoding the filename with a URI Encoder manually before sending it. On the other side, I decode it. That way, special characters are not a problem anymore.
Frontend in Angular:
encodeURI(fileName));
Backend in Java:
URLDecoder.decode(fileName, "UTF-8");
Adding a javax.ws.rs.container.ContainerRequestFilter solved this issue for me (Wildfly 11.0.0, Resteasy provided):
#Provider
public class CharsetRequestFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
requestContext.setProperty(InputPart.DEFAULT_CHARSET_PROPERTY, "UTF-8");
}
}
The problem has been fixed in the latest version: https://issues.jboss.org/browse/RESTEASY-1779

#BeanParam gives exception A message body reader for Java class was not found

I am trying to make a jersey based web service. In this if i take input params using #FormParam it works fine:
#POST
#Consumes({MediaType.TEXT_PLAIN, MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, MediaType.TEXT_HTML, "application/x-www-form-urlencoded"})
#Path("/registeruser")
public Response registerUser(#FormParam ("email") String email,#FormParam ("name") String name ){
System.out.println("Inside register device");
System.out.println("registered" + email);
return null;
}
but when I try using #BeanParam it does not works and gives me an exception
#POST
#Consumes({MediaType.TEXT_PLAIN, MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, MediaType.TEXT_HTML, "application/x-www-form-urlencoded"})
#Path("/registeruser")
public Response registerUser(#BeanParam UserForm userForm ){
System.out.println("Inside register device");
service.registerUser(userForm);
System.out.println("registered" + userForm.getEmail());
return null;
}
A message body reader for Java class com.stc.dms.forms.UserForm, and Java type class com.stc.dms.forms.UserForm, and MIME media type application/octet-stream was not found.
You don't need to use #BeanParam to pass an object as input. Just pass it like this :
#POST
#Path("register")
#Consumes(MediaType.APPLICATION_JSON)
public Response registerUser(UserForm dto) {
// ...
}
Just make sure to include the libraries for producing/consuming json. If the client is in javascript you don't need anything else (just use JSON.stringify() on your form object), for the server add some json libraries such as Jackson
EDIT :
If you want to stick with #BeanParam, take a look at this tutorial here. Basically it seems that you don't need to specify the mediatype, as Jersey will handle that for you.

Jaxrs QueryParam as a sub or a root item

Hi the design document I'm working off of wants me to have a URL in the pattern of
<root>/v1/installs/XYZ123/actions/next?app=1234ABCD HTTP/1.1
However the only examples I can find are extremely simple, and only show URLs that would end at the /next.
http://www.mkyong.com/webservices/jax-rs/jax-rs-queryparam-example/
I'm thinking it's something like... ? I'm missing a key step
#GET
#Produces({ "application/json" })
#Path("v1/installs/{id}/actions<SOMETHINGHERE?>/next HTTP/1.1")
public Response getSetupCommands(#PathParam("id")
String id,#QueryParam("next") String next) {
I'm using jboss and jaxrs
Why do I get the feeling that HTTP/1.1 should not be a part of the URL. You may have read/understood the design document incorrectly. If it was saying that the request should look like
GET /v1/installs/XYZ123/actions/next?app=1234ABCD HTTP/1.1
Then you only need to be worried about /v1/installs/XYZ123/actions/next?app=1234ABCD. HTTP/1.1 is simply the HTTP version that will be used implicitly with every request and response.
Your original example was fine, exception you should have replaced #QueryParam("next") with #QueryParam("app"). next is actually part of the path.
This /v1/installs/{id}/actions/next should be what's included in #Path.
The complete semantics of this request URL seems to read something like:
Get the next (resource) controller, and we will use the app query parameter as an argument to pass to this controller.
UPDATE: with example
#Path("/v1")
public class QueryResource {
#GET
#Path("/installs/{id}/actions/next")
public Response getResponse(#PathParam("id") String id,
#QueryParam("app") String app) {
StringBuilder sb = new StringBuilder();
sb.append("ID: ").append(id).append("<br/>");
sb.append("app param: ").append(app);
return Response.ok(sb.toString()).build();
}
}
Browser Test
Fire bug
v1/installs/{id}/actions/{next : .+}
public Response getSetupCommands(#PathParam("id") String id,#PathParam("next") String next) {
Way too hackish for my tastes, but I don't have any control in this situation..

Is it possible to place variables into a resource path within a sling servlet?

We are trying to provide a clean URI structure for external endpoints to pull json information from CQ5.
For example, if you want to fetch information about a particular users history (assuming you have permissions etc), ideally we would like the endpoint to be able to do the following:
/bin/api/user/abc123/phone/555-klondike-5/history.json
In the URI, we would specifying /bin/api/user/{username}/phone/{phoneNumber}/history.json so that it is very easy to leverage the dispatcher to invalidate caching changes etc without invalidating a broad swath of cached information.
We would like to use a sling servlet to handle the request, however, I am not aware as to how to put variables into the path.
It would be great if there were something like #PathParam from JaxRS to add to the sling path variable, but I suspect it's not available.
The other approach we had in mind was to use a selector to recognise when we are accessing the api, and thus could return whatever we wanted to from the path, but it would necessitate a singular sling servlet to handle all of the requests, and so I am not happy about the approach as it glues a lot of unrelated code together.
Any help with this would be appreciated.
UPDATE:
If we were to use a OptingServlet, then put some logic inside the accepts function, we could stack a series of sling servlets on and make the acceptance decisions from the path with a regex.
Then during execution, the path itself can be parsed for the variables.
If the data that you provide comes from the JCR repository, the best is to structure it exactly as you want the URLs to be, that's the recommended way of doing things with Sling.
If the data is external you can create a custom Sling ResourceProvider that you mount on the /bin/api/user path and acquires or generates the corresponding data based on the rest of the path.
The Sling test suite's PlanetsResourceProvider is a simple example of that, see http://svn.apache.org/repos/asf/sling/trunk/launchpad/test-services/src/main/java/org/apache/sling/launchpad/testservices/resourceprovider/
The Sling resources docs at https://sling.apache.org/documentation/the-sling-engine/resources.html document the general resource resolution mechanism.
It is now possible to integrate jersy(JAX-RS) with CQ. We are able to create primitive prototype to say "Hello" to the world.
https://github.com/hstaudacher/osgi-jax-rs-connector
With this we can use the #PathParam to map the requests
Thanks and Regards,
San
There is no direct way to create such dynamic paths. You could register servlet under /bin/api/user.json and provide the rest of the path as a suffix:
/bin/api/user.json/abc123/phone/555-klondike-5/history
^ ^
| |
servlet path suffix starts here
then you could parse the suffix manually:
#SlingServlet(paths = "/bin/api/user", extensions = "json")
public class UserServlet extends SlingSafeMethodsServlet {
public void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) {
String suffix = request.getRequestPathInfo().getSuffix();
String[] split = StringUtils.split(suffix, '/');
// parse split path and check if the path is valid
// if path is not valid, send 404:
// response.sendError(HttpURLConnection.HTTP_NOT_FOUND);
}
}
The RESTful way to approach this would be to have the information stored in the structure that you want to use. i.e. /content/user/abc123/phone/555-klondike-5/history/ would contain all the history nodes for that path.
In that usage. you can obtain an out of the box json response by simply calling
/content/user/abc123/phone/555-klondike-5/history.json
Or if you need something in a specific json format you could use the sling resource resolution to use a custom json response.
Excited to share this! I've worked ~ a week solving this, finally have the best Answer.
First: Try to use Jersey
The osgi-jax-rs-connector suggested by kallada is best, but I couldn't get it working on Sling 8. I lost a full day trying, all I have to show for it are spooky class not found errors and dependency issues.
Solution: The ResourceProvider
Bertrand's link is for Sling 9 only, which isn't released. So here's how you do it in Sling 8 and older!
Two Files:
ResourceProvider
Servlet
The ResourceProvider
The purpose of this is only to listen to all requests at /service and then produce a "Resource" at that virtual path, which doesn't actually exist in the JCR.
#Component
#Service(value=ResourceProvider.class)
#Properties({
#Property(name = ResourceProvider.ROOTS, value = "service/image"),
#Property(name = ResourceProvider.OWNS_ROOTS, value = "true")
})
public class ImageResourceProvider implements ResourceProvider {
#Override
public Resource getResource(ResourceResolver resourceResolver, String path) {
AbstractResource abstractResource;
abstractResource = new AbstractResource() {
#Override
public String getResourceType() {
return TypeServlet.RESOURCE_TYPE;
}
#Override
public String getResourceSuperType() {
return null;
}
#Override
public String getPath() {
return path;
}
#Override
public ResourceResolver getResourceResolver() {
return resourceResolver;
}
#Override
public ResourceMetadata getResourceMetadata() {
return new ResourceMetadata();
}
};
return abstractResource;
}
#Override
public Resource getResource(ResourceResolver resourceResolver, HttpServletRequest httpServletRequest, String path) {
return getResource(resourceResolver , path);
}
#Override
public Iterator<Resource> listChildren(Resource resource) {
return null;
}
}
The Servlet
Now you just write a servlet which handles any of the resources coming from that path - but this is accomplished by handling any resources with the resource type which is produced by the ResourceProvider listening at that path.
#SlingServlet(
resourceTypes = TypeServlet.RESOURCE_TYPE,
methods = {"GET" , "POST"})
public class TypeServlet extends SlingAllMethodsServlet {
static final String RESOURCE_TYPE = "mycompany/components/service/myservice";
#Override
protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException {
final String [] pathParts = request.getResource().getPath().split("/");
final String id = pathParts[pathParts.length-1];
response.setContentType("text/html");
PrintWriter out = response.getWriter();
try {
out.print("<html><body>Hello, received this id: " + id + "</body></html>");
} finally {
out.close();
}
}
}
Obviously your servlet would do something much more clever, such as process the "path" String more intelligently and probably produce JSON.