I am writing a restful API and try to use all available http method but have a problem with PUT method.
When I send http request whith put method, I have "400 Bad request" error.
If I use POST method, I have no problem.
Here is my http PUT request :
Remote Address:::1:8080
Request URL:http://localhost:8080/adminRight
Request Method:PUT
Status Code:400 Mauvaise Requête
Request Headersview parsed
PUT /adminRight HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 37
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36
Origin: chrome-extension://hgmloofddffdnphfgcellkdfbfbjeloo
Content-Type: application/json
Accept: */*
Accept-Encoding: gzip,deflate,sdch
Accept-Language: fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4
Cookie: JSESSIONID=41D1CCDF94D3150F0FCA3754E347A4AD
Request Payload
typeList=1&id=2&nom=labelViewerAvance
Response Headersview parsed
HTTP/1.1 400 Mauvaise Requête
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=utf-8
Content-Length: 984
Date: Fri, 30 May 2014 12:55:32 GMT
Connection: close
And here my http POST request :
Remote Address:::1:8080
Request URL:http://localhost:8080/adminRight
Request Method:POST
Status Code:200 OK
Request Headersview parsed
POST /adminRight HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 37
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36
Origin: chrome-extension://hgmloofddffdnphfgcellkdfbfbjeloo
Content-Type: application/json
Accept: */*
Accept-Encoding: gzip,deflate,sdch
Accept-Language: fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4
Cookie: JSESSIONID=41D1CCDF94D3150F0FCA3754E347A4AD
Request Payload
typeList=1&id=2&nom=labelViewerAvance
Response Headersview parsed
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/json;charset=utf-8
Content-Length: 2
Date: Fri, 30 May 2014 13:09:03 GMT
What is the difference between PUT and POST syntax? Or maybe, is it one special configuration in my web.xml?
Thanks in advance for your help.
Edit with new information :
My requests are mapped in java with these two methods :
#RequestMapping(value = "/adminRight",
method = RequestMethod.PUT
)
#ResponseBody
public ResponseEntity<String> updateListRights(#RequestParam(value = "typeList") String typeList,
#RequestParam(value = "id") String idList,
#RequestParam(value = "nom") String nomList)
{
and
#RequestMapping(value = "/adminRight",
method = RequestMethod.POST
)
#ResponseBody
public ResponseEntity<String> addNewListRights(#RequestParam(value = "typeList") String typeList,
#RequestParam(value = "id") String idList,
#RequestParam(value = "nom") String nomList)
{
Your Server: Apache-Coyote/1.1 is just a HTTP connector. Behind that connector there is a web server, for example Apache Tomcat. You have to look up the manual of that server and check how you can allow a HTTP method. By Tomcat there is a server.xml file, in that there is something like this:
// Sample Security Constraint
<security-constraint>
<web-resource-collection>
<web-resource-name><strong>restricted methods</strong></web-resource-name>
<url-pattern>/*</url-pattern>
<http-method>PUT</http-method>
<http-method>POST</http-method>
<http-method>DELETE</http-method>
<http-method>OPTIONS</http-method>
<http-method>TRACE</http-method>
</web-resource-collection>
<auth-constraint />
</security-constraint>
You should add PUT and DELETE to that list. If your REST clients are running in browsers and they are served under a different domain, then you have to enable the OPTIONS method either (for CORS preflight requests), and add CORS allow headers as well. By serving browsers you have to add some HTTP response headers as well and set them properly to prevent XSS attacks.
Another security concern that you should hide the version number of the coyote connector.
Btw. using session cookies like Cookie: JSESSIONID=41D1CCDF94D3150F0FCA3754E347A4AD is not RESTful.
I know very little about java request mapping, but by REST you use POST usually to add a new item resource to a collection resource, for example POST /rights in your case, and PUT usually to edit an entire item resource, for example PUT /rights/{id} where {id} should be a unique resource id (probably the same as one of your aggregate ids). In your code I can't see anything related to this URL structure by the PUT request. You may be interested in PATCH as well.
Related
Running into an issue with CORS and redirect, here is the simplified flow:
Browser ---> K8 NGINX Ingress Controller ---> Service
|
|
Oauth Proxy
From Chrome developer tools: Preflight
General:
Request URL: https://yyyy
Request Method: OPTIONS
Status Code: 204
Remote Address: x.x.x.x:443
Referrer Policy: strict-origin-when-cross-origin
Request headers Preflight
:authority: yyyy
:method: OPTIONS
:path: /api
:scheme: https
accept:*/*
accept-encoding: gzip, deflate, br
accept-language: en-US,en;q=0.9
access-control-request-headers: x-requested-with
access-control-request-method: GET
cache-control: no-cache
origin: https://xxxx
pragma: no-cache
referer: https://xxxx
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: same-site
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36
Response Headers: Preflight
access-control-allow-credentials: true
access-control-allow-headers: DNT,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization
access-control-allow-methods: GET, PUT, POST, DELETE, PATCH, OPTIONS
access-control-allow-origin: https://xxx
access-control-max-age: 1728000
content-length: 0
date: Mon, 06 Feb 2023 13:47:36 GMT strict-transport-security: max-age=15724800; includeSubDomains
Original Request: Headers
:authority: yyyy
:method: GET
:path: /api
:scheme: https
accept: */*
accept-encoding: gzip, deflate, br
accept-language: en-US,en;q=0.9
cache-control: no-cache origin: https://xxxx
pragma: no-cache
referer: https://xxxx
sec-ch-ua: "Not?A_Brand";v="8", "Chromium";v="108", "Google Chrome";v="108"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: same-site
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 x-requested-with: XMLHttpRequest
General
Request URL: https://yyy/a/api/a/a?=abc
Request Method: GET
Status Code: 302
Referrer Policy: strict-origin-when-cross-origin
Response Headers:
content-length: 138
content-type: text/html
date: Mon, 06 Feb 2023 13:47:36 GMT
location: https://yyyy/oauth2/start?rd=%2Fa%2Fapi%2Fs%2Fb%3Fx%3D1
Browser Blocks:
Access to XMLHttpRequest at 'https://yyyy from origin 'https://xxxx has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource
Steps taken so far:
Read many SO questions for a match including this:
Chrome cancels CORS XHR upon HTTP 302 redirect
The way I understand this article: https://github.com/monmohan/cors-tutorial-practical/tree/master/issue
is that if the CORS request redirect with 3XX, then the browser should follow the redirect, but that is not happening
Read Mozilla docs:
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors/CORSMissingAllowOrigin
Tried to follow this three part blog series and its associated github exercise:
https://software-factotum.medium.com/cross-origin-resource-sharing-a-hands-on-tutorial-fb19748cb3b7
The sequence when making a preflighted request to a URL that gets a redirect response is:
The browser makes a preflight OPTIONS request to A
The server must respond with CORS permission
The browser makes the real request to A
The server must respond with CORS permission and the redirect directive to B
The browser makes a preflight OPTIONS request to B
The server must respond with CORS permission
The browser makes the real request to B
The server must response with CORS permission and the data
It isn't entirely clear from your examples, but your sequence appears to be failing at step 4, where you are responding with the redirect directive but without CORS permission.
So I have a REST Service based on Java EE which returns every request with this function in addition to set the CORS Headers:
protected Response.ResponseBuilder addRequiredHeaders(Response.ResponseBuilder rb) {
return rb
.header("Access-Control-Allow-Origin", "http://localhost:8080")
.header("Access-Control-Allow-Credentials", "true")
.header("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE")
.header("Access-Control-Allow-Headers", "Content-Type, *");
}
Now when I'm making a request from the frontend I'm still getting some CORS related issues. Here's the code for the request from the frontend
fetch (apiURL + "/api/rest/users/create", {
body: JSON.stringify(payload),
headers: {
"content-type": "application/json"
},
method: "POST",
mode: "cors",
})
.then((response) => {
...
}.catch((err) => {
...
}
Here is the exact error message:
Failed to load http://localhost:8888/java_ee_project/api/rest/users/create:
Response to preflight request doesn't pass access control check:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://localhost:8080' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Here is some additional information from the network tab in the devtools from the browser
General
Request URL: http://localhost:8888/java_ee_project/api/rest/users/create
Request Method: OPTIONS
Status Code: 200 OK
Remote Address: 127.0.0.1:8888
Referrer Policy: no-referrer-when-downgrade
Response Headers
Allow: POST, OPTIONS
Connection: keep-alive
Content-Length: 13
Content-Type: text/plain;charset=UTF-8
Date: Fri, 04 May 2018 23:10:04 GMT
Server: WildFly/11
X-Powered-By: Undertow/1
Request Headers
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: POST
Connection: keep-alive
DNT: 1
Host: localhost:8888
Origin: http://localhost:8080
Referer: http://localhost:8080/
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1
When I'm executing the same request with Postman everything works fine so I would be very gladful for any help or information about this problem.
Somehow the solution via the added responsebuilder function is not working properly.
Using the second option from http://www.codingpedia.org/ama/how-to-add-cors-support-on-the-server-side-in-java-with-jersey/ with the cors response filter it's working now.
I am getting a weird behavior from Chrome, which I was not recently getting. A week ago, my project was making proper CORS requests to my REST api (Java spring) and all was fine. But yesterday, Chrome (59.0.3071.115) is no longer sending the JSESSIONID as it used to. It now sends a io cookie in its place. That io cookie seems related to WebSocket in some way, which I am not making use of in any of my code.
The thing is, when I try the same code on Edge browser, everything is fine. Edge does send my JSESSIONID cookie and no "io" cookie is sent.
Anyone has experienced this before?
Here is my setup and an example of a request on both browsers.
Setup:
OS: Windows 10
SPA: React, AXIOS (0.16.2)
REST: Java, Spring boot, Tomcat (Embedded)
AXIOS configuration
function getNewAxiosInstance() {
//Init our instance
const ax = axios.create();
//Config defaults
ax.defaults.baseURL = rootUri;
ax.defaults.timeout = 1000;
ax.defaults.withCredentials = true;
return ax;
}
Chrome request
Request URL:http://localhost:8080/api/2.0/client/service_orders/_activeState
Request Method:GET
Status Code:200
Remote Address:[::1]:8080
Referrer Policy:no-referrer-when-downgrade
Response Headers
view source
Access-Control-Allow-Credentials:true
Access-Control-Allow-Origin:http://localhost:3000
Content-Type:application/json;charset=UTF-8
Date:Sun, 16 Jul 2017 13:58:39 GMT
Set-Cookie:JSESSIONID=6DA824C819AB48051DC6A63367010DDA;path=/;HttpOnly
Transfer-Encoding:chunked
Vary:Origin
Request Headers
view source
Accept:application/json, text/plain, */*
Accept-Encoding:gzip, deflate, br
Accept-Language:en-US,en;q=0.8,fr-CA;q=0.6,fr;q=0.4
Cache-Control:no-cache
Connection:keep-alive
Cookie:io=T6umtNN53FTADnx1AAAA
Host:localhost:8080
Origin:http://localhost:3000
Pragma:no-cache
Referer:http://localhost:3000/
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36
Edge request
Request URL: http://localhost:8080/api/2.0/reception/service-orders/_activeState
Request Method: GET
Status Code: 200 /
- Request Headers
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate
Accept-Language: en-CA
Connection: Keep-Alive
Cookie: JSESSIONID=177FD4191C578793F8BC04D9DB7287A5
Host: localhost:8080
Origin: http://localhost:3000
Referer: http://localhost:3000/
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Edge/15.15063
- Response Headers
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: http://localhost:3000
Content-Type: application/json; charset=UTF-8
Date: Sun, 16 Jul 2017 14:08:26 GMT
Transfer-Encoding: chunked
Vary: Origin
Oh well,
There was indeed something fishy with Chrome. I just reinstalled Chrome from scratch and all is good.
Don't know what happened with Chrome, as I did not install any new extensions nor did I changed any settings.
Cheers
Magento's 1.9 REST API needs both Authorization Header and oauth query params, but oauth() only allows for either OAuthSignature.HEADER, or QUERY_STRING
given().auth().oauth(CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TOKEN,
SECRET_TOKEN,OAuthSignature.HEADER)
I tracked the code down to com.jayway.restassured.internal.httpAuthConfig.process(..), but I am not sure what to do from here.
Q: Is there a filter or some method that would allow me to force both?
TL;DR
I started by referring to this: How to use POSTMAN rest client with magento REST api with Oauth. How to get Token and Token Secret?
The last statement
Note, you must check the "Add params to header" checkbox in order for Magento REST calls to work properly.
Using Postman, OAuth 1.0 GET works when I check the box and fails
when I don't, with 403 access denied. This is the same response I get when I use OAuthSignature.QUERY_STRING in rest-assured.
WORKS: Sent from Postman (add params to header)
GET /api/rest/products?oauth_consumer_key=<my-consumer-key>&oauth_token=<my-oauth-token>&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1471929347&oauth_nonce=LJ3o2K&oauth_version=1.0&oauth_signature=0Any8rQ+XjbnWcdXmpHFujg1V7o= HTTP/1.1
Host: dockerized-magento.local
Connection: keep-alive
Authorization: OAuth oauth_consumer_key="<my-consumer-key>",oauth_token="<my-oauth-token>",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1471996573",oauth_nonce="ElK9Fx",oauth_version="1.0",oauth_signature="SvDfMxrWj1O0P2%2FWPOomEVEb93c%3D"
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36
Postman-Token: 9348e805-3c6f-54d7-082f-a1458164725d
Accept: */*
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
rest-assured OAuthSignature.QUERY_STRING
Doesn't Work: OAuthSignature.QUERY_STRING
GET /api/rest/products?oauth_nonce=-316324336&oauth_signature=TlANZu5ogxowYJCpr2V7W448tjw%3D&oauth_token=<my-oauth-token>&oauth_consumer_key=<my-consumer-key>&oauth_timestamp=1471996938&oauth_signature_method=HMAC-SHA1&oauth_version=1.0 HTTP/1.1
Accept: */*
Content-Length: 0
Host: dockerized-magento.local
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.5.1 (Java/1.8.0_77)
Accept-Encoding: gzip,deflate
RESP: {"messages":{"error":[{"code":403,"message":"Access denied"}]}}
Same failed response using Postman with out "add params to header")
Doesn'T WORK: Sent from Postman (NO - add params to header)
GET /api/rest/products?oauth_consumer_key=<my-consumer-key>&oauth_token=<my-oauth-token>&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1471976516&oauth_nonce=OTWTNW&oauth_version=1.0&oauth_signature=Dsh5TEErEC9rMbKakta1v2E7ZTw= HTTP/1.1
Host: dockerized-magento.local
Connection: keep-alive
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36
Postman-Token: f9800e1c-b259-f025-cf48-68e483283869
Accept: */*
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
Response: {"messages":{"error":[{"code":403,"message":"Access denied"}]}}
Mistake made, HEADER option works fine.
The Postman link above, which works fine and was a great help, led me to believe I needed both url params and headers. I went back to postman and deleted the url params after adding params to headers. This worked fine.
I went back and found my consumer keys were wrong.
Tip: Magento Consumer Keys and Secret are not "copyable", use firebug!
How to use #HEAD in jax-rs using Jersey API or any other jax-rs API ? please give me sample.
You don't need to explicitly support HEAD as Jersey will automatically support it. Here is what Jersey's the documentation says:
By default the JAX-RS runtime will automatically support the methods HEAD and OPTIONS, if not explicitly implemented. For HEAD the runtime will invoke the implemented GET method (if present) and ignore the response entity (if set). For OPTIONS the Allow response header will be set to the set of HTTP methods support by the resource. In addition Jersey will return a WADL document describing the resource.
(Source: https://eclipse-ee4j.github.io/jersey.github.io/documentation/latest/user-guide.html#d0e2157)
Here is some dirt simple code illustrating how to send a HEAD request using the Jersey client:
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
WebResource resource = client
.resource("http://localhost:8080/services/echo?message=Hello+World");
ClientResponse response = resource.accept(
MediaType.APPLICATION_JSON).head();
System.out.println(response);
Note the use of the head method. The response object returned contains lots of useful information, like the content type produced, the status code of the request, etc etc. The example can be translated to other client library types, but basically you send exactly the same request as you would with a GET, but with the HEAD method instead. Heres an example of the request that would be sent via a browser tool like 'REST Console':
Request
HEAD /services/echo?message=Hello+World HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 0
Accept: application/json
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.65 Safari/537.31
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Cookie: JSESSIONID=vWu5N2H8Y+P9SuZKWxhpIdgP.undefined
Response:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/json
Content-Length: 0
Date: Fri, 03 May 2013 05:42:20 GMT