How to use HEAD to check if file exests - java

I want to use this Spring Endpoint to upload a file.
#PostMapping(value = "/upload", produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<StringResponseDTO> uploadFile(#RequestParam("file") MultipartFile file,
RedirectAttributes redirectAttributes, #RequestParam("id") Integer merchantId) throws Exception {
..............
return ResponseEntity.ok(new StringResponseDTO("test"));
}
Angular code:
imports(file: any, id: number) {
const formData = new FormData();
formData.append('file', file, file.name);
return this.http.post(environment.api.urls.merchants.uploadLogo, formData, {
params: { id: id.toString() }
}).pipe(
catchError(this.handleError)
);
}
In my case I want to display a download link into Angular if the file is present. How I can make a request using the HEAD and check if the file is present?
I don;t want to download the file in order to verify the the file if available for download.

HTTP Request Methods
Before addressing your question[s] directly I want to address the concept of an HTTP Request Method's in general. An HTTP Request Method is used to indicate the desired action to be performed on the identified resource. The following methods are standardized to some extent
GET
POST
HEAD
OPTIONS
PUT
DELETE
PATCH
CONNECT
What the resource represents and how you actual implement the service and invocation of the service ( the request ) is completely up to you.
With that being said, it would be accurate to say that a GET request that is used explicitly to update an existing value is implemented incorrectly.
Additionally, what method you choose will be limit your functionality if you are using any type of client-side or server-side platform or library. For example, Angular won't provide a means of sending a request body with a GET, even though you technically could craft an HTTP GET call that contains a request body.
Spring Boot
Looking at your code you explicitly set your service as a POST service with the following code
#PostMapping(value = "/upload", produces = { MediaType.APPLICATION_JSON_VALUE })
You can explicitly specify your service as a HEAD service by swapping that line with the following
#RequestMapping( value = "/upload", method = RequestMethod.HEAD, produces = { MediaType.APPLICATION_JSON_VALUE } )
However, remember that a HEAD method should basically be a GET method but should only return headers. In the sample provided I cannot tell what your DTO does specifically, but it appears to be returning a body. Instead you can return only headers using something like
String fileId = ....
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("fileId", fileId);
return ResponseEntity.ok().headers(responseHeaders);
The naming convention you are using is not RESTful. Firstly, /upload endpoint is not resource oriented because there is no resource specified in your path.
Additionally uploading is probably not the correct verb to use, since what you're actually doing is checking or validating. I would probably swap the endpoint for something like
#RequestMapping( value = "/resourceName/validate", method = RequestMethod.HEAD, produces = { MediaType.APPLICATION_JSON_VALUE } )
Replacing resourceName with the name of your resource.
Angular
Likewise in your Angular call your invocation is explicitly POST
return this.http.post( ... )
Similarly Angular has an $http.head method
return this.http.head( ... )
Unlike Spring Boot, you may not be able to simply swap out line for line though. You will need to make sure that the parameters in head match the parameters in post, and if not you will need to adjust accordingly.
Summary
My answer can be summarized as follows
You may want to rethink your use of HEAD in general ( does it apply here? )
You may want to rename your endpoint to be resource-oriented
If you do want to use head you need to change your Spring Boot annotation
If you do want to use head you need to change your Spring Boot response to only include headers
If you do want to use had you need to change your Angular request to use the head method

Related

Spring Boot Content type 'multipart/form-data;boundary=--------------------------#;charset=UTF-8' not supported

