POST request to Spring REST web service fails with HTTP status 415 - spring

I have set up a spring RESTful web service for taking in the username and password from a user. Been following the tutorial on Spring IO
My service is set up to accept user name and password as shown below:
#Controller
#RequestMapping("/users")
public class UserCommandController {
private static Logger log = LoggerFactory.getLogger(UserCommandController.class);
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity createUser(#RequestBody UserDetail userDetail, UriComponentsBuilder builder) {
User newUser = new User();
newUser.setEmail(userDetail.getEmail());
newUser.setPassword(userDetail.getPassword());
newUser.setUserName(userDetail.getUsername());
try {
UserFactory.getInstance().saveNewUser(newUser);
} catch(UserException ue) {
log.error("Saving user failed. Exception: "+ue.getMessage());
}
return new ResponseEntity(HttpStatus.OK);
}
}
I am sending POST parameters to the service as a test through Google chrome plugin POSTMAN but I get "HTTP: 415..The server refused this request because the request entity is in a format not supported by the requested resource for the requested method."
Does anyone have an idea what I am doing wrong ?

Set the header:
Content-Type=application/json
This solved my problem!

The HTTP 415 response code means that the server expected data posted with a different content type. It appears you simply posted a form with username, password and email as parameters. That would result in a content-type of application/x-www-form-urlencoded.
Try posting with a content-type of application/xml or application/json. In your post body, you will need to put your data in the corresponding format. For example, if you use application.xml, the XML body should look something like:
<userDetail>
<userName>xxx</userName>
<password>xxx</password>
<email>xxx</email>
</userDatail>
Of course the exact format (i.e. element names) depends on the XML bindings. In fact, whether or not the expected format is XML or JSON or something else is also likely a server configuration.
Posting a request of this type cannot easily be done with a browser. You will need some other HTTP client. A tool like SOAP-UI might be a good bet.

Related

Best Way to encode Fragment in URL - SpringBoot

I have a spring boot application where an endpoint responds with a url for the client to redirect to. This correct url looks something like:
https://checkout.stripe.com/c/pay/stuff#morestuff which is being properly logged below
Here is my spring boot code:
#PostMapping(path = "/create-checkout-session", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Void> createSubscription(#RequestParam String priceId) throws StripeException {
...
Session session = Session.create(params);
log.info("Redirecting with session {}", session.getUrl());
return ResponseEntity.status(HttpStatus.FOUND).location(URI.create(session.getUrl())).build();
}
However, when my client receives a response from my endpoint, the URL is truncated up to the # to something like:https://checkout.stripe.com/c/pay/stuff (removing the #morestuff).
I found this post about needing to encode the # and I was wondering what the best way to do this is?

Restrict API access by domain name [duplicate]

I have one war file for my application and I will be using 2 domains to access it. For example I want to access admin.jsp using admin.mydomain.com/adminpage and other jsp pages I want to access with local.mydomain.com.
Also, admin.jsp should be only accessible via admin.mydomain.com and not via local.mydomain.com. How to do this in spring-security / spring-mvc? Is there a support in spring framework for this?
Any help on this would be helpful. Thanks.
You can implement RequestMatcher, and maybe like
HostOnlyRequestMatch(String relativePath, String hostname)
and then override the boolean matches(HttpServletRequest request) method, and if the relativePath and hostname are same with request, return true.
Add the requestMatcher to http like this:
http
.authorizeRequests()
.requestMatcher(new HostOnlyRequestMatch("/admin", "admin.mydomain.com")).permitAll()
.antMatchers("/admin").denyAll();
One way would be to configure proxy (e.g. Nginx) to route your requests to your application server (e.g Tomcat) properly. Read here for more details https://www.nginx.com/resources/admin-guide/reverse-proxy/
You can get the requested url from request object in you mvc controller and if it is not form correct domain then you can throw or show proper error based on your project. Following is the code snippet
#Controller
#RequestMapping("/adminpage")
public class AdminPageController{
#RequestMapping(method = RequestMethod.GET)
public String getAdminPage(HttpServletRequest request) {
String url = request.getRequestURL().toString();
if(!url.contains("admin.mydomain.com")) {
throw RuntimeException("Not accessible through this domain.");
// You can implement your own logic of showing error here
}
}
}

Getting 403 forbidden error using #PostMapping via rest api in spring boot project

I am getting 403 forbidden error on postman while accessing #PostMapping. However, #GetMapping is working fine with basic authentication
#PostMapping(value = { "/version" })
#ApiOperation(value = "Set Version")
#Monitor(useCase = "setApplicationVersion")
public void setApplicationVersion() {
System.out.println("Hey postman");
}
This is my #PostMapping method. Suggest something for the issue
Please post screenshot of what kind of request is being sent via postman and the type of content we are sending as same needs to be set at the controller level.
check if content type is set to application/json in the controller and when sending request from postman.

