I am using Spring 4 with mapped methods as follows
#RestController
#RequestMapping("/v3/users")
public class UserController {
...
#RequestMapping(value = "/{userId}/reset_password", method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<Void> resetPassword(
#PathVariable("userId") Long userId, #RequestBody UserPasswordResetRequestDTO data) {
// Logic here
return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
}
}
public class UserPasswordResetRequestDTO {
private Long userId;
private String oldPassword;
private String newPassword;
// Getters and setters
}
then, i do this request:
POST /v3/users/6/reset_password HTTP/1.1
Host: localhost:8080
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: 8afe6ef8-a4cd-fc9d-a6cc-b92766a56bd6
{"oldPassword":"actualPassword", "newPassword":"newOne"}
And all UserPasswordResetRequestDTO attributes are coming null
I've been searching and i find some related problems with the difference that those were PUT methods (then since Spring 3.1 can be done with HttpPutFormContentFilter), but this is a post and i couldn't find any problem... what i am doing wrong?
EDIT
I've tried changing #RequestBody with #ModelAttribute and it just mapped "userId" attribute (coming from url), leaving null the rest of attributes. Just the same as #RequestBody did with the difference of userId
I am really really disconcerted
Finally, i discovered what was going on. I had this jackson config
#Bean
public Jackson2ObjectMapperBuilder jacksonBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.indentOutput(true).dateFormat(new SimpleDateFormat("yyyy-MM-dd"));
builder.propertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
builder.serializationInclusion(Include.NON_NULL);
return builder;
}
which was causing the conversion from camel case to underscores on serialization and deserealization. So there was nothing wrong with code, just an invalid request, which in this case had to be this one:
POST /v3/users/6/reset_password HTTP/1.1
Host: localhost:8080
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: 8afe6ef8-a4cd-fc9d-a6cc-b92766a56bd6
{"old_password":"actualPassword", "new_password":"newOne"}
Related
This question already has answers here:
What does "consume an API" mean?
(6 answers)
Closed last month.
I am aware what an API is but I do not actually understand what it means when we say consumes or produces application/json in the context of a REST API. I have found several sources explaining how to do it but not what it actually is. I am using Java with SpringBoot in IntelliJ, so any examples relevant to that would be greatly appreciated.
I'd like to explain what consume means, it means if you send a POST request with JSON as the content type, your service should be able to accept it and doesn't reject it.
The service sample code is here:
import com.fishpro.restcontroller.domain.UserDO;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
#RestController
#RequestMapping("user2")
public class UserRestController {
#PostMapping("/update")
#ResponseBody
public Object update(#RequestBody User user){
Map<String,Object> map=new HashMap<>();
if(null==user){
map.put("status",3);
map.put("message","Empty Content");
return map;
}
//更新逻辑
map.put("status",0);
return map;
}
}
// The entity class
public class User {
private Integer userId;
private String userName;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
Let's take look at an example(you can use IDEA HTTP Client to execute it https://www.jetbrains.com/help/idea/2022.3/http-client-in-product-code-editor.html#open-requests-collection ) :
Do post to a Restful Service:
POST http://localhost:8087/user2/update, the request:
POST /user2/update HTTP/1.1
Content-Type: application/json
Host: localhost:8087
{
"userId": 1,
"userName": "userName_txxhs"
}
And the response:
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Fri, 13 Jan 2023 04:09:57 GMT
{"status":0}
It works because the HTTP client sends the correct content type and the server accepts it and produces the right one.
So in case we change the service annotation and force the client to use another Content-Type: application/text:
// ...
#PostMapping(value="/update", consumes = {"application/text"}, produces = {"application/json"})
#ResponseBody
public Object update(#ModelAttribute User user){
// ...
Then the response will say that it can't consume (support) this content type:
HTTP/1.1 415
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Fri, 13 Jan 2023 04:41:46 GMT
Connection: close
{"timestamp":"2023-01-13T04:41:46.278+0000","status":415,"error":"Unsupported Media Type","message":"Content type 'application/json' not supported","path":"/user2/update"}
So the request has to change the content type and then the response will work:
POST /user2/update HTTP/1.1
Content-Type: application/text
Host: localhost:8087
Connection: close
User-Agent: RapidAPI/4.1.1 (Macintosh; OS X/13.1.0) GCDHTTPRequest
Content-Length: 50
{
"userId": 1,
"userName": "userName_txxhs"
}
It's much simple for the annotation produces, just simply change the Content-Type header of the response, and yes that your Spring boot application should provide logic to convert any object to that content type.
I hope this will help you to understand it, and you may find the HTTP resources and specifications helpful:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Resources_and_specifications
I have a spring boot application that needs a controller that can handle the following request:
The request is sent by another service through the Post method..
Headers
accept-encoding: gzip,deflate
user-agent: Apache-HttpClient/4.3.6 (java 1.5)
connection: Keep-Alive
host: webhook.site
content-type: application/x-www-form-urlencoded
content-length: 558
Query strings:(empty)
Form values
BillNumber: 41492032464
BillValue: 600000.0
Description: Description
I have this controller, but my application returns an HTTP Error 406:
#RequestMapping(value = "/bills", method = RequestMethod.POST, headers = "Accept=application/x-www-form-urlencoded")
#ResponseBody
#Transactional
public void createBill(UriComponentsBuilder uriComponentsBuilder, final HttpServletRequest request,
final HttpServletResponse response) throws IOException {
}
How should this controller be implemented in my spring boot app?
Many Thanks!
It's not very clear for me but if you use spring boot you can of course create a controller , service and repository or dao.
Indeed, your controller will call your service witch will call the repository.
Let's suppose that you have a client witch call your api.
So the call will look like :
// Suppose that is a spring boot project
Class A {
#Autowired
RestTemplate restTemplate;
public void create(){
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
headers.setContentType((MediaType.APPLICATION_JSON));
headers.add("X-yourCustom-context", "yourCustom");
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(your_service_url)
.queryParam("params1", "value".queryParam("params2", value2));
HttpEntity<?> entity = new HttpEntity<>(headers);
restTemplate.exchange(builder.build().encode().toUri(), HttpMethod.POST, entity, null); // if you want to return an objectr you put it rather than the null
}
}
Th service api :
#RestController
public class YourController {
#Autowired
private YourService service;
#Autowired
private ObjectMapper objectMapper;
#PostMapping(value = "/bills")
//#ResponseBody if you do not return any think you can not use it
// #CrossOrigin if you want to call your reste from an external project like javascript or angular
//#Transactional you can put it on the top of your service methode
public void createBill(#RequestParam(value = "params1", required = true) String params1,
#RequestParam(value = "params2", required = true) String params2,
#RequestHeader(value = "X-yourCustom-context", required = true) String yourContxt) throws IOException {
// You can then convert your x-context to an object that you have already by using objectMapper.readValue
// Finaly you call you service to create the bill and you passe as params what you get fron the client
}
}
I hope it meets your needs :)
I'm struggling to understand why I'm getting the following error when I call my spring boot end point
{
"timestamp": 1489573322678,
"status": 406,
"error": "Not Acceptable",
"exception": "org.springframework.web.HttpMediaTypeNotAcceptableException",
"message": "Could not find acceptable representation",
"path": "/quotes"
}
This is the request that I'm sending to the server
POST /quotes HTTP/1.1
Host: localhost:8080
tamid: 5
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: 94370a3f-6165-106f-f27f-44a44093e0d5
{
"test": "works"
}
I would like the incoming JSON request body to map to a java class I have defined. Here is the class.
#Embedded
public class QuoteVersion {
private String test;
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}
public void validate() {
}
}
I'm using the #Embedded annotation for use with a mongodb mapping library that I'm hoping is unrelated to the issue I'm facing
Here is the controller method
#RequestMapping(
path = "/quotes",
method = RequestMethod.POST,
headers = "Accept=application/json",
produces = "application/json"
)
public #ResponseBody QuoteStatus create (#RequestHeader(value = "tamid") String tamId,
#RequestBody QuoteVersion firstQuoteVersion) {
// final QuoteVersion firstQuoteVersion = this.quoteFactory.createQuoteVersion(incomingQuote);
final User currentUser = User.getFromTamId(tamId);
currentUser.can(Permissions.CREATE_QUOTE);
firstQuoteVersion.validate();
final Quote newQuote = new Quote();
newQuote.addVersion(firstQuoteVersion);
this.dataRepository.save(newQuote);
QuoteStatus qs = new QuoteStatus(newQuote);
return qs;
}
I'm guessing that Spring Boot for some reason does not understand how to map the incoming payload to the class I have defined but I have no idea how to fix the issue. Thanks in advance for any help you may have to offer.
Spring clearly indicates this problem:
HttpMediaTypeNotAcceptableException
This means that in your content-type header you provided the wrong information or made a syntactical mistake. Try putting there something like application/json.
Also
Make sure the other end will accept it. You currently only accepting requests with an accept header with value application/json. I don't think that is what you want.
So either remove that requirement or add this header to the request.
I'm trying to do a proof of concept that involves a .Net system posting a file to a Rest endpoint on a Java Spring Boot application. I keep getting the "Required Parameter is not present" error. There are a lot of SO questions with this error, and I've attempted the solutions presented in those with no luck. Can anyone see what I'm doing wrong?
Here's my C# code:
private async Task<string> PostFileAsync(string uri, System.IO.FileStream fileStream)
{
using (var client = _httpClientFactory())
{
using (var content = new MultipartFormDataContent())
{
content.Add(new StreamContent(fileStream), "assetFile");
using (var message = await client.PostAsync(uri, content))
{
return await message.Content.ReadAsStringAsync();
}
}
}
}
Here's the request as Fiddler sees it:
POST http://10.0.2.2:8083/asset/1000/1001 HTTP/1.1
Content-Type: multipart/form-data; boundary="bac9aebd-d9ff-40ef-bcf3-4fffdd1b2c00"
Host: 10.0.2.2:8083
Content-Length: 158
Expect: 100-continue
Connection: Keep-Alive
--bac9aebd-d9ff-40ef-bcf3-4fffdd1b2c00
Content-Disposition: form-data; name=assetFile
foo,bar,10
foo2,bar2,12
--bac9aebd-d9ff-40ef-bcf3-4fffdd1b2c00--
Here's my Controller:
#RestController
#RequestMapping("/asset/")
public class AssetController {
#RequestMapping(path="{merchantId}/{assetId}", method=RequestMethod.POST)
public String getAsset(
HttpServletRequest request,
#RequestParam("assetFile") MultipartFile file,
#PathVariable("merchantId") long merchantId,
#PathVariable("assetId") long assetId) throws IOException
{
return "It worked!";
}
}
Here's my config:
#SpringBootApplication(exclude={MultipartAutoConfiguration.class})
public class MySpringApplication {
public static void main(String[] args) {
SpringApplication.run(MySpringApplication.class, args);
}
#Bean(name = "multipartResolver")
public CommonsMultipartResolver multipartResolver() {
System.out.println("multipartResolver()");
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
return multipartResolver;
}
}
And here's the response:
HTTP/1.1 400 Bad Request
Server: Apache-Coyote/1.1
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Fri, 25 Mar 2016 19:34:55 GMT
Connection: close
f3
{"timestamp":1458934495566,"status":400,"error":"Bad Request","exception":"org.springframework.web.bind.MissingServletRequestParameterException","message":"Required MultipartFile parameter 'assetFile' is not present","path":"/asset/1000/1001"}
0
Edited because I posted the wrong C# code
Ok, maybe I hadn't tried ALL of the solutions I saw on SO.
This question had a solution for me.
I had to use #ModelAttribute rather than #RequestParam.
I've got an app that's basically a proxy to a service. The app itself is built on Jersey and served by Jetty. I have this Resource method:
#POST
#Path("/{default: .*}")
#Timed
#Consumes("application/x-www-form-urlencoded")
public MyView post(#Context UriInfo uriInfo, #Context HttpServletRequest request) {
...
}
A user submits a POST form. All POST requests go through this method. The UriInfo and HttpServletRequest are injected appropriately except for one detail: there seem to be no parameters. Here is my request being sent from the terminal:
POST /some/endpoint HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 15
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Host: localhost:8010
User-Agent: HTTPie/0.9.2
foo=bar&biz=baz
Here the POST body clearly contains 2 parameters: foo and biz. But when I try to get them in my code (request.getParameterMap) the result is a map of size 0.
How do I access these parameters or this parameter string from inside my resource method? If it matters, the implementation of HttpServletRequest that is used is org.eclipse.jetty.server.Request.
Three options
#FormParam("<param-name>") to gt individual params. Ex.
#POST
#Consumes("application/x-www-form-urlencoded")
public Response post(#FormParam("foo") String foo
#FormParam("bar") String bar) {}
Use a MultivaluedMap to get all the params
#POST
#Consumes("application/x-www-form-urlencoded")
public Response post(MultivaluedMap<String, String> formParams) {
String foo = formParams.getFirst("foo");
}
Use Form to get all the params.
#POST
#Consumes("application/x-www-form-urlencoded")
public Response post(Form form) {
MultivaluedMap<String, String> formParams = form.asMap();
String foo = formParams.getFirst("foo");
}
Use a #BeanParam along with individual #FormParams to get all the individual params inside a bean.
public class FormBean {
#FormParam("foo")
private String foo;
#FormParam("bar")
private String bar;
// getters and setters
}
#POST
#Consumes("application/x-www-form-urlencoded")
public Response post(#BeanParam FormBean form) {
}