Кто виноват, что не справился с запросом - запретил или не выполнил?

задняя часть сервер API AWS
Кто виноват, что не справился с запросом - запретил или не выполнил?

В кодах состояния HTTP есть несколько похожих кодов состояния, которые легко запутать. Одна из менее популярных групп — 405 (Метод не разрешен) и 501 (Не реализован). Если запрос не поддерживается запрошенным методом, считается ли он нереализованным? Какая разница между двумя? Давайте проанализируем его подробно.

Определение кодов состояния

Говоря об определении кода состояния, наиболее авторитетным являетсядокументация rfc-2616в настоящее время:

6.5.5 Метод 405 не разрешен Код состояния 405 (метод не разрешен) указывает на то, что метод, полученный в строке запроса, известен исходному серверу, но не поддерживается целевым ресурсом. Исходный сервер ДОЛЖЕН создать поле заголовка Allow. в ответе 405, содержащем список поддерживаемых в настоящее время методов целевого ресурса.

Код состояния 405 указывает на метод в запросе (напомним, что строка HTTP-запроса состоит из 3 частей: метод запроса, запрошенный ресурс и запрошенный номер версии, например:GET /some/amazing/resource HTTP/1.1) Целевой сервер распознает его, но для запрошенного на этот раз ресурса сервер не может ответить запрошенным методом. В то же время протокол требует, чтобы заголовок ответа содержалAllowЗаголовок, который возвращает метод запроса, поддерживающий соответствующий ресурс.

6.6.2. 501 Not Implemented The 501 (Not Implemented) status code indicates that the server does not support the functionality required to fulfill the request. This is the appropriate response when the server does not recognize the request method and is not capable of supporting it for any resource.

Код состояния 501 указывает на то, что целевой сервер не знает запрашиваемый метод и не может ответить на все запрошенные ресурсы.

сравнивать

Есть тонкие различия в тщательном сравнении.

405

405 означает, что этот метод запроса (GET, PUT, DELETE) известен нашему API-серверу, но для ресурса (URI), который вы запрашиваете, мы не поддерживаем используемый вами метод. ответ должен включать заголовок Allow. , который возвращает поддерживаемые методы HTTP-запроса.

Например, обратитесь к запросам, связанным с жизненным циклом объекта AWS S3. Этот тип ресурса поддерживает только методы GET, PUT и DELETE. Если мы запросим ресурсы жизненного цикла с помощью метода HEAD, будет возвращена следующая ошибка: (обратите внимание на Разрешить заголовок ответа)

curl -vvv -I "https://s3-ap-southeast-1.amazonaws.com/test-voidmain-method/?lifecycle"
* Trying 52.219.40.137...
* TCP_NODELAY set
* Connected to s3-ap-southeast-1.amazonaws.com (52.219.40.137) port 443 (#0)
* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate: *.s3-ap-southeast-1.amazonaws.com
* Server certificate: DigiCert Baltimore CA-2 G2
* Server certificate: Baltimore CyberTrust Root
> HEAD /test-voidmain-method/?lifecycle HTTP/1.1
> Host: s3-ap-southeast-1.amazonaws.com
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 405 Method Not Allowed
HTTP/1.1 405 Method Not Allowed
< x-amz-request-id: 5ECA8F9A66AC236B
x-amz-request-id: 5ECA8F9A66AC236B
< x-amz-id-2: mM6O8oq7yytIjjg+CtB94SN09CZjZE975f1cIhLPzRlFmJEYclANCVxnzgyoAMdd3SBsm2uqBnQ=
x-amz-id-2: mM6O8oq7yytIjjg+CtB94SN09CZjZE975f1cIhLPzRlFmJEYclANCVxnzgyoAMdd3SBsm2uqBnQ=
< Allow: DELETE, GET, PUT
Allow: DELETE, GET, PUT
< Content-Type: application/xml
Content-Type: application/xml
< Transfer-Encoding: chunked
Transfer-Encoding: chunked
< Date: Sat, 30 Sep 2017 03:31:57 GMT
Date: Sat, 30 Sep 2017 03:31:57 GMT
< Server: AmazonS3
Server: AmazonS3
<
* Connection #0 to host s3-ap-southeast-1.amazonaws.com left intact

Важно отметить, что заголовок ответаAllowЗаголовок возвращает методы GET, PUT и DELETE, которые являются всеми методами HTTP, поддерживаемыми этим ресурсом.

501

501 означает, что ваш метод запроса (например, метод PROPFIND) не распознан нашим API-сервером (не может быть сопоставлен), поэтому он называется нереализованным. Возврат 501 в основном ограничен реализацией API-сервера. Версия причала, используемая компанией, поддерживает толькоrfc-2068Метод запроса определен в , поэтому, когда для запроса используется метод PROPFIND, он возвращает 501. Например: (Учитывая такую ​​информацию, как конфиденциальность, конкретное доменное имя, порт и IP-адрес скрыты)

curl -vvv -X PROPFIND "http://example.com"
* Rebuilt URL to: http://example.com/
* Trying xx.xx.xx.xx...
* TCP_NODELAY set
* Connected to example.com (xx.xx.xx.xx) port 80 (#0)
> ASDFASDF / HTTP/1.1
> Host: example.com
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 501 Method PROPFIND is not defined in RFC 2068 and is not supported by the Servlet API
< Server: XXX
< Date: Sat, 30 Sep 2017 03:12:55 GMT
< Content-Length: 0
< Connection: keep-alive
< X-Application-Context: application
<
* Connection #0 to host example.com left intact

Это поведение выполняется причалом:

10:51:05.498 415559652 [6061] [qtp2047899112-1676226 - /] DEBUG o.e.jetty.server.Server REQUEST PROPFIND / on HttpChannelOverHttp@55741056{r=1,c=false,a=DISPATCHED,uri=/}
10:51:05.499 415559653 [6061] [qtp2047899112-1676226 - /] DEBUG o.e.j.s.h.ContextHandler scope null||/ @ o.s.b.c.e.j.JettyEmbeddedWebAppContext@4d56b779{/,null,AVAILABLE}
10:51:05.499 415559653 [6061] [qtp2047899112-1676226 - /] DEBUG o.e.j.s.h.ContextHandler context=||/ @ o.s.b.c.e.j.JettyEmbeddedWebAppContext@4d56b779{/,null,AVAILABLE}
10:51:05.499 415559653 [6061] [qtp2047899112-1676226 - /] DEBUG o.e.jetty.server.session sessionManager=org.eclipse.jetty.server.session.HashSessionManager@6011c7cf
10:51:05.499 415559653 [6061] [qtp2047899112-1676226 - /] DEBUG o.e.jetty.server.session session=null
10:51:05.499 415559653 [6061] [qtp2047899112-1676226 - /] DEBUG o.e.j.s.ServletHandler servlet ||/ -> dispatcherServlet@7ef5559e==org.springframework.web.servlet.DispatcherServlet,-1,true
10:51:05.499 415559653 [6061] [qtp2047899112-1676226 - /] DEBUG o.e.j.s.ServletHandler chain=apiFilter->applicationContextIdFilter->webRequestLoggingFilter->dispatcherServlet@7ef5559e==org.springframework.web.servlet.DispatcherServlet,-1,true
10:51:05.499 415559653 [6061] [qtp2047899112-1676226 - /] DEBUG o.e.j.s.ServletHandler call filter apiFilter
10:51:05.499 415559653 [6061] [qtp2047899112-1676226 - /] DEBUG o.e.j.s.ServletHandler call filter applicationContextIdFilter
10:51:05.499 415559653 [6061] [qtp2047899112-1676226 - /] DEBUG o.e.j.s.ServletHandler call filter webRequestLoggingFilter
10:51:05.499 415559653 [6061] [qtp2047899112-1676226 - /] DEBUG o.e.j.s.ServletHandler call servlet dispatcherServlet@7ef5559e==org.springframework.web.servlet.DispatcherServlet,-1,true
10:51:05.499 415559653 [6061] [qtp2047899112-1676226 - /] DEBUG o.e.j.s.HttpConnection org.eclipse.jetty.server.HttpConnection$SendCallback@4521a8a2[PROCESSING][i=ResponseInfo{HTTP/1.0 501 Method PROPFIND is not defined in RFC 2068 and is not supported by the Servlet API ,-1,false},cb=org.eclipse.jetty.server.HttpChannel$CommitCallback@24c64723] generate: NEED_HEADER (null,[p=0,l=0,c=0,r=0],true)@START
10:51:05.499 415559653 [6061] [qtp2047899112-1676226 - /] DEBUG o.e.j.s.HttpConnection org.eclipse.jetty.server.HttpConnection$SendCallback@4521a8a2[PROCESSING][i=ResponseInfo{HTTP/1.0 501 Method PROPFIND is not defined in RFC 2068 and is not supported by the Servlet API ,-1,false},cb=org.eclipse.jetty.server.HttpChannel$CommitCallback@24c64723] generate: FLUSH ([p=0,l=227,c=8192,r=227],[p=0,l=0,c=0,r=0],true)@COMPLETING

Но AWS так не делает: (AWS возвращает 400)

curl -vvv -X ROPFAS "https://s3-ap-southeast-1.amazonaws.com/test-voidmain-method"
* Trying 52.219.32.101...
* TCP_NODELAY set
* Connected to s3-ap-southeast-1.amazonaws.com (52.219.32.101) port 443 (#0)
* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate: *.s3-ap-southeast-1.amazonaws.com
* Server certificate: DigiCert Baltimore CA-2 G2
* Server certificate: Baltimore CyberTrust Root
> ROPFAS /test-voidmain-method HTTP/1.1
> Host: s3-ap-southeast-1.amazonaws.com
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 400 Bad Request
< Date: Sat, 30 Sep 17 03:20:19 GMT
< Connection: close
< Transfer-Encoding: chunked
< x-amz-id-2: KMMj8crIsT1SPVKbK5sVnuxbqkNSMbzq4XHVAfIA9V6BUuzYIR7AX5XXeInRgJg9tlnaE9wO2ba0SvIIB/VSebPu87jR9Q5o
< x-amz-request-id: 69FAB6DEE37AC9B9
< Content-Type: application/xml
<
* Closing connection 0
<?xml version="1.0" encoding="UTF-8"?><Error><Code>BadRequest</Code><Message>An error occurred when parsing the HTTP request.</Message><RequestId>69FAB6DEE37AC9B9</RequestId><HostId>KMMj8crIsT1SPVKbK5sVnuxbqkNSMbzq4XHVAfIA9V6BUuzYIR7AX5XXeInRgJg9tlnaE9wO2ba0SvIIB/VSebPu87jR9Q5o</HostId></Error>

Еще один момент, который стоит обсудить, — это то, что будет возвращено, если мы получим доступ к API с помощью метода запроса, который Jetty распознает (определено в rfc-2068), но API не поддерживает (requestMapper не зарегистрирован):

curl -vvv -X PATCH "http://example.com/12345/?lifecycle"
* Trying xx.xx.xx.xx...
* TCP_NODELAY set
* Connected to example.com (xx.xx.xx.xx) port 80 (#0)
> PATCH /12345/?lifecycle HTTP/1.1
> Host: example.com
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 405 Request method 'PATCH' not supported
< Server: XXX
< Date: Sat, 30 Sep 2017 03:26:03 GMT
< Content-Length: 0
< Connection: keep-alive
< X-Application-Context: application
< Allow: DELETE, GET, PUT, HEAD, OPTIONS, POST
<
* Connection #0 to host example.com left intact

Но этот возврат не является стандартным, потому что ресурс, который я запросил, является жизненным циклом, а жизненный цикл не поддерживает POST, OPTIONS и другие запросы. Причина в том, что Jetty также возвращает эту ошибку 405. Jetty не может понять тип бизнес-ресурса, поэтому возвращает все типы запросов, которые мы поддерживаем.

Просмотр журналов подтверждает, что Jetty ведет себя следующим образом:

10:49:03.228 415437382 [6061] [qtp2047899112-1676226 - /] DEBUG o.e.jetty.server.Server REQUEST PATCH / on HttpChannelOverHttp@462350f6{r=1,c=false,a=DISPATCHED,uri=/}
10:49:03.228 415437382 [6061] [qtp2047899112-1676226 - /] DEBUG o.e.j.s.h.ContextHandler scope null||/ @ o.s.b.c.e.j.JettyEmbeddedWebAppContext@4d56b779{/,null,AVAILABLE}
10:49:03.228 415437382 [6061] [qtp2047899112-1676226 - /] DEBUG o.e.j.s.h.ContextHandler context=||/ @ o.s.b.c.e.j.JettyEmbeddedWebAppContext@4d56b779{/,null,AVAILABLE}
10:49:03.228 415437382 [6061] [qtp2047899112-1676226 - /] DEBUG o.e.jetty.server.session sessionManager=org.eclipse.jetty.server.session.HashSessionManager@6011c7cf
10:49:03.228 415437382 [6061] [qtp2047899112-1676226 - /] DEBUG o.e.jetty.server.session session=null
10:49:03.228 415437382 [6061] [qtp2047899112-1676226 - /] DEBUG o.e.j.s.ServletHandler servlet ||/ -> dispatcherServlet@7ef5559e==org.springframework.web.servlet.DispatcherServlet,-1,true
10:49:03.228 415437382 [6061] [qtp2047899112-1676226 - /] DEBUG o.e.j.s.ServletHandler chain=apiFilter->applicationContextIdFilter->webRequestLoggingFilter->dispatcherServlet@7ef5559e==org.springframework.web.servlet.DispatcherServlet,-1,true
10:49:03.228 415437382 [6061] [qtp2047899112-1676226 - /] DEBUG o.e.j.s.ServletHandler call filter apiFilter
10:49:03.228 415437382 [6061] [qtp2047899112-1676226 - /] DEBUG o.e.j.s.ServletHandler call filter applicationContextIdFilter
10:49:03.228 415437382 [6061] [qtp2047899112-1676226 - /] DEBUG o.e.j.s.ServletHandler call filter webRequestLoggingFilter
10:49:03.228 415437382 [6061] [qtp2047899112-1676226 - /] DEBUG o.e.j.s.ServletHandler call servlet dispatcherServlet@7ef5559e==org.springframework.web.servlet.DispatcherServlet,-1,true
10:49:03.228 415437382 [6061] [qtp2047899112-1676226 - /] DEBUG o.s.w.s.DispatcherServlet DispatcherServlet with name 'dispatcherServlet' processing PATCH request for [/]
10:49:03.228 415437382 [6061] [qtp2047899112-1676226 - /] DEBUG o.s.b.a.e.m.EndpointHandlerMapping Looking up handler method for path /
10:49:03.228 415437382 [6061] [qtp2047899112-1676226 - /] DEBUG o.s.b.a.e.m.EndpointHandlerMapping Did not find handler method for [/]
10:49:03.228 415437382 [6061] [qtp2047899112-1676226 - /] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping Looking up handler method for path /ExceptionHandlerExceptionResolver Resolving exception from handler [null]: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'PATCH' not supported
10:49:03.229 415437383 [6061] [qtp2047899112-1676226 - /] DEBUG o.s.w.s.m.a.ResponseStatusExceptionResolver Resolving exception from handler [null]: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'PATCH' not supported

Объединив приведенные выше эксперименты, мы видим, что метод различения 405 и 501 пристани таков:rfc-2068Если запрошенный URL-адрес не может совпасть, он вернет 405 и вернет все методы запроса, определенные в API; если запрошенный метод не находится вrfc-2068В определении он возвращает 501 напрямую.

Как использовать 405 и 501?

Итак, для разработчиков приложений, как использовать оба?

После анализа наиболее разумным методом обработки является то, что если метод запроса может быть сопоставлен с запрошенным ресурсом и может быть обработан нормально, то следует обычной логике; если нет успешного совпадения, сначала проанализируйте ресурс, требуемый запросом, и найдите запрос, который поддерживает этот ресурс в соответствии с методами ресурса, и объедините эти методы вAllowЗаголовок возвращается клиенту; если не удается найти метод для его обработки на основе ресурса, возвращается 501.

Кроме того, следует отметить, что, поскольку код состояния HTTP 5xx обычно считается ошибкой на стороне сервера, может быть система мониторинга компании, отслеживающая все запросы 5xx, поэтому 501 может повлиять на сигнал тревоги мониторинга, который требует внимания. Если реализация разумна, код состояния 501 можно игнорировать при мониторинге.