Diffrence b/w #ResponseStatus and ResponseEntity.created(location).build() in post method of rest controller in spring boot

For a POST method in Rest controller I want to return status code 201.
I saw two approaches for that.
First one is:
#PostMapping("/offers")
#ResponseStatus(HttpStatus.CREATED)
public Offer createOffer(#Valid #RequestBody Offer offer) {
return offerRepository.Save(offer);
}
Second approach is:
#PostMapping("/offers")
public ResponseEntity<Object> createOffer(#Valid #RequestBody Offer offer) {
return offerService.createOffer(offer);
}
Below is my service class:
#Override
public ResponseEntity<Object> createOffer(Offer offer) {
Offer uOffer=OfferRepository.save(offer);
URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{jobTitle}").
buildAndExpand(uOffer.getJobTitle()).toUri();
return ResponseEntity.created(location).build();
}
So my question is: for first approach we are not using any ResponseEntity.created as we are simply returning #ResponseStatus(HttpStatus.CREATED) from controller. But in the second we are not using #ResponseStatus(HttpStatus.CREATED) and we are handling that status code 201 by using uri and response entity.
What is the difference b/w the both approaches? Both seems to be same as they are returning the same response code 201. which one is preferred?
In my opinion you should apply the following rules. If you want to return a ResponseEntity then use that to affect the status. Thus something like:
#PostMapping("/offers")
public ResponseEntity<Offer> createOffer(#Valid #RequestBody Offer offer) {
Offer offer = offerService.createOffer(offer);
URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{jobTitle}").
buildAndExpand(uOffer.getJobTitle()).toUri();
return ResponseEntity.created(location)
.body(offer)
.build();
}
Do not allow your service to generate the ResponseEntity as this is a view class for controllers and should not be in a service.
The second option is by using the class rather then response entity. Then the example would be something like:
#PostMapping("/offers")
#ResponseStatus(HttpStatus.CREATED)
public Offer createOffer(#Valid #RequestBody Offer offer) {
// Do not return response entity but the offer
return offerService.createOffer(offer);
}
There is no difference in general when it comes to status codes. In the end, you still get an HTTP response with 201 status code.
However, in second approach you're also returning a Location header which is a preferred way to do. From Mozilla's HTTP guide:
The HTTP 201 Created success status response code indicates that the request has succeeded and has led to the creation of a resource. The new resource is effectively created before this response is sent back and the new resource is returned in the body of the message, its location being either the URL of the request, or the content of the Location header.
The first approach is the preferred one, since it allows you to keep your service layer decoupled from your web layer (service layer should not know about HttpEntity and all that stuff, so it could potentially be reused without the web layer).
You should refactor you service method to return Object instead of ResponseEntity<Object>.
What is the difference b/w the both approaches?
Using return ResponseEntity.created(location).build(); adds the Location header to the response.
Is also recommended to return the new resource in the body of the response.
201 Created
The HTTP 201 Created success status response code indicates that the request has succeeded and has led to the creation of a resource. The new resource is effectively created before this response is sent back and the new resource is returned in the body of the message, its location being either the URL of the request, or the content of the Location header.
Thus the best option would be:
ResponseEntity.created(location).body(uOffer);

How to filter request based on domain in spring-mvc

I have one war file for my application and I will be using 2 domains to access it. For example I want to access admin.jsp using admin.mydomain.com/adminpage and other jsp pages I want to access with local.mydomain.com.
Also, admin.jsp should be only accessible via admin.mydomain.com and not via local.mydomain.com. How to do this in spring-security / spring-mvc? Is there a support in spring framework for this?
Any help on this would be helpful. Thanks.
You can implement RequestMatcher, and maybe like
HostOnlyRequestMatch(String relativePath, String hostname)
and then override the boolean matches(HttpServletRequest request) method, and if the relativePath and hostname are same with request, return true.
Add the requestMatcher to http like this:
http
.authorizeRequests()
.requestMatcher(new HostOnlyRequestMatch("/admin", "admin.mydomain.com")).permitAll()
.antMatchers("/admin").denyAll();
One way would be to configure proxy (e.g. Nginx) to route your requests to your application server (e.g Tomcat) properly. Read here for more details https://www.nginx.com/resources/admin-guide/reverse-proxy/
You can get the requested url from request object in you mvc controller and if it is not form correct domain then you can throw or show proper error based on your project. Following is the code snippet
#Controller
#RequestMapping("/adminpage")
public class AdminPageController{
#RequestMapping(method = RequestMethod.GET)
public String getAdminPage(HttpServletRequest request) {
String url = request.getRequestURL().toString();
if(!url.contains("admin.mydomain.com")) {
throw RuntimeException("Not accessible through this domain.");
// You can implement your own logic of showing error here
}
}
}

Resources