When creating or listing and item using REST api I return also the whole resource path.
For example creating a person record returns http://service:9000/person/1234in response. In order to get schema, host & port part like http://service:9000, I extract it from URL obtained by HttpServletRequest.getRequestURL().
Example (not the production code but conceptually same):
#RequestMapping(value = "/person", method = RequestMethod.PUT)
public Object putPerson(
#RequestParam(value = "name") String name,
HttpServletRequest req) {
long id = createPerson(name);
String uriStart = RestUtils.getSchemeHostPortUrlPart(req
.getRequestURL().toString());
String uri = uriStart + "/person/" + id;
Person person = new Person(name, id, uri);
return person; //Serialized to json by Spring & Jackson
}
//Simple bean-like class
public class Person {
//Getter methods for uri, name & id
}
Since this is quite a boiler plate code which repeats in every method I was wondering if Spring does not have support for this which eluded me when reading it's documentation.
By this I mean accessing either URL without neededn HttpServletRequest or even better its schema, host, port part only.
The documentation provides a lot of examples for constructing URIs using a UriComponentsBuilder.
Furthermore I recommend to take a look at Spring HATEOAS if you want to take your REST API to the next level.
BTW: PUT means that you place what you send (request body) to the location to which you send it (URL). If nothing is there something new is created, otherwise what exists is updated (replaced).
This is not what is happening in your example. The proper way would be to either POST to /person or PUT to the person's own URL, e.g. /person/1234, provided you have the ID beforehand.
You can construct the URI in an interceptor (that's executed previous to controller methods) and put it as an attribute and use it in the controller method.
I believe it is quite simple. Look at this example:
#RequestMapping(value = "/person", method = RequestMethod.PUT)
public Object putPerson(#RequestParam(value = "name") String name, HttpServletRequest req) {
long id = createPerson(name);
Person person = new Person(id, name, req);
return person; //Serialized to json by Spring & Jackson
}
public class JsonResponse {
private String url;
public JsonResponse(HttpServletRequest request) {
url = request.getRequestURI() + "?" + request.getQueryString();
}
public final String url() {
return url;
}
}
public class User extends JsonResponse {
private Long id;
private String name;
public User(Long id, String name, HttpServletRequest request) {
super(request);
this.id = id;
this.name = name;
}
// Getter, Setter
}
You can use org.springframework.web.servlet.support.ServletUriComponentsBuilder like this:
String uri = ServletUriComponentsBuilder.fromCurrentRequest()
.replacePath("/person/{id}")
.buildAndExpand(id)
.toUriString();
Related
I have a condition where I want to use the same method to be called for request coming from mobile app and request coming from webapp with different clientId and different Client secret and redirect uris,base uris .
When a request comes from mob we have header "source" value as mobile and when request comes from web we have header "source" value as web.
I want to have something like that when request comes from web ,values of client id,clientsecret,baseurl,redirecturl of web is loaded and when request comes from mobile
values of client id,clientsecret,baseurl,redirecturl of mobile is loaded.
I want to avoid to write the same logic either by creating in different method or different endpoint for mobile/web.I wan to use the same method and the same endpoint ,just that values injected from conifguration will be different based on the header value
How can I achieve this??Is there a way to #Autowire different config class based on condition
TokenController
public class TokenController {
#GetMapping("/getToken")
public ResponseEntity<?> fetchToken(#RequestParam("foo") String foo) {
ResponseEntity<?> tokenInfo = tokenInfoServiceImpl.getTokenDetails(foo);
return tokenInfo;
}}
TokenServiceImpl
public class TokenServiceImpl{
#Autowired
SSOConfig ssoConfig;
public ResponseEntity<?> getTokenDetails(String foo) {
HttpHeaders tokenExchangeHeaders = prepareRestTemplateHeader(ssoConfig.getClientId(),
ssoConfig.getClientSecret());
String baseUrl =soConfig.getBaseurl();
String redirectUrl = ssoConfig.getRedirectUrl();
//rest of the logic
}
}
SSOConfig class
#Configuration
#ConfigurationProperties(prefix = "sso.web")
public class SSOConfig {
private String baseurl;
private String redirectUrl;
private String tokenClientId;
private String tokenClientSecret;
//Getters and Setters
}
For your TokenController class, you should be able to add an argument to process a RequestHeader.
public class TokenController {
#GetMapping("/getToken")
public ResponseEntity<?> fetchToken(#RequestHeader(name = "source") String source, #RequestParam("foo") String foo) {
ResponseEntity<?> tokenInfo = null;
if ("mobile".equals(source)) {
ResponseEntity<?> tokenInfo = tokenInfoServiceImpl.getTokenDetails(foo);
} else {
//do something else
}
return tokenInfo;
}}
There's a great tutorial on this at Baeldung
in the below example, i am using POST and GET methods. post is to initialize a varibale and GET is to get this varibale.
I use Postman to make the requests.
i am receiving an error
#RequestBody(value = "val") //cant resolve method value
please let me know how to fix the belwo error so i can use post method for initialization and get methdo to retrieve the value
Controller1
#Controller
#RequestMapping("/call1")
public class Call1 {
public String str = "inti";
#RequestMapping(value = "/intiparam1", method = RequestMethod.POST)
public void intiParam1(#RequestBody(value = "val") String val) {
this.str = val;
}
#RequestMapping(value = "/getparam1", method = RequestMethod.GET)
public String getParam1() {
return this.str;
}
}
Create a class Variable and use other code in controller.
class Variable {
String data= 'val';
}
#RequestMapping(value = "/intiparam1", method = RequestMethod.POST)
public void intiParam1(#RequestBody Variable val) {
this.str = val.data;
}
When making a request pass json as {"data":"12345"}
and then use #RequestBody Variable v in code instead of String as it will serve your purpose of default value and will make the code extensible as you can add different properties to the existing variable in future if needed.
When to use #RequestBody?
You can not use value with it. You can use this when you have multiple field entity which you want to perform the operation. Let's say you want to save the user then you may need to create User Model first and use in a controller #RequestBody.
Model:
public class User
{
#Id
private int id;
private String firstname;
private String lastname;
//Getters-Setters, AllArgConstructor-constructor
}
#RequestMapping(value = "/requestBodyExample", method = RequestMethod.POST)
public String intiParam1(#RequestBody User user) {
return user.getFirstname();
}
Quick Start with Spring Boot
I need to have my Java server receive a PUT request to create a new user from an id and a json body, the URI needs to be like:
/usermanagement/user/$id { "name":john, "type":admin }
Given that I've made a simple Java class and can later convert the JSON to a POJO using Jackson, here's my problem:
How do I specify the PUT request to accept both the id and the JSON body as parameters? So far I've got:
#PUT
#Path("{id}")
#Consumes(MediaType.APPLICATION_JSON)
public String createUser(#PathParam("id") int id){
User user = new User();
User.setId(id);
return SUCCESS_MSG;
}
And this works, but I've had no luck adding the JSON body and having the function parse it. I've tried:
public String createUser(#PathParam("id") int id, String body){
return body;
}
It should return the same input JSON when testing in Postman, however it always returns a "resource not available" error.
I feel there's something obvious that I'm missing here?
As per REST API conventions, a POST method on a uri like /usermanagement/users is what is needed. PUT method is used for updating an existing resource. You can go through this wonderful article on how to design pragmatic RESTful API. http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api.
If you are trying to create a new user, why give it an ID? You have to POST the data such as user name, lastname, email, ... and let the backend generate an ID (like an auto-incremented id, or some UUUID) for this new resource.
For example, in my app, I use a json body for a POST request like below:
{
"loginId": "ravi.sharma",
"firstName": "Ravi",
"lastName": "Sharma",
"email": "myemail#email.com",
"contactNo": "919100000001",
"..." : ".."
}
Moreover, your response should return HTTP-201, after successful creation, and it should contain a location header, pointing to the newly created resource.
Instead of Using String body, use a Class with Member variables name and Type Like this.
public class User {
private String name;
private String type;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
(This works in Spring Boot Web out-of-box. incase of Spring MVC, you might need to add Jackson dependency): On your Controller , Add #RequestBody Annotation, then Jackson will take care of the un-marshaling of JSON String to User Object.
public String createUser(#PathParam("id") int id, #RequestBody User user){
I have a spring web service method where i want to get a string as a parameter. The string is sent in body of the request. My web service class is:
#Controller
#RequestMapping(value = "/users/{uid}/openchart")
public class OpenChartWebService {
#RequestMapping(method = RequestMethod.POST)
#ResponseBody
public String saveABC(#PathVariable("uid") Long uid,
#RequestBody String myString) {
System.out.println("sent string is: "+myString);
return "something";
}
}
My request in body is :
{
"name":"Ramesh"
}
But this is not working. This shows "Bad Request" HTTP error(400). How to send a string in a body and how to get a string sent in a body inside webservice method?
As #Leon suggests, you should add the media type to your request mapping, but also make sure you have Jackson on your classpath. You'll also want to change your #RequestBody argument type to something that has a "name" property, rather than just a String so that you don't have to convert it after.
public class Person {
private name;
public getName() {
return name;
}
}
If your data object looked like the above, then you could set your #RequestBody argument to Person type.
If all you want is a String, then perhaps just pass the value of "name" in your request body rather than an object with a name property.
I am new to the Dropwizard framework. I am trying to work on creating a new resource similar to person and people resource mentioned in the tutorial here https://github.com/dropwizard/dropwizard/tree/master/dropwizard-example.
I am creating a document class like this -
#Entity
#Table(name = "document")
#NamedQueries({
#NamedQuery(
name = "com.example.helloworld.core.Document.findAll",
query = "SELECT d FROM Document d"
),
#NamedQuery(
name = "com.example.helloworld.core.Document.findById",
query = "SELECT d FROM Document d WHERE d.Id = :Id"
)
})
public class Document {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long Id;
#Column(name = "ProcessingSetID")
private String ProcessingSetID;
#Column(name = "processed")
private String processed;
public long getId() {
return Id;
}
public void setId(long id) {
this.Id = id;
}
public String getProcessingSetID() {
return ProcessingSetID;
}
public void setProcessingSetID(String processingSetID) {
ProcessingSetID = processingSetID;
}
public String getProcessed() {
return processed;
}
public void setProcessed(String processed) {
this.processed = processed;
}
}
My document Dao is like this,
public Optional<Document> findById(Long id) {
return Optional.fromNullable(get(id));
}
public Document create(Document document) {
return persist(document);
}
public List<Document> findAll() {
return list(namedQuery("com.example.helloworld.core.Document.findAll"));
}
}
I am trying to call the POST method on my document resource,
#Path("/documents")
#Produces(MediaType.APPLICATION_JSON)
public class DocumentsResource {
private final DocumentDao documentDAO;
private static final Logger log = LoggerFactory.getLogger(DocumentsResource.class);
public DocumentsResource(DocumentDao documentDAO) {
this.documentDAO = documentDAO;
}
#POST
#UnitOfWork
public Document createDocument(Document document) {
log.info("inside POST method of document.");
System.out.println("inside POST method of document.....");
return documentDAO.create(document);
}
#GET
#UnitOfWork
public List<Document> listDocuments() {
return documentDAO.findAll();
}
}
But I am getting a 400 response back from my client request, please find below the client request
Client client = Client.create();
WebResource webResource = client.resource("http://localhost:8080/documents");
String input = "{\"processed\":\"new process\",\"ProcessingSetID\":\"new iD\"}";
ClientResponse response =
webResource.type("application/json").post(ClientResponse.class, input);
if (response.getStatus() != 200) {
throw new RuntimeException("Failed : HTTP error code : "
+ response.getStatus());
}
I tried to debug the problem, but the call is not reaching the POST method at the first place. It seems that it is not creating the document object from the JSON string, but i could not see a reason for that. Also when I do an entry directly in my database and make a GET call, perfect JSON string equivalent to object is received.
To get helpful message regarding 400 error, register this on jersey:
environment.jersey().register(new JsonProcessingExceptionMapper(true));
It will give more detailed message on 400 response, useful for debugging.
A little background: Dropwizard utilizes Jersey, and Jersey is what ultimately gives you back the 400 Bad Request response, probably along with a vague and laconic message.
In order to see exactly what did bother Jackson (which in turn bothered Jersey), I started out by sending a blank (empty) JSON object and see whether it was accepted (it did - and all the fields in the POJO where zero-initialized). Then I started to add fields, sending each such object along, until I reached the problematic field (in my case it was a boolean field which should have been a Boolean).
I think I can spot two difficulties in your POJO (the Document class):
The getters/setters should be annotated with #JsonProperty.
Try to change Id's type to Long (nullable long). If you are concerned about getting a null in that field, you can have the getter return a zero or any default value instead.
I faced the same issue. The errors are suppressed and not passed properly in the stack trace.
What I did was to add a try catch around the function. Then added a debugger point in the exception. I was able to figure out the exact reason.
You could try something like this.
#POST
#UnitOfWork
public Document createDocument(Document document) throws Exception{
....
}
Add debugger points in the Exception class. You will find out the exact reason of the parsing failure.
Hope I am clear and it helps!
Http status 400 means "bad request". Which it is, the json you are sending is not a valid Document.
This in turn means you will never reach the body of
#POST
#UnitOfWork
public Document createDocument(Document document){}
To solve it, try passing the json:
String input = "{\"id\":\"123456789\",\"processed\":\"new process\",\"ProcessingSetID\":\"new iD\"}";
Replace 123456789 with your actual id.
PS. it might be a good idea (depending on your scenario) to create a DTO for Document instead of passing the actual entity around.
If you are registering the Jersey's CsrfProtectionFilter in your Dropwizard *Application.java within the run(...) method, make sure you're adding the X-Requested-By header to all of your state changing HTTP calls (POST, PUT, etc). The server will return an HTTP 400 Bad Request if that header is not found in the request.