I'm new to Spring...I have a Spring Boot API application and all of my methods (POST, GET, etc) work great with Postman when the Content-Type is set to application/json, I can send and receive JSON.
However, I'd really like for my methods to also accept a GET or POST in the browser. When I use the browser to do a GET, the API returns an INTERNAL_SERVER_ERROR. I created a small form and try to POST to my API, but then I get the UNSUPPORTED_MEDIA_TYPE: Content type 'multipart/form-data;boundary=--------------------------802438220043016845671644;charset=UTF-8' not supported
These are 2 of the methods in my #RestController:
#RequestMapping(method = RequestMethod.POST, value = {"","/"})
public ResponseEntity<MyModel> createModel(#Valid #RequestBody MyModelDto modelDto) {
MyModel model = modelService.createModel(modelDto);
URI createdModelUrl = ServletUriComponentsBuilder.fromCurrentRequest().path("/{identifier}")
.buildAndExpand(model.getIdentifier()).normalize().toUri();
return ResponseEntity.created(createdModelUrl).build();
#RequestMapping(method = RequestMethod.GET, value = "/{identifier}")
public Resource<MyModel> getByIdentifier(#PathVariable("identifier") String identifier) {
MyModel model = modelService.getByIdentifier(identifier);
Resource<MyModel> resource = new Resource<>(model);
return resource;
}
If there's any other code that would be helpful to show, let me know and I'll update the thread.
In createModel method, instead of #RequestBody, please use #ModelAttribute for MyModelDto parameter.
You can use can try following ways,
Set consume block in "#RequestMapping".
like , #RequestMapping(value="/abc", consume = "multipart/form-data", method=HTTPMethod.POST")
Use #Multipart annotation and file object as #Part annotation
Instead of use #RequestBody use #RequestPart.

Spring MVC default MIME type when multiple endpoints are mapped to the same path

I have two endpoints in a controller mapped to the same path (the controller's root path) with different MIME types.
#RequestMapping(method = RequestMethod.GET, produces = {"application/a+json"})
public ResponseEntity<URI> methodA() {
}
#RequestMapping(method = RequestMethod.GET, produces = {"application/b+json"})
public ResponseEntity<URI> methodB() {
}
When no Accept header is sent with the request, the response is always of type application/a+json.
How is spring-mvc choosing that by default? After some trials, my observation is that it's being chosen based on the alphabetical order (MIME type starting with a vs starting with b), but I didn't find any documentation around that. Is that how it works?
Even if you are seeing some order, I would advise to not rely on it.
Instead define another method that does not declare producesand in this way you will know for sure that Accept was not sent - instead of :
was in sent with application/a+json or not sent at all.
This 3-rd method could do nothing really, it could just delegate to whatever you already have, initially logging the request for example.

How to sign JSON message used by Spring Rest controller?

I have a Spring REST application that accepts JSON messages, written like
#RequestMapping(value = "/myhook", method = RequestMethod.POST,
produces = JSON, consumes = JSON)
public #ResponseBody MyResponse doIt
(#Valid #RequestBody(required = true) MyContractRequest request) {
MyResponse response;
...
return response;
}
This works really well with almost no code to support, but now I have a requirement to sign both response and request.
I started from simply computing the shared signature of all message fields at Java level and assigning it to the dedicated signature field. However this requires to have and maintain code for computing the signatures:
public void update(java.security.Signature sign) throws Exception {
sign.update(name);
sign.update(value);
sign.update(etc);
}
Some people around me expressed opinion that the need to write and maintain this signing code may not be the best design, and it may be better to sign the whole message as a single JSON string. I could fetch the request as a string manually, and then process JSON manually, but I really would like to preserve the Spring controller concepts.
Also, I cannot longer have the signature field in the message itself because the value of this field obviously also changes the signature of the JSON string.
Is there any way to compute the signature of the whole JSON message body on the message departure and arrival, and where to place the signature so it could be passed together with the message? One of the idea is to use the custom HTTP header for the signature. Anyway, how to compute it first?
You can use a servlet filter with Spring MVC and modified your content whatever you want in request and response as well
Example :
http://www.mkyong.com/spring-mvc/how-to-register-a-servlet-filter-in-spring-mvc/
or you can use Spring 3 MVC Interceptor
http://viralpatel.net/blogs/spring-mvc-interceptor-example/

Why is my Spring service returning any content type requested by client?

I have a Spring rest service using Spring 3.1.0.RELEASE. Here is the relevant code for the service call in question:
#RequestMapping(value="/{var1}", method=RequestMethod.GET, produces="application/json")
#ResponseBody
public String getSomeStuff(#PathVariable final String var1) {
return myJsonString;
}
If I call this using the following curl command, it happily returns me my json string with a content-type of application/xml whereas I would expect a 406 based on the Spring 3.1 docs:
curl -v -H "Accept: application/xml" http://localhost:8080/MyServiceSite/myvalue
There is no extra configuration in my app for this service (no serialization), I am returning raw json with no post-processing for the service configured. I'm certain I have missed something, can anyone point out anything I may have missed?
Edit: Here is the documentation I was looking at when attempting to get this working. Specifically section 16.3.2.5. My code is very similar except that their code looks like it assumes config setup to let Spring handle serialization. Perhaps the produces does not work when bypassing the Spring serialization?
Edit: I changed my expectation for the response code. A 415 would indicate I was sending improper content in my request body whereas 406 is proper for having an accept header that doesn't jive with the content type of the server.
Anyway, I have changed this method do return a Map and added config for it to serialize to json and now if I send an invalid content type from the client I get the proper 406 response. It seems that maybe the "produces" setting is ignored when the output of the method is not being serialized.
The produces condition is new to Spring MVC 3.1 and is only supported with the RequestMappingHandlerMapping and related #MVC support classes, also new in Spring 3.1. My guess is that you're using the 3.0 #MVC support classes, which do not support the produces condition. Your code otherwise is correct and so are your expectations of what should happen.
The use of headers="Accept=application/json" is unnecessary in 3.1. That's exactly what the produces condition was introduced for.
What about the headers attribute for the #RequestMapping. You could set the Accept header in there. Something like:
#RequestMapping(value="/{var1}", method=RequestMethod.GET, produces="application/json", headers = "Accept=application/json")
#ResponseBody
public String getSomeStuff(#PathVariable final String var1) {
return myJsonString;
}
I don't know how Spring would handle a request to that path without a matching header. If it doesn't give what you want you might need to define a similar mapping without the headers and have it send back a ResponseEntity and set the response code or something, but I would hope it would handle it appropriately.

Spring and ExtJS "400 Bad Request" with PUT but not with POST

I'm trying to send parameters with PUT from JavaScript to a Spring application. Here is the #RequestMapping in a Spring Controller:
#RequestMapping(value = "toggle-paid-action", method = RequestMethod.PUT)
#ResponseBody
public final String togglePaid(#RequestParam final int year,
#RequestParam final String docType, #RequestParam final int number) {
And here the JavaScript snippet that is supposed to send those parameters.
Ext.Ajax.request({
params: {year: year, docType: docType, number: number},
url: 'toggle-paid-action',
method: 'PUT',
However, I get a "400 Bad Request" every time with description "The request sent by the client was syntactically incorrect ()".
If I check with Firebug, there is a PUT tab with all my parameters, and the parameters are correctly spelled since if I switch from PUT to POST on both sides everything works.
I was wondering what could be the problem, is PUT limited to #PathVariable parameters, or it can send also POST-like parameters?
I suppose you can't pass parameters to spring using request method PUT as there is a restriction in the servlet API. You can only work with PUT methods implementing a restful service, passing data as the request body, in other cases (like Spring MVC Databinding) PUT won't work.
see SpringMVC is not recognizing request body parameters if using PUT
JIRA:
https://jira.springsource.org/browse/SPR-7414
This, as suggest above, seems to be a bug in spring/servlet API. In reality PUT requests are supposed to work on Request Body (or payload) and not on Request Parameters. In that sense, servlet API & spring's handling is correct.
Having said that, a better and much easier workaround is to pass no data element from your javascript/jQuery call and pass your parameters as part of the url itself. meaning, set parameters in the url field the way you would do in a GET call.
$.ajax({
url: "yoururl" + "?param1=param2Val&..",
type: "PUT",
data: "",
success: function(response) {
// ....
}
});
now this works for simple parameters, i guess, will not work for complex JSON types. Hope this helps.

Categories