I an creating an endpoint with spring boot...i can upload image to folder and save it via postman everythink works good.
i have a problem with get method when i am adding the value #RequestMapping value = "getImage/{imageName:.+}" in postman i add http://localhost:8080/api/images/getImage/{burger+png}
is that corect ???
#RequestMapping(value = "api/images")
public class ImageController {
#Autowired
public ImageService imageService;
#PostMapping(value ="upload")
public ResponseEntity uploadImage(#RequestParam MultipartFile file){
return this.imageService.uploadToLocalFileSystem(file);
}
#GetMapping(
value = "getImage/{imageName:.+}",
produces = {MediaType.IMAGE_JPEG_VALUE,MediaType.IMAGE_GIF_VALUE,MediaType.IMAGE_PNG_VALUE}
)
public #ResponseBody byte[] getImageWithMediaType(#PathVariable(name = "imageName") String fileName) throws IOException {
return this.imageService.getImageWithMediaType(fileName);
}
}
what should be the correct request url ???
It seems like it's reaching the backend fine, but failing to find path. Usually API endpoints end with parameters with a slug or query param. You can try either of the following to see if it works:
http://localhost:8080/api/images/getImage/burger.png
http://localhost:8080/api/images/getImage?imageName=burger.png
Keep in mind, you want to make sure that file exists at the path it's mentioning at the very top of the trace in the JSON response. This may depend on how you uploaded the file and with what name.
I need to serve multiple file types using same endpoint ( zip,pdf,xml).
I needed to add error handling to those endpoints so in case of error they should return json (using controller advice) to indicate problem to user.
For example:
#GetMapping(value = "api/books", produces = {applicaton/zip, application/json}
public ResponseEntity<byte[]> getZipedBooks(){...}
#GetMapping(value = "api/books", produces = {applicaton/pdf, application/json}
public ResponseEntity<byte[]> getPdfBooks()(...}
Without application/json Spring was able to differentiate between those endpoints and call correct one based on accept header. But when I added json Spring is now throwing exception:
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
even if it can be deduced from accept: application/json,application/pdf header that getPdfBooks should be called.
Is there any way to configure spring to work with multiple content types on the same endpoint or I need to make special endpoints for every file type ?
I would reconsider this approach. If you want to return JSON on error do with exception handling
So that you would add something like
private class ErrorResponse {
String message;
public ErrorResponse(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
#ExceptionHandler(value = Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception e) {
return new ResponseEntity<ErrorResponse>(new ErrorResponse(e.getMessage()), HttpStatus.BAD_REQUEST);
}
Hi friends I am using Angularjs and rest-servies but when I am calling rest services from service.js file something is goning wrong and it is throwing 400(bad request )
main.js
garantiesService.getTarifs($scope.recap.ageDirigeant,$scope.selectedCompany.zipcode)
.success(function(){
console.log('in success');
})
service.js
healthApp.factory('garantiesService', ['$http', function($http) {
var service = {
getTarifs: function(age,zipcode)
{
console.log("age : "+age);
console.log("zipcode : "+zipcode);
var directorHealthInsuranceInfo = {};
directorHealthInsuranceInfo.age=age;
directorHealthInsuranceInfo.department=zipcode;
return $http.post('rest-service/quotes/health /director',directorHealthInsuranceInfo);
}
};
return service;
HealthInsuranceController.java
#Controller
public class HealthInsuranceQuoteResource {
#RequestMapping("quotes/health/director")
#ResponseBody
public String quoteDirector(#RequestBody DirectorHealthInsuranceInfo info) {
System.out.println("------HealthInsuranceQuoteResult------");
return "hi";
}
DirectorHealthInsuranceInfo.java
#Value
public class DirectorHealthInsuranceInfo {
private String department;
private int age;
}
when I am sending the request it is throwing Bad Request 400 error.
I see that there is a space in the url you supplied to the http.post method.
"rest-service/quotes/health /director"
I don't know if that is causing it.
But I also see that you POST your request to the service. Are you sure that your endpoint has been set up for POST requests?
I would recommend creating a basic endpoint that you call with a GET request, and no parameters. Just to root out the problem.
I'm using the following exception handler in Spring 4.0.3 to intercept exceptions and display a custom error page to the user:
#ControllerAdvice
public class ExceptionHandlerController
{
#ExceptionHandler(value = Exception.class)
public ModelAndView handleError(HttpServletRequest request, Exception e)
{
ModelAndView mav = new ModelAndView("/errors/500"));
mav.addObject("exception", e);
return mav;
}
}
But now I want a different handling for JSON requests so I get JSON error responses for this kind of requests when an exception occurred. Currently the above code is also triggered by JSON requests (Using an Accept: application/json header) and the JavaScript client doesn't like the HTML response.
How can I handle exceptions differently for HTML and JSON requests?
The ControllerAdvice annotation has an element/attribute called basePackage which can be set to determine which packages it should scan for Controllers and apply the advices. So, what you can do is to separate those Controllers handling normal requests and those handling AJAX requests into different packages then write 2 Exception Handling Controllers with appropriate ControllerAdvice annotations. For example:
#ControllerAdvice("com.acme.webapp.ajaxcontrollers")
public class AjaxExceptionHandlingController {
...
#ControllerAdvice("com.acme.webapp.controllers")
public class ExceptionHandlingController {
The best way to do this (especially in servlet 3) is to register an error page with the container, and use that to call a Spring #Controller. That way you get to handle different response types in a standard Spring MVC way (e.g. using #RequestMapping with produces=... for your machine clients).
I see from your other question that you are using Spring Boot. If you upgrade to a snapshot (1.1 or better in other words) you get this behaviour out of the box (see BasicErrorController). If you want to override it you just need to map the /error path to your own #Controller.
As you have the HttpServletRequest, you should be able to get the request "Accept" header. Then you could process the exception based on it.
Something like:
String header = request.getHeader("Accept");
if(header != null && header.equals("application/json")) {
// Process JSON exception
} else {
ModelAndView mav = new ModelAndView("/errors/500"));
mav.addObject("exception", e);
return mav;
}
Since i didn't find any solution for this, i wrote some code that manually checks the accept header of the request to determine the format. I then check if the user is logged in and either send the complete stacktrace if he is or a short error message.
I use ResponseEntity to be able to return both JSON or HTML like here.
Code:
#ExceptionHandler(Exception.class)
public ResponseEntity<?> handleExceptions(Exception ex, HttpServletRequest request) throws Exception {
final HttpHeaders headers = new HttpHeaders();
Object answer; // String if HTML, any object if JSON
if(jsonHasPriority(request.getHeader("accept"))) {
logger.info("Returning exception to client as json object");
headers.setContentType(MediaType.APPLICATION_JSON);
answer = errorJson(ex, isUserLoggedIn());
} else {
logger.info("Returning exception to client as html page");
headers.setContentType(MediaType.TEXT_HTML);
answer = errorHtml(ex, isUserLoggedIn());
}
final HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
return new ResponseEntity<>(answer, headers, status);
}
private String errorHtml(Exception e, boolean isUserLoggedIn) {
String error = // html code with exception information here
return error;
}
private Object errorJson(Exception e, boolean isUserLoggedIn) {
// return error wrapper object which will be converted to json
return null;
}
/**
* #param acceptString - HTTP accept header field, format according to HTTP spec:
* "mime1;quality1,mime2;quality2,mime3,mime4,..." (quality is optional)
* #return true only if json is the MIME type with highest quality of all specified MIME types.
*/
private boolean jsonHasPriority(String acceptString) {
if (acceptString != null) {
final String[] mimes = acceptString.split(",");
Arrays.sort(mimes, new MimeQualityComparator());
final String firstMime = mimes[0].split(";")[0];
return firstMime.equals("application/json");
}
return false;
}
private static class MimeQualityComparator implements Comparator<String> {
#Override
public int compare(String mime1, String mime2) {
final double m1Quality = getQualityofMime(mime1);
final double m2Quality = getQualityofMime(mime2);
return Double.compare(m1Quality, m2Quality) * -1;
}
}
/**
* #param mimeAndQuality - "mime;quality" pair from the accept header of a HTTP request,
* according to HTTP spec (missing mimeQuality means quality = 1).
* #return quality of this pair according to HTTP spec.
*/
private static Double getQualityofMime(String mimeAndQuality) {
//split off quality factor
final String[] mime = mimeAndQuality.split(";");
if (mime.length <= 1) {
return 1.0;
} else {
final String quality = mime[1].split("=")[1];
return Double.parseDouble(quality);
}
}
The trick is to have a REST controller with two mappings, one of which specifies "text/html" and returns a valid HTML source. The example below, which was tested in Spring Boot 2.0, assumes the existence of a separate template named "error.html".
#RestController
public class CustomErrorController implements ErrorController {
#Autowired
private ErrorAttributes errorAttributes;
private Map<String,Object> getErrorAttributes( HttpServletRequest request ) {
WebRequest webRequest = new ServletWebRequest(request);
boolean includeStacktrace = false;
return errorAttributes.getErrorAttributes(webRequest,includeStacktrace);
}
#GetMapping(value="/error", produces="text/html")
ModelAndView errorHtml(HttpServletRequest request) {
return new ModelAndView("error.html",getErrorAttributes(request));
}
#GetMapping(value="/error")
Map<String,Object> error(HttpServletRequest request) {
return getErrorAttributes(request);
}
#Override public String getErrorPath() { return "/error"; }
}
References
ModelAndView -- return type for HTML
DefaultErrorAttributes -- data used to render HTML template (and JSON response)
BasicErrorController.java -- Spring Boot source from which this example was derived
The controlleradvice annotation has several properties that can be set, since spring 4. You can define multiple controller advices applying different rules.
One property is "annotations. Probably you can use a specific annotation on the json request mapping or you might find another property more usefull?
Use #ControllerAdvice
Let the exception handler send a DTO containing the field errors.
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public ValidationErrorDTO processValidationError(MethodArgumentNotValidException ex) {
BindingResult result = ex.getBindingResult();
List<FieldError> fieldErrors = result.getFieldErrors();
return processFieldErrors(fieldErrors);
}
This code is of this website:http://www.petrikainulainen.net/programming/spring-framework/spring-from-the-trenches-adding-validation-to-a-rest-api/
Look there for more info.
I want to forward a REST request to another server.
I use JAX-RS with Jersey and Tomcat. I tried it with setting the See Other response and adding a Location header, but it's not real forward.
If I use:
request.getRequestDispatcher(url).forward(request, response);
I get:
java.lang.StackOverflowError: If the url is a relative path
java.lang.IllegalArgumentException: Path http://website.com does not start with a / character (I think the forward is only legal in the same servlet context).
How can I forward a request?
Forward
The RequestDispatcher allows you to forward a request from a servlet to another resource on the same server. See this answer for more details.
You can use the JAX-RS Client API and make your resource class play as a proxy to forward a request to a remote server:
#Path("/foo")
public class FooResource {
private Client client;
#PostConstruct
public void init() {
this.client = ClientBuilder.newClient();
}
#POST
#Produces(MediaType.APPLICATION_JSON)
public Response myMethod() {
String entity = client.target("http://example.org")
.path("foo").request()
.post(Entity.json(null), String.class);
return Response.ok(entity).build();
}
#PreDestroy
public void destroy() {
this.client.close();
}
}
Redirect
If a redirect suits you, you can use the Response API:
Response.seeOther(URI): Used in the redirect-after-POST (aka POST/redirect/GET) pattern.
Response.temporaryRedirect(URI): Used for temporary redirection.
See the example:
#Path("/foo")
public class FooResource {
#POST
#Produces(MediaType.APPLICATION_JSON)
public Response myMethod() {
URI uri = // Create your URI
return Response.temporaryRedirect(uri).build();
}
}
It may be worth it to mention that UriInfo can be injected in your resource classes or methods to get some useful information, such as the base URI and the absolute path of the request.
#Context
UriInfo uriInfo;