I run a web service where I convert a file from one file format into another. The conversion logic is already functioning but now, I want to query this logic via Jersey. Whenever file upload via Jersey is addressed in tutorials / questions, people describe how to do this using multipart form data. I do however simply want to send and return a single file and skip the overhead of sending multiple parts. (The webservice is triggered by another machine which I control so there is no HTML form involved.)
My question is how would I achieve something like the following:
#POST
#Path("{sessionId"}
#Consumes("image/png")
#Produces("application/pdf")
public Response put(#PathParam("sessionId") String sessionId,
#WhatToPutHere InputStream uploadedFileStream) {
return BusinessLogic.convert(uploadedFile); // returns StreamingOutput - works!
}
How do I get hold of the uploadedFileStream (It should be some annotation, I guess which is of course not #WhatToPutHere). I figured out how to directly return a file via StreamingOutput.
Thanks for any help!
You do not have to put anything in the second param of the function; just leave it un-annoted.
The only thing you have to be carefull is to "name" the resource:
The resource should have an URI like: someSite/someRESTEndPoint/myResourceId so the function should be:
#POST
#Path("{myResourceId}")
#Consumes("image/png")
#Produces("application/pdf")
public Response put(#PathParam("myResourceId") String myResourceId,
InputStream uploadedFileStream) {
return BusinessLogic.convert(uploadedFileStream);
}
If you want to use some kind of SessionID, I'd prefer to use a Header Param... something like:
#POST
#Path("{myResourceId}")
#Consumes("image/png")
#Produces("application/pdf")
public Response put(#HeaderParam("sessionId") String sessionId,
#PathParam("myResourceId") String myResourceId,
InputStream uploadedFileStream) {
return BusinessLogic.convert(uploadedFileStream);
}
Related
If I have an Apex function that is named authorize() that just gets a username, password, and session token, and another function called getURL('id#', 'key'), that takes an id# for the record as a string and a key for the image to return as a string as parameters. getURL calls the authorize function inside it in order to get the credentials for its callout. The authorize is a post request, and the getURL is a get request.
I am trying to figure out how to test both of these callouts just so I can make sure that getURL is returning the proper JSON as a response. It doesn't even have to be the URL yet which is its intention eventually. But I just need to test it to make sure these callouts are working and that I am getting a response back for the 75% code coverage that it needs.
I made a multiRequestMock class that looks like this:
public class MultiRequestMock implements HttpCalloutMock {
Map<String, HttpCalloutMock> requests;
public MultiRequestMock(Map<String, HttpCalloutMock> requests) {
this.requests = requests;
}
public HTTPResponse respond(HTTPRequest req) {
HttpCalloutMock mock = requests.get(req.getEndpoint());
if (mock != null) {
return mock.respond(req);
} else {
throw new MyCustomException('HTTP callout not supported for test methods');
}
}
public void addRequestMock(String url, HttpCalloutMock mock) {
requests.put(url, mock);
}
}
I then began to write a calloutTest.cls file but wasn't sure how to use this mock class in order to test my original functions. Any clarity or assistance on this would be helpful Thank you.
I believe in your calloutTest class you use Test.setMock(HttpCalloutMock.class, new MultiRequestMock(mapOfRequests)); then call the getUrl and/or authorize methods and instead of the request really executing the response returned will be that which is specified in the response(HttpRequest) method you have implemented in the MultiRequestMock class. That is basically how I see it working, for more info and an example you can see this resource on testing callout classes. This will get you the code coverage you need but unfortunately cannot check you are getting the correct JSON response. For this, you may be able to use the dev console and Execute Anonymous?
You may want to look at simplifying your HttpCalloutMock Implementation and think about removing the map from the constructor as this class really only needs to return a simple response then your calloutTest class can be where you make sure the returned response is correct.
Hope this helps
I have two POST methods and one DELETE in my resource. They have same path.
I annotated one of POSTs with #DefaultMethod, so when someone doesn't send correct Accept header, correct method will be selected. But this causes that when DELETE is called, cxf selects POST instead of correct delete method. Is there any workaround for this?
CXF version: 3.1.17
#DefaultMethod
#POST
#Consumes(MeasurementMediaType.MEASUREMENT_TYPE)
#Produces(MeasurementMediaType.MEASUREMENT_TYPE)
public Response post(MeasurementRepresentation measurementRepresentation, #HeaderParam(value = HttpHeaders.ACCEPT) String acceptHeader) URISyntaxException {
...
}
#POST
#Consumes(MEASUREMENT_COLLECTION_TYPE)
#Produces(MEASUREMENT_COLLECTION_TYPE)
public Response post(MeasurementCollectionRepresentation measurementCollectionRepresentation, #HeaderParam(value = HttpHeaders.ACCEPT) String acceptHeader) {
...
}
#DELETE
public Response delete(
#QueryParam("fragmentType") String fragmentType,
#QueryParam("source") String source,
#QueryParam("dateFrom") DateTime dateFrom,
#QueryParam("dateTo") DateTime dateTo,
#QueryParam("type") String type) {
...
}
java.lang.NullPointerException
at com.cumulocity.measurement.rest.resources.MeasurementCollectionResource.post(MeasurementCollectionResource.java:280)
Two things:
1) DefaultMethod appears not to refer to the default method to select, but the default HTTPMethod. So it's essentially overriding the httpmethod of your call. This is a CXF extension to JAX-RS, so you may be able to ask the CXF team to update the functionality or create a new annotation for your use case.
2) If I understand you correctly, you would like the first method to be called if someone sends the body of {"Hello" : "World"}? Wouldn't you then get errors when trying to construct your MeasurementRepresentation? If they send a bad request, why not let CXF respond with an appropriate HTTP error code?
I made a custom editor plugin, in a Seam 2.2.2 project, which makes file upload this way:
1) config the editor to load my specific xhtml upload page;
2) call the following method inside this page, and return a javascript callback;
public String sendImageToServer()
{
HttpServletRequest request = ServletContexts.instance().getRequest();
try
{
List<FileItem> items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);
processItems(items);//set the file data to specific att
saveOpenAttachment();//save the file to disk
}
//build callback
For this to work I have to put this inside components.xml:
<web:multipart-filter create-temp-files="false"
max-request-size="1024000" url-pattern="*"/>
The attribute create-temp-files do not seems to matter whatever its value.
But url-pattern has to be "" or "/myUploadPage.seam", any other value makes the item list returns empty. Does Anyone know why?
This turns into a problem because when I use a url-pattern that work to this case, every form with enctype="multipart/form-data" in my application stops to submit data. So I end up with other parts of the system crashing.
Could someone help me?
To solve my problem, I changed the solution to be like Seam multipart filter handle requests:
ServletRequest request = (ServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
try
{
if (!(request instanceof MultipartRequest))
{
request = unwrapMultipartRequest(request);
}
if (request instanceof MultipartRequest)
{
MultipartRequest multipartRequest = (MultipartRequest) request;
String clientId = "upload";
setFileData(multipartRequest.getFileBytes(clientId));
setFileContentType(multipartRequest.getFileContentType(clientId));
setFileName(multipartRequest.getFileName(clientId));
saveOpenAttachment();
}
}
Now I handle the request like Seam do, and do not need the web:multipart-filter config that was breaking other types of request.
I know that instead of using file_get_contents in Yii you can somehow use Yii::app()->request->getRawBody() but where do you specify the url you are making the call to?
No, they are not equivalent. You only use CHttpRequest::getRawBody() to retrieve the content of the request payload of the CURRENT request.
Below is the getRawBody implementation:
public function getRawBody()
{
static $rawBody;
if($rawBody===null)
$rawBody=file_get_contents('php://input');
return $rawBody;
}
To answer your question, the url is php://input
Using Restlet 2.1 for Java EE, I am discovering an interesting problem with its ability to handle attributes.
Suppose you have code like the following:
cmp.getDefaultHost().attach("/testpath/{attr}",SomeServerResource.class);
and on your browser you provide the following URL:
http://localhost:8100/testpath/command
then, of course, the attr attribute gets set to "command".
Unfortunately, suppose you want the attribute to be something like command/test, as in the following URL:
http://localhost:8100/testpath/command/test
or if you want to dynamically add things with different levels, like:
http://localhost:800/testpath/command/test/subsystems/network/security
in both cases the attr attribute is still set to "command"!
Is there some way in a restlet application to make an attribute that can retain the "slash", so that one can, for example, make the attr attribute be set to "command/test"? I would like to be able to just grab everything after testpath and have the entire string be the attribute.
Is this possible? Someone please advise.
For the same case I usually change the type of the variable :
Route route = cmp.getDefaultHost().attach("/testpath/{attr}",SomeServerResource.class);
route.getTemplate().getVariables().get("attr") = new Variable(Variable.TYPE_URI_PATH);
You can do this by using url encoding.
I made the following attachment in my router:
router.attach("/test/{cmd}", TestResource.class);
My test resource class looks like this, with a little help from Apache Commons Codec URLCodec
#Override
protected Representation get() {
try {
String raw = ResourceWrapper.get(this, "cmd");
String decoded = new String(URLCodec.decodeUrl(raw.getBytes()));
return ResourceWrapper.wrap(raw + " " + decoded);
} catch(Exception e) { throw new RuntimeException(e); }
}
Note my resource wrapper class is simply utility methods. The get returns the string of the url param, and the wrap returns a StringRepresentation.
Now if I do something like this:
http://127.0.0.1/test/haha/awesome
I get a 404.
Instead, I do this:
http://127.0.0.1/test/haha%2fawesome
I have URLEncoded the folder path. This results in my browser saying:
haha%2fawesome haha/awesome
The first is the raw string, the second is the result. I don't know if this is suitable for your needs as it's a simplistic example, but as long as you URLEncode your attribute, you can decode it on the other